Merge commit '9c99831edeabd91eecfc4ee68aa0f8e51e54b1ac' into dev-release
diff --git a/build.gradle b/build.gradle
index 2edec91..fba205d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -90,6 +90,12 @@
         }
         output.resourcesDir = 'build/classes/cfSegments'
     }
+    libraryDesugarConversions {
+        java {
+            srcDirs = ['src/library_desugar/java']
+        }
+        output.resourcesDir = 'build/classes/library_desugar_conversions'
+    }
     debugTestResources {
         java {
             srcDirs = ['src/test/debugTestResources']
@@ -566,6 +572,7 @@
 setJava11Compilation(sourceSets.jdk11TimeTests.compileJavaTaskName)
 
 task compileMainWithJava11 (type: JavaCompile) {
+    dependsOn downloadDeps
     def jdkDir = 'third_party/openjdk/jdk-11/'
     options.fork = true
     options.forkOptions.jvmArgs = []
@@ -851,7 +858,14 @@
     }
 }
 
-task testJar(type: ShadowJar, dependsOn: testClasses) {
+task buildLibraryDesugarConversions(type: Zip, dependsOn: downloadDeps) {
+    from sourceSets.libraryDesugarConversions.output
+    include "java/**/*.class"
+    baseName 'library_desugar_conversions'
+    destinationDir file('build/libs')
+}
+
+task testJar(type: ShadowJar, dependsOn: [testClasses, buildLibraryDesugarConversions]) {
     baseName = "r8tests"
     from sourceSets.test.output
     // We only want to include tests that use R8 when generating keep rules for applymapping.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
similarity index 98%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs.json
rename to src/library_desugar/desugar_jdk_libs.json
index a281c3d..3cfd647 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -1,7 +1,8 @@
 {
-  "configuration_format_version": 2,
-  "version": "0.8.0",
+  "configuration_format_version": 3,
+  "version": "0.9.0",
   "required_compilation_api_level": 26,
+  "synthesized_library_classes_package_prefix": "j$.",
   "library_flags": [
     {
       "api_level_below_or_equal": 25,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs_comments.md b/src/library_desugar/desugar_jdk_libs_comments.md
similarity index 100%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs_comments.md
rename to src/library_desugar/desugar_jdk_libs_comments.md
diff --git a/src/test/desugaredLibrary/stubs/timestubs/Duration.java b/src/library_desugar/java/j$/time/Duration.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/Duration.java
rename to src/library_desugar/java/j$/time/Duration.java
diff --git a/src/test/desugaredLibrary/stubs/timestubs/Instant.java b/src/library_desugar/java/j$/time/Instant.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/Instant.java
rename to src/library_desugar/java/j$/time/Instant.java
diff --git a/src/test/desugaredLibrary/stubs/timestubs/LocalDate.java b/src/library_desugar/java/j$/time/LocalDate.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/LocalDate.java
rename to src/library_desugar/java/j$/time/LocalDate.java
diff --git a/src/test/desugaredLibrary/stubs/timestubs/MonthDay.java b/src/library_desugar/java/j$/time/MonthDay.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/MonthDay.java
rename to src/library_desugar/java/j$/time/MonthDay.java
diff --git a/src/test/desugaredLibrary/stubs/timestubs/ZoneId.java b/src/library_desugar/java/j$/time/ZoneId.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/ZoneId.java
rename to src/library_desugar/java/j$/time/ZoneId.java
diff --git a/src/test/desugaredLibrary/stubs/timestubs/ZonedDateTime.java b/src/library_desugar/java/j$/time/ZonedDateTime.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/ZonedDateTime.java
rename to src/library_desugar/java/j$/time/ZonedDateTime.java
diff --git a/src/test/desugaredLibrary/stubs/optionalstubs/Optional.java b/src/library_desugar/java/j$/util/optionalstubs/Optional.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/optionalstubs/Optional.java
rename to src/library_desugar/java/j$/util/optionalstubs/Optional.java
diff --git a/src/test/desugaredLibrary/stubs/optionalstubs/OptionalDouble.java b/src/library_desugar/java/j$/util/optionalstubs/OptionalDouble.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/optionalstubs/OptionalDouble.java
rename to src/library_desugar/java/j$/util/optionalstubs/OptionalDouble.java
diff --git a/src/test/desugaredLibrary/stubs/optionalstubs/OptionalInt.java b/src/library_desugar/java/j$/util/optionalstubs/OptionalInt.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/optionalstubs/OptionalInt.java
rename to src/library_desugar/java/j$/util/optionalstubs/OptionalInt.java
diff --git a/src/test/desugaredLibrary/stubs/optionalstubs/OptionalLong.java b/src/library_desugar/java/j$/util/optionalstubs/OptionalLong.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/optionalstubs/OptionalLong.java
rename to src/library_desugar/java/j$/util/optionalstubs/OptionalLong.java
diff --git a/src/test/desugaredLibrary/stubs/summarystatisticsstubs/DoubleSummaryStatistics.java b/src/library_desugar/java/j$/util/summarystatisticsstubs/DoubleSummaryStatistics.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/summarystatisticsstubs/DoubleSummaryStatistics.java
rename to src/library_desugar/java/j$/util/summarystatisticsstubs/DoubleSummaryStatistics.java
diff --git a/src/test/desugaredLibrary/stubs/summarystatisticsstubs/IntSummaryStatistics.java b/src/library_desugar/java/j$/util/summarystatisticsstubs/IntSummaryStatistics.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/summarystatisticsstubs/IntSummaryStatistics.java
rename to src/library_desugar/java/j$/util/summarystatisticsstubs/IntSummaryStatistics.java
diff --git a/src/test/desugaredLibrary/stubs/summarystatisticsstubs/LongSummaryStatistics.java b/src/library_desugar/java/j$/util/summarystatisticsstubs/LongSummaryStatistics.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/summarystatisticsstubs/LongSummaryStatistics.java
rename to src/library_desugar/java/j$/util/summarystatisticsstubs/LongSummaryStatistics.java
diff --git a/src/test/desugaredLibrary/conversions/TimeConversions.java b/src/library_desugar/java/java/time/TimeConversions.java
similarity index 100%
rename from src/test/desugaredLibrary/conversions/TimeConversions.java
rename to src/library_desugar/java/java/time/TimeConversions.java
diff --git a/src/test/desugaredLibrary/conversions/DoubleSummaryStatisticsConversions.java b/src/library_desugar/java/java/util/DoubleSummaryStatisticsConversions.java
similarity index 100%
rename from src/test/desugaredLibrary/conversions/DoubleSummaryStatisticsConversions.java
rename to src/library_desugar/java/java/util/DoubleSummaryStatisticsConversions.java
diff --git a/src/test/desugaredLibrary/conversions/IntSummaryStatisticsConversions.java b/src/library_desugar/java/java/util/IntSummaryStatisticsConversions.java
similarity index 100%
rename from src/test/desugaredLibrary/conversions/IntSummaryStatisticsConversions.java
rename to src/library_desugar/java/java/util/IntSummaryStatisticsConversions.java
diff --git a/src/test/desugaredLibrary/conversions/LongSummaryStatisticsConversions.java b/src/library_desugar/java/java/util/LongSummaryStatisticsConversions.java
similarity index 100%
rename from src/test/desugaredLibrary/conversions/LongSummaryStatisticsConversions.java
rename to src/library_desugar/java/java/util/LongSummaryStatisticsConversions.java
diff --git a/src/test/desugaredLibrary/conversions/OptionalConversions.java b/src/library_desugar/java/java/util/OptionalConversions.java
similarity index 100%
rename from src/test/desugaredLibrary/conversions/OptionalConversions.java
rename to src/library_desugar/java/java/util/OptionalConversions.java
diff --git a/src/main/java/com/android/tools/r8/CompatDexHelper.java b/src/main/java/com/android/tools/r8/CompatDexHelper.java
deleted file mode 100644
index fa52e25..0000000
--- a/src/main/java/com/android/tools/r8/CompatDexHelper.java
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2017, 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;
-
-public class CompatDexHelper {
-  public static void ignoreDexInArchive(BaseCommand.Builder builder) {
-    builder.setIgnoreDexInArchive(true);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/CompatDxHelper.java b/src/main/java/com/android/tools/r8/CompatDxHelper.java
new file mode 100644
index 0000000..a84bab5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/CompatDxHelper.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2017, 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.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class CompatDxHelper {
+  public static void run(D8Command command, Boolean minimalMainDex)
+      throws CompilationFailedException {
+    AndroidApp app = command.getInputApp();
+    InternalOptions options = command.getInternalOptions();
+    // DX does not desugar.
+    options.enableDesugaring = false;
+    // DX allows --multi-dex without specifying a main dex list for legacy devices.
+    // That is broken, but for CompatDX we do the same to not break existing builds
+    // that are trying to transition.
+    options.enableMainDexListCheck = false;
+    // DX has a minimal main dex flag. In compat mode only do minimal main dex
+    // if the flag is actually set.
+    options.minimalMainDex = minimalMainDex;
+    D8.runForTesting(app, options);
+  }
+
+  public static void ignoreDexInArchive(BaseCommand.Builder builder) {
+    builder.setIgnoreDexInArchive(true);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/D8Logger.java b/src/main/java/com/android/tools/r8/D8Logger.java
index a3015dc..6c3fca0 100644
--- a/src/main/java/com/android/tools/r8/D8Logger.java
+++ b/src/main/java/com/android/tools/r8/D8Logger.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.compatdx.CompatDx;
 import com.google.common.collect.ImmutableList;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -17,7 +18,8 @@
       "Usage: java -jar d8logger.jar <compiler-options>",
       " where <compiler-options> will be",
       "",
-      " 1. forwarded to the 'd8' tool, and also",
+      " 1. forwarded to the 'd8' or 'compatdx' tool (depending on the presence of the '--dex'",
+      "    option), and also",
       " 2. appended to the file in the environment variable 'D8LOGGER_OUTPUT'",
       "",
       " The options will be appended as a new line with TAB characters between the arguments."));
@@ -41,6 +43,10 @@
       }
     }
 
-    D8.main(args);
+    if (Arrays.stream(args).anyMatch(s -> s.equals("--dex"))) {
+      CompatDx.main(args);
+    } else {
+      D8.main(args);
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 28713e3..50ea501 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -89,10 +89,7 @@
             desugar(app, options, executorService);
           });
       if (shrink) {
-        InternalOptions r8Options = r8Command.getInternalOptions();
-        // Disable outlining for R8 when called from L8.
-        r8Options.outline.enabled = false;
-        R8.runForTesting(r8Command.getInputApp(), r8Options);
+        R8.run(r8Command);
       } else {
         D8.run(d8Command, executorService);
       }
@@ -107,6 +104,9 @@
     try {
       // Disable global optimizations.
       options.disableGlobalOptimizations();
+      // Since L8 Cf representation is temporary, just disable long running back-end optimizations
+      // on it.
+      options.enableLoadStoreOptimization = false;
 
       DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
       PrefixRewritingMapper rewritePrefix =
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 0d8f3ba..b906cb2 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -31,6 +32,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
@@ -70,6 +72,7 @@
   private Map<DexType, Set<DexMethod>> methods = Maps.newIdentityHashMap();
   private Map<DexType, Set<DexField>> fields = Maps.newIdentityHashMap();
   private Set<DexType> noObfuscationTypes = Sets.newIdentityHashSet();
+  private Set<String> keepPackageNames = Sets.newHashSet();
   private final DexApplication application;
   private final AppInfoWithSubtyping appInfo;
   private int errors;
@@ -165,11 +168,12 @@
     private void addType(DexType type) {
       if (isTargetType(type) && types.add(type)) {
         DexClass clazz = appInfo.definitionFor(type);
-        if (clazz == null
-            || clazz.accessFlags.isVisibilityDependingOnPackage()
-            || !allowObfuscation) {
+        if (clazz == null || !allowObfuscation) {
           noObfuscationTypes.add(type);
         }
+        if (clazz != null && clazz.accessFlags.isVisibilityDependingOnPackage()) {
+          keepPackageNames.add(clazz.type.getPackageName());
+        }
         methods.put(type, Sets.newIdentityHashSet());
         fields.put(type, Sets.newIdentityHashSet());
       }
@@ -192,9 +196,12 @@
       Set<DexField> typeFields = fields.get(field.holder);
       if (typeFields != null) {
         assert baseField != null;
-        if (!allowObfuscation || baseField.accessFlags.isVisibilityDependingOnPackage()) {
+        if (!allowObfuscation) {
           noObfuscationTypes.add(field.holder);
         }
+        if (baseField.accessFlags.isVisibilityDependingOnPackage()) {
+          keepPackageNames.add(baseField.field.holder.getPackageName());
+        }
         typeFields.add(field);
       }
     }
@@ -208,10 +215,13 @@
       Set<DexMethod> typeMethods = methods.get(method.holder);
       if (typeMethods != null) {
         DexEncodedMethod encodedMethod = appInfo.definitionFor(method);
-        assert encodedMethod != null;
-        if (!allowObfuscation || encodedMethod.accessFlags.isVisibilityDependingOnPackage()) {
+        assert encodedMethod != null : "Could not find method " + method.toString();
+        if (!allowObfuscation) {
           noObfuscationTypes.add(method.holder);
         }
+        if (encodedMethod.accessFlags.isVisibilityDependingOnPackage()) {
+          keepPackageNames.add(encodedMethod.method.holder.getPackageName());
+        }
         typeMethods.add(method);
       }
     }
@@ -344,7 +354,8 @@
   }
 
   private void print() {
-    errors = printer.print(application, types, noObfuscationTypes, methods, fields);
+    errors =
+        printer.print(application, types, noObfuscationTypes, keepPackageNames, methods, fields);
   }
 
   private abstract static class Printer {
@@ -378,6 +389,8 @@
 
     abstract void printMethod(DexEncodedMethod encodedMethod, String typeName);
 
+    abstract void printPackageNames(List<String> packageNames);
+
     void printNameAndReturn(DexEncodedMethod encodedMethod) {
       if (encodedMethod.accessFlags.isConstructor()) {
         printConstructorName(encodedMethod);
@@ -397,6 +410,7 @@
         DexApplication application,
         Set<DexType> types,
         Set<DexType> noObfuscationTypes,
+        Set<String> keepPackageNames,
         Map<DexType, Set<DexMethod>> methods,
         Map<DexType, Set<DexField>> fields) {
       int errors = 0;
@@ -431,6 +445,9 @@
         }
         printTypeFooter();
       }
+      ArrayList<String> packageNamesToKeep = new ArrayList<>(keepPackageNames);
+      Collections.sort(packageNamesToKeep);
+      printPackageNames(packageNamesToKeep);
       return errors;
     }
   }
@@ -457,6 +474,11 @@
     }
 
     @Override
+    void printPackageNames(List<String> packageNames) {
+      // No need to print package names for text output.
+    }
+
+    @Override
     void printTypeHeader(DexClass dexClass, boolean allowObfuscation) {
       appendLine(dexClass.type.toSourceString());
     }
@@ -520,6 +542,11 @@
     }
 
     @Override
+    void printPackageNames(List<String> packageNames) {
+      append("-keeppackagenames " + StringUtils.join(packageNames, ",") + "\n");
+    }
+
+    @Override
     public void printTypeFooter() {
       appendLine("}");
     }
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
index e352ede..f54233b 100644
--- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java
+++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.bisect.Bisect;
+import com.android.tools.r8.compatdx.CompatDx;
 import com.android.tools.r8.compatproguard.CompatProguard;
 import com.android.tools.r8.dexfilemerger.DexFileMerger;
 import com.android.tools.r8.dexsplitter.DexSplitter;
@@ -30,6 +31,9 @@
       case "bisect":
         Bisect.main(shift(args));
         break;
+      case "compatdx":
+        CompatDx.main(shift(args));
+        break;
       case "compatproguard":
         CompatProguard.main(shift(args));
         break;
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
index f7e6dc3..f4f3c7e 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.compatdexbuilder;
 
 import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.CompatDexHelper;
+import com.android.tools.r8.CompatDxHelper;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -162,7 +162,7 @@
       throws IOException, CompilationFailedException {
     DexConsumer consumer = new DexConsumer();
     D8Command.Builder builder = D8Command.builder();
-    CompatDexHelper.ignoreDexInArchive(builder);
+    CompatDxHelper.ignoreDexInArchive(builder);
     builder
         .setProgramConsumer(consumer)
         .setMode(noLocals ? CompilationMode.RELEASE : CompilationMode.DEBUG)
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
new file mode 100644
index 0000000..a52c2ea
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -0,0 +1,618 @@
+// Copyright (c) 2017, 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.compatdx;
+
+import com.android.tools.r8.ByteDataView;
+import com.android.tools.r8.CompatDxHelper;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.Version;
+import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.DxUsageMessage;
+import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.PositionInfo;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+/**
+ * Dx compatibility interface for d8.
+ *
+ * This should become a mostly drop-in replacement for uses of the DX dexer (eg, dx --dex ...).
+ */
+public class CompatDx {
+
+  private static final String USAGE_HEADER = "Usage: compatdx [options] <input files>";
+
+  /**
+   * Compatibility options parsing for the DX --dex sub-command.
+   */
+  public static class DxCompatOptions {
+    // Final values after parsing.
+    // Note: These are ordered by their occurrence in "dx --help"
+    public final boolean help;
+    public final boolean version;
+    public final boolean debug;
+    public final boolean verbose;
+    public final PositionInfo positions;
+    public final boolean noLocals;
+    public final boolean noOptimize;
+    public final boolean statistics;
+    public final String optimizeList;
+    public final String noOptimizeList;
+    public final boolean noStrict;
+    public final boolean keepClasses;
+    public final String output;
+    public final String dumpTo;
+    public final int dumpWidth;
+    public final String dumpMethod;
+    public final boolean verboseDump;
+    public final boolean dump;
+    public final boolean noFiles;
+    public final boolean coreLibrary;
+    public final int numThreads;
+    public final boolean incremental;
+    public final boolean forceJumbo;
+    public final boolean noWarning;
+    public final boolean multiDex;
+    public final String mainDexList;
+    public final boolean minimalMainDex;
+    public final int minApiLevel;
+    public final String inputList;
+    public final ImmutableList<String> inputs;
+    // Undocumented option
+    public final int maxIndexNumber;
+
+    private static final String FILE_ARG = "file";
+    private static final String NUM_ARG = "number";
+    private static final String METHOD_ARG = "method";
+
+    public enum PositionInfo {
+      NONE, IMPORTANT, LINES, THROWING
+    }
+
+    // Exception thrown on invalid dx compat usage.
+    public static class DxUsageMessage extends Exception {
+      public final String message;
+
+      DxUsageMessage(String message) {
+        this.message = message;
+      }
+
+      void printHelpOn(PrintStream sink) throws IOException {
+        sink.println(message);
+      }
+    }
+
+    // Parsing specification.
+    private static class Spec {
+      final OptionParser parser;
+
+      // Note: These are ordered by their occurrence in "dx --help"
+      final OptionSpec<Void> debug;
+      final OptionSpec<Void> verbose;
+      final OptionSpec<String> positions;
+      final OptionSpec<Void> noLocals;
+      final OptionSpec<Void> noOptimize;
+      final OptionSpec<Void> statistics;
+      final OptionSpec<String> optimizeList;
+      final OptionSpec<String> noOptimizeList;
+      final OptionSpec<Void> noStrict;
+      final OptionSpec<Void> keepClasses;
+      final OptionSpec<String> output;
+      final OptionSpec<String> dumpTo;
+      final OptionSpec<Integer> dumpWidth;
+      final OptionSpec<String> dumpMethod;
+      final OptionSpec<Void> dump;
+      final OptionSpec<Void> verboseDump;
+      final OptionSpec<Void> noFiles;
+      final OptionSpec<Void> coreLibrary;
+      final OptionSpec<Integer> numThreads;
+      final OptionSpec<Void> incremental;
+      final OptionSpec<Void> forceJumbo;
+      final OptionSpec<Void> noWarning;
+      final OptionSpec<Void> multiDex;
+      final OptionSpec<String> mainDexList;
+      final OptionSpec<Void> minimalMainDex;
+      final OptionSpec<Integer> minApiLevel;
+      final OptionSpec<String> inputList;
+      final OptionSpec<String> inputs;
+      final OptionSpec<Void> version;
+      final OptionSpec<Void> help;
+      final OptionSpec<Integer> maxIndexNumber;
+
+      Spec() {
+        parser = new OptionParser();
+        parser.accepts("dex");
+        debug = parser.accepts("debug", "Print debug information");
+        verbose = parser.accepts("verbose", "Print verbose information");
+        positions = parser
+            .accepts("positions",
+                "What source-position information to keep. One of: none, lines, important")
+            .withOptionalArg()
+            .describedAs("keep")
+            .defaultsTo("lines");
+        noLocals = parser.accepts("no-locals", "Don't keep local variable information");
+        statistics = parser.accepts("statistics", "Print statistics information");
+        noOptimize = parser.accepts("no-optimize", "Don't optimize");
+        optimizeList = parser
+            .accepts("optimize-list", "File listing methods to optimize")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        noOptimizeList = parser
+            .accepts("no-optimize-list", "File listing methods not to optimize")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        noStrict = parser.accepts("no-strict", "Disable strict file/class name checks");
+        keepClasses = parser.accepts("keep-classes", "Keep input class files in in output jar");
+        output = parser
+            .accepts("output", "Output file or directory")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        dumpTo = parser
+            .accepts("dump-to", "File to dump information to")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        dumpWidth = parser
+            .accepts("dump-width", "Max width for columns in dump output")
+            .withRequiredArg()
+            .ofType(Integer.class)
+            .defaultsTo(0)
+            .describedAs(NUM_ARG);
+        dumpMethod = parser
+            .accepts("dump-method", "Method to dump information for")
+            .withRequiredArg()
+            .describedAs(METHOD_ARG);
+        dump = parser.accepts("dump", "Dump information");
+        verboseDump = parser.accepts("verbose-dump", "Dump verbose information");
+        noFiles = parser.accepts("no-files", "Don't fail if given no files");
+        coreLibrary = parser.accepts("core-library", "Construct a core library");
+        numThreads = parser
+            .accepts("num-threads", "Number of threads to run with")
+            .withRequiredArg()
+            .ofType(Integer.class)
+            .defaultsTo(1)
+            .describedAs(NUM_ARG);
+        incremental = parser.accepts("incremental", "Merge result with the output if it exists");
+        forceJumbo = parser.accepts("force-jumbo", "Force use of string-jumbo instructions");
+        noWarning = parser.accepts("no-warning", "Suppress warnings");
+        maxIndexNumber = parser.accepts("set-max-idx-number",
+            "Undocumented: Set maximal index number to use in a dex file.")
+            .withRequiredArg()
+            .ofType(Integer.class)
+            .defaultsTo(0)
+            .describedAs("Maximum index");
+        minimalMainDex = parser.accepts("minimal-main-dex", "Produce smallest possible main dex");
+        mainDexList = parser
+            .accepts("main-dex-list", "File listing classes that must be in the main dex file")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        multiDex =
+            parser
+                .accepts("multi-dex", "Allow generation of multi-dex")
+                .requiredIf(minimalMainDex, mainDexList, maxIndexNumber);
+        minApiLevel = parser
+            .accepts("min-sdk-version", "Minimum Android API level compatibility.")
+            .withRequiredArg().ofType(Integer.class);
+        inputList = parser
+            .accepts("input-list", "File listing input files")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        inputs = parser.nonOptions("Input files");
+        version = parser.accepts("version", "Print the version of this tool").forHelp();
+        help = parser.accepts("help", "Print this message").forHelp();
+      }
+    }
+
+    private DxCompatOptions(OptionSet options, Spec spec) {
+      help = options.has(spec.help);
+      version = options.has(spec.version);
+      debug = options.has(spec.debug);
+      verbose = options.has(spec.verbose);
+      if (options.has(spec.positions)) {
+        switch (options.valueOf(spec.positions)) {
+          case "none":
+            positions = PositionInfo.NONE;
+            break;
+          case "important":
+            positions = PositionInfo.IMPORTANT;
+            break;
+          case "lines":
+            positions = PositionInfo.LINES;
+            break;
+          case "throwing":
+            positions = PositionInfo.THROWING;
+            break;
+          default:
+            positions = PositionInfo.IMPORTANT;
+            break;
+        }
+      } else {
+        positions = PositionInfo.LINES;
+      }
+      noLocals = options.has(spec.noLocals);
+      noOptimize = options.has(spec.noOptimize);
+      statistics = options.has(spec.statistics);
+      optimizeList = options.valueOf(spec.optimizeList);
+      noOptimizeList = options.valueOf(spec.noOptimizeList);
+      noStrict = options.has(spec.noStrict);
+      keepClasses = options.has(spec.keepClasses);
+      output = options.valueOf(spec.output);
+      dumpTo = options.valueOf(spec.dumpTo);
+      dumpWidth = options.valueOf(spec.dumpWidth);
+      dumpMethod = options.valueOf(spec.dumpMethod);
+      dump = options.has(spec.dump);
+      verboseDump = options.has(spec.verboseDump);
+      noFiles = options.has(spec.noFiles);
+      coreLibrary = options.has(spec.coreLibrary);
+      numThreads = lastIntOf(options.valuesOf(spec.numThreads));
+      incremental = options.has(spec.incremental);
+      forceJumbo = options.has(spec.forceJumbo);
+      noWarning = options.has(spec.noWarning);
+      multiDex = options.has(spec.multiDex);
+      mainDexList = options.valueOf(spec.mainDexList);
+      minimalMainDex = options.has(spec.minimalMainDex);
+      if (options.has(spec.minApiLevel)) {
+        List<Integer> allMinApiLevels = options.valuesOf(spec.minApiLevel);
+        minApiLevel = allMinApiLevels.get(allMinApiLevels.size() - 1);
+      } else {
+        minApiLevel = AndroidApiLevel.getDefault().getLevel();
+      }
+      inputList = options.valueOf(spec.inputList);
+      inputs = ImmutableList.copyOf(options.valuesOf(spec.inputs));
+      maxIndexNumber = options.valueOf(spec.maxIndexNumber);
+    }
+
+    public static DxCompatOptions parse(String[] args) {
+      Spec spec = new Spec();
+      return new DxCompatOptions(spec.parser.parse(args), spec);
+    }
+
+    private static int lastIntOf(List<Integer> values) {
+      assert !values.isEmpty();
+      return values.get(values.size() - 1);
+    }
+  }
+
+  public static void main(String[] args) throws IOException {
+    try {
+      run(args);
+    } catch (DxUsageMessage e) {
+      System.err.println(USAGE_HEADER);
+      e.printHelpOn(System.err);
+      System.exit(1);
+    } catch (CompilationFailedException e) {
+      System.exit(1);
+    }
+  }
+
+  private static void run(String[] args)
+      throws DxUsageMessage, IOException, CompilationFailedException {
+    DxCompatOptions dexArgs = DxCompatOptions.parse(args);
+    if (dexArgs.help) {
+      printHelpOn(System.out);
+      return;
+    }
+    if (dexArgs.version) {
+      System.out.println("CompatDx " + Version.getVersionString());
+      return;
+    }
+    CompilationMode mode = CompilationMode.RELEASE;
+    Path output = null;
+    List<Path> inputs = new ArrayList<>();
+    boolean singleDexFile = !dexArgs.multiDex;
+    Path mainDexList = null;
+    int numberOfThreads = 1;
+
+    for (String path : dexArgs.inputs) {
+      processPath(new File(path), inputs);
+    }
+    if (inputs.isEmpty()) {
+      if (dexArgs.noFiles) {
+        return;
+      }
+      throw new DxUsageMessage("No input files specified");
+    }
+
+    if (!Log.ENABLED && dexArgs.debug) {
+      System.out.println("Warning: logging is not enabled for this build.");
+    }
+
+    if (dexArgs.dump && dexArgs.verbose) {
+      System.out.println("Warning: dump is not supported");
+    }
+
+    if (dexArgs.verboseDump) {
+      throw new Unimplemented("verbose dump file not yet supported");
+    }
+
+    if (dexArgs.dumpMethod != null) {
+      throw new Unimplemented("method-dump not yet supported");
+    }
+
+    if (dexArgs.output != null) {
+      output = Paths.get(dexArgs.output);
+      if (FileUtils.isDexFile(output)) {
+        if (!singleDexFile) {
+          throw new DxUsageMessage("Cannot output to a single dex-file when running with multidex");
+        }
+      } else if (!FileUtils.isArchive(output)
+          && (!output.toFile().exists() || !output.toFile().isDirectory())) {
+        throw new DxUsageMessage("Unsupported output file or output directory does not exist. "
+            + "Output must be a directory or a file of type dex, apk, jar or zip.");
+      }
+    }
+
+    if (dexArgs.dumpTo != null && dexArgs.verbose) {
+      System.out.println("dump-to file not yet supported");
+    }
+
+    if (dexArgs.positions == PositionInfo.NONE && dexArgs.verbose) {
+      System.out.println("Warning: no support for positions none.");
+    }
+
+    if (dexArgs.positions == PositionInfo.LINES && !dexArgs.noLocals) {
+      mode = CompilationMode.DEBUG;
+    }
+
+    if (dexArgs.incremental) {
+      throw new Unimplemented("incremental merge not supported yet");
+    }
+
+    if (dexArgs.forceJumbo && dexArgs.verbose) {
+      System.out.println(
+          "Warning: no support for forcing jumbo-strings.\n"
+              + "Strings will only use jumbo-string indexing if necessary.\n"
+              + "Make sure that any dex merger subsequently used "
+              + "supports correct handling of jumbo-strings (eg, D8/R8 does).");
+    }
+
+    if (dexArgs.noOptimize && dexArgs.verbose) {
+      System.out.println("Warning: no support for not optimizing");
+    }
+
+    if (dexArgs.optimizeList != null) {
+      throw new Unimplemented("no support for optimize-method list");
+    }
+
+    if (dexArgs.noOptimizeList != null) {
+      throw new Unimplemented("no support for dont-optimize-method list");
+    }
+
+    if (dexArgs.statistics && dexArgs.verbose) {
+      System.out.println("Warning: no support for printing statistics");
+    }
+
+    if (dexArgs.numThreads > 1) {
+      numberOfThreads = dexArgs.numThreads;
+    }
+
+    if (dexArgs.mainDexList != null) {
+      mainDexList = Paths.get(dexArgs.mainDexList);
+    }
+
+    if (dexArgs.noStrict) {
+      if (dexArgs.verbose) {
+        System.out.println("Warning: conservative main-dex list not yet supported");
+      }
+    } else {
+      if (dexArgs.verbose) {
+        System.out.println("Warning: strict name checking not yet supported");
+      }
+    }
+
+    if (dexArgs.minimalMainDex && dexArgs.verbose) {
+      System.out.println("Warning: minimal main-dex support is not yet supported");
+    }
+
+    if (dexArgs.maxIndexNumber != 0 && dexArgs.verbose) {
+      System.out.println("Warning: internal maximum-index setting is not supported");
+    }
+
+    if (numberOfThreads < 1) {
+      throw new DxUsageMessage("Invalid numThreads value of " + numberOfThreads);
+    }
+    ExecutorService executor = ThreadUtils.getExecutorService(numberOfThreads);
+
+    try {
+      D8Command.Builder builder = D8Command.builder();
+      CompatDxHelper.ignoreDexInArchive(builder);
+      builder
+          .addProgramFiles(inputs)
+          .setProgramConsumer(
+              createConsumer(inputs, output, singleDexFile, dexArgs.keepClasses))
+          .setMode(mode)
+          .setMinApiLevel(dexArgs.minApiLevel);
+      if (mainDexList != null) {
+        builder.addMainDexListFiles(mainDexList);
+      }
+      CompatDxHelper.run(builder.build(), dexArgs.minimalMainDex);
+    } finally {
+      executor.shutdown();
+    }
+  }
+
+  private static ProgramConsumer createConsumer(
+      List<Path> inputs, Path output, boolean singleDexFile, boolean keepClasses)
+      throws DxUsageMessage {
+    if (output == null) {
+      return DexIndexedConsumer.emptyConsumer();
+    }
+    if (singleDexFile) {
+      return new SingleDexFileConsumer(
+          FileUtils.isDexFile(output)
+              ? new NamedDexFileConsumer(output)
+              : createDexConsumer(output, inputs, keepClasses));
+    }
+    return createDexConsumer(output, inputs, keepClasses);
+  }
+
+  private static DexIndexedConsumer createDexConsumer(
+      Path output, List<Path> inputs, boolean keepClasses)
+      throws DxUsageMessage {
+    if (keepClasses) {
+      if (!FileUtils.isArchive(output)) {
+        throw new DxCompatOptions.DxUsageMessage(
+            "Output must be an archive when --keep-classes is set.");
+      }
+      return new DexKeepClassesConsumer(output, inputs);
+    }
+    return FileUtils.isArchive(output)
+        ? new DexIndexedConsumer.ArchiveConsumer(output)
+        : new DexIndexedConsumer.DirectoryConsumer(output);
+  }
+
+  private static class SingleDexFileConsumer extends DexIndexedConsumer.ForwardingConsumer {
+
+    private byte[] bytes = null;
+
+    public SingleDexFileConsumer(DexIndexedConsumer consumer) {
+      super(consumer);
+    }
+
+    @Override
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+      if (fileIndex > 0) {
+        throw new CompilationError(
+            "Compilation result could not fit into a single dex file. "
+                + "Reduce the input-program size or run with --multi-dex enabled");
+      }
+      assert bytes == null;
+      // Store a copy of the bytes as we may not assume the backing is valid after accept returns.
+      bytes = data.copyByteData();
+    }
+
+    @Override
+    public void finished(DiagnosticsHandler handler) {
+      if (bytes != null) {
+        super.accept(0, ByteDataView.of(bytes), null, handler);
+      }
+      super.finished(handler);
+    }
+  }
+
+  private static class NamedDexFileConsumer extends DexIndexedConsumer.ForwardingConsumer {
+    private final Path output;
+
+    public NamedDexFileConsumer(Path output) {
+      super(null);
+      this.output = output;
+    }
+
+    @Override
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+      StandardOpenOption[] options = {
+        StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING
+      };
+      try (OutputStream stream = new BufferedOutputStream(Files.newOutputStream(output, options))) {
+        stream.write(data.getBuffer(), data.getOffset(), data.getLength());
+      } catch (IOException e) {
+        handler.error(new ExceptionDiagnostic(e, new PathOrigin(output)));
+      }
+    }
+  }
+
+  private static class DexKeepClassesConsumer extends DexIndexedConsumer.ArchiveConsumer {
+
+    private final List<Path> inputs;
+
+    public DexKeepClassesConsumer(Path archive, List<Path> inputs) {
+      super(archive);
+      this.inputs = inputs;
+    }
+
+    @Override
+    public void finished(DiagnosticsHandler handler) {
+      try {
+        writeZipWithClasses(handler);
+      } catch (IOException e) {
+        handler.error(new ExceptionDiagnostic(e, getOrigin()));
+      }
+      super.finished(handler);
+    }
+
+    private void writeZipWithClasses(DiagnosticsHandler handler) throws IOException {
+      // For each input archive file, add all class files within.
+      for (Path input : inputs) {
+        if (FileUtils.isArchive(input)) {
+          try (ZipFile zipFile = FileUtils.createZipFile(input.toFile(), StandardCharsets.UTF_8)) {
+            final Enumeration<? extends ZipEntry> entries = zipFile.entries();
+            while (entries.hasMoreElements()) {
+              ZipEntry entry = entries.nextElement();
+              if (ZipUtils.isClassFile(entry.getName())) {
+                try (InputStream entryStream = zipFile.getInputStream(entry)) {
+                  byte[] bytes = ByteStreams.toByteArray(entryStream);
+                  outputBuilder.addFile(entry.getName(), ByteDataView.of(bytes), handler);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  static void printHelpOn(PrintStream sink) throws IOException {
+    sink.println(USAGE_HEADER);
+    new DxCompatOptions.Spec().parser.printHelpOn(sink);
+  }
+
+  private static void processPath(File file, List<Path> files) {
+    if (!file.exists()) {
+      throw new CompilationError("File does not exist: " + file);
+    }
+    if (file.isDirectory()) {
+      processDirectory(file, files);
+      return;
+    }
+    Path path = file.toPath();
+    if (FileUtils.isZipFile(path) || FileUtils.isJarFile(path) || FileUtils.isClassFile(path)) {
+      files.add(path);
+      return;
+    }
+    if (FileUtils.isApkFile(path)) {
+      throw new Unimplemented("apk files not yet supported");
+    }
+  }
+
+  private static void processDirectory(File directory, List<Path> files) {
+    assert directory.exists();
+    for (File file : directory.listFiles()) {
+      processPath(file, files);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
index 079b8bb..f573b76 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
@@ -7,6 +7,7 @@
 import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /** Provides immutable access to {@link FieldAccessInfoImpl}. */
 public interface FieldAccessInfo {
@@ -29,5 +30,7 @@
 
   boolean isWritten();
 
+  boolean isWrittenInMethodSatisfying(Predicate<DexEncodedMethod> predicate);
+
   boolean isWrittenOutside(DexEncodedMethod method);
 }
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index a263cb6..5924435 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -147,6 +147,23 @@
   }
 
   /**
+   * Returns true if this field is written by a method for which {@param predicate} returns true.
+   */
+  @Override
+  public boolean isWrittenInMethodSatisfying(Predicate<DexEncodedMethod> predicate) {
+    if (writesWithContexts != null) {
+      for (Set<DexEncodedMethod> encodedWriteContexts : writesWithContexts.values()) {
+        for (DexEncodedMethod encodedWriteContext : encodedWriteContexts) {
+          if (predicate.test(encodedWriteContext)) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
    * Returns true if this field is written by a method in the program other than {@param method}.
    */
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 4d8d00d..8feed9b 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -98,7 +98,7 @@
   }
 
   private final Origin origin;
-  private final JarApplicationReader application;
+  private JarApplicationReader application;
   private CfCode code;
   protected ReparseContext context;
   private boolean reachabilitySensitive = false;
@@ -122,6 +122,8 @@
   public CfCode asCfCode() {
     if (code == null) {
       ReparseContext context = this.context;
+      JarApplicationReader application = this.application;
+      assert application != null;
       assert context != null;
       // The ClassCodeVisitor is in charge of setting this.context to null.
       try {
@@ -130,6 +132,7 @@
         for (Code code : context.codeList) {
           code.asLazyCfCode().code = null;
           code.asLazyCfCode().context = context;
+          code.asLazyCfCode().application = application;
         }
         try {
           parseCode(context, true);
@@ -155,6 +158,7 @@
     assert this.context != null;
     this.code = code;
     this.context = null;
+    this.application = null;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 2c86c7b..17b91f3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -25,12 +25,12 @@
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.DominatorTree.Assumption;
+import com.android.tools.r8.ir.code.FieldInstruction;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -56,9 +56,11 @@
       AppView<AppInfoWithLiveness> appView,
       IRCode code,
       OptimizationFeedback feedback,
+      DexProgramClass clazz,
       DexEncodedMethod method) {
+    assert clazz.type == method.method.holder;
     this.appView = appView;
-    this.clazz = appView.definitionFor(method.method.holder).asProgramClass();
+    this.clazz = clazz;
     this.code = code;
     this.feedback = feedback;
     this.method = method;
@@ -67,11 +69,29 @@
 
   public static void run(
       AppView<?> appView, IRCode code, OptimizationFeedback feedback, DexEncodedMethod method) {
-    if (appView.enableWholeProgramOptimizations() && method.isClassInitializer()) {
-      assert appView.appInfo().hasLiveness();
-      new FieldValueAnalysis(appView.withLiveness(), code, feedback, method)
-          .computeFieldOptimizationInfo();
+    if (!appView.enableWholeProgramOptimizations()) {
+      return;
     }
+    assert appView.appInfo().hasLiveness();
+    if (!method.isInitializer()) {
+      return;
+    }
+    DexProgramClass clazz = appView.definitionFor(method.method.holder).asProgramClass();
+    if (method.isInstanceInitializer()) {
+      if (!appView.options().enableValuePropagationForInstanceFields) {
+        return;
+      }
+      DexEncodedMethod otherInstanceInitializer =
+          clazz.lookupDirectMethod(other -> other.isInstanceInitializer() && other != method);
+      if (otherInstanceInitializer != null) {
+        // Conservatively bail out.
+        // TODO(b/125282093): Handle multiple instance initializers on the same class.
+        return;
+      }
+    }
+
+    new FieldValueAnalysis(appView.withLiveness(), code, feedback, clazz, method)
+        .computeFieldOptimizationInfo();
   }
 
   private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
@@ -86,55 +106,51 @@
     AppInfoWithLiveness appInfo = appView.appInfo();
     DominatorTree dominatorTree = null;
 
-    if (method.isClassInitializer()) {
-      DexType context = method.method.holder;
+    DexType context = method.method.holder;
 
-      // Find all the static-put instructions that assign a field in the enclosing class which is
-      // guaranteed to be assigned only in the current initializer.
-      boolean isStraightLineCode = true;
-      Map<DexEncodedField, LinkedList<StaticPut>> staticPutsPerField = new IdentityHashMap<>();
-      for (Instruction instruction : code.instructions()) {
-        if (instruction.isStaticPut()) {
-          StaticPut staticPut = instruction.asStaticPut();
-          DexField field = staticPut.getField();
-          DexEncodedField encodedField = appInfo.resolveField(field);
-          if (encodedField != null
-              && encodedField.field.holder == context
-              && appInfo.isStaticFieldWrittenOnlyInEnclosingStaticInitializer(encodedField)) {
-            staticPutsPerField
-                .computeIfAbsent(encodedField, ignore -> new LinkedList<>())
-                .add(staticPut);
-          }
-        }
-        if (instruction.isJumpInstruction()) {
-          if (!instruction.isGoto() && !instruction.isReturn()) {
-            isStraightLineCode = false;
-          }
+    // Find all the static-put instructions that assign a field in the enclosing class which is
+    // guaranteed to be assigned only in the current initializer.
+    boolean isStraightLineCode = true;
+    Map<DexEncodedField, LinkedList<FieldInstruction>> putsPerField = new IdentityHashMap<>();
+    for (Instruction instruction : code.instructions()) {
+      if (instruction.isFieldPut()) {
+        FieldInstruction fieldPut = instruction.asFieldInstruction();
+        DexField field = fieldPut.getField();
+        DexEncodedField encodedField = appInfo.resolveField(field);
+        if (encodedField != null
+            && encodedField.field.holder == context
+            && appInfo.isFieldOnlyWrittenInMethod(encodedField, method)) {
+          putsPerField.computeIfAbsent(encodedField, ignore -> new LinkedList<>()).add(fieldPut);
         }
       }
-
-      List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
-      for (Entry<DexEncodedField, LinkedList<StaticPut>> entry : staticPutsPerField.entrySet()) {
-        DexEncodedField encodedField = entry.getKey();
-        LinkedList<StaticPut> staticPuts = entry.getValue();
-        if (staticPuts.size() > 1) {
-          continue;
+      if (instruction.isJumpInstruction()) {
+        if (!instruction.isGoto() && !instruction.isReturn()) {
+          isStraightLineCode = false;
         }
-        StaticPut staticPut = staticPuts.getFirst();
-        if (!isStraightLineCode) {
-          if (dominatorTree == null) {
-            dominatorTree = new DominatorTree(code, Assumption.NO_UNREACHABLE_BLOCKS);
-          }
-          if (!dominatorTree.dominatesAllOf(staticPut.getBlock(), normalExitBlocks)) {
-            continue;
-          }
-        }
-        if (fieldMaybeReadBeforeInstruction(encodedField, staticPut)) {
-          continue;
-        }
-        updateFieldOptimizationInfo(encodedField, staticPut.value());
       }
     }
+
+    List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
+    for (Entry<DexEncodedField, LinkedList<FieldInstruction>> entry : putsPerField.entrySet()) {
+      DexEncodedField encodedField = entry.getKey();
+      LinkedList<FieldInstruction> fieldPuts = entry.getValue();
+      if (fieldPuts.size() > 1) {
+        continue;
+      }
+      FieldInstruction fieldPut = fieldPuts.getFirst();
+      if (!isStraightLineCode) {
+        if (dominatorTree == null) {
+          dominatorTree = new DominatorTree(code, Assumption.NO_UNREACHABLE_BLOCKS);
+        }
+        if (!dominatorTree.dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
+          continue;
+        }
+      }
+      if (fieldMaybeReadBeforeInstruction(encodedField, fieldPut)) {
+        continue;
+      }
+      updateFieldOptimizationInfo(encodedField, fieldPut.value());
+    }
   }
 
   private boolean fieldMaybeReadBeforeInstruction(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index fce7b09..92954e4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -30,6 +30,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Predicate;
 
 // TODO(b/112437944): Handle cycles in the graph + add a test that fails with the current
 //  implementation. The current caching mechanism is unsafe, because we may mark a message as not
@@ -222,6 +223,19 @@
         }
 
         if (newlyLiveField != null) {
+          // Mark hazzer and one-of proto fields as read from dynamicMethod() if they are written in
+          // the app. This is needed to ensure that field writes are not removed from the app.
+          DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer();
+          assert defaultInitializer != null;
+          Predicate<DexEncodedMethod> neitherDefaultConstructorNorDynamicMethod =
+              writer -> writer != defaultInitializer && writer != dynamicMethod;
+          if (enqueuer.isFieldWrittenInMethodSatisfying(
+              newlyLiveField, neitherDefaultConstructorNorDynamicMethod)) {
+            enqueuer.registerFieldRead(newlyLiveField.field, dynamicMethod);
+          }
+
+          // Unconditionally register the hazzer and one-of proto fields as written from
+          // dynamicMethod().
           if (enqueuer.registerFieldWrite(newlyLiveField.field, dynamicMethod)) {
             worklist.enqueueMarkReachableFieldAction(
                 clazz, newlyLiveField, KeepReason.reflectiveUseIn(dynamicMethod));
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 55cabed..682804e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -136,7 +136,7 @@
     LoadStoreHelper loadStoreHelper = new LoadStoreHelper(appView, code, typeVerificationHelper);
     loadStoreHelper.insertLoadsAndStores();
     // Run optimizations on phis and basic blocks in a fixpoint.
-    if (!appView.options().testing.disallowLoadStoreOptimization) {
+    if (appView.options().enableLoadStoreOptimization) {
       PhiOptimizations phiOptimizations = new PhiOptimizations();
       boolean reachedFixpoint = false;
       phiOptimizations.optimize(code);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 3e9ecab..45be243 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -943,10 +943,18 @@
   private DexType computeOutlineClassType() {
     DexType result;
     int count = 0;
+    String tempPrefix =
+        appView
+            .options()
+            .desugaredLibraryConfiguration
+            .getSynthesizedLibraryClassesPackagePrefix(appView);
+    String prefix = tempPrefix.replace('/', '.');
     do {
-      String name = OutlineOptions.CLASS_NAME + (count == 0 ? "" : Integer.toString(count));
+      String name =
+          prefix + OutlineOptions.CLASS_NAME + (count == 0 ? "" : Integer.toString(count));
       count++;
       result = appView.dexItemFactory().createType(DescriptorUtils.javaTypeToDescriptor(name));
+
     } while (appView.definitionFor(result) != null);
     return result;
   }
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 511b971..41995ce 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
@@ -59,7 +59,6 @@
 public final class BackportedMethodRewriter {
 
   public static final String UTILITY_CLASS_NAME_PREFIX = "$r8$backportedMethods$utility";
-  private static final String UTILITY_CLASS_DESCRIPTOR_PREFIX = "L" + UTILITY_CLASS_NAME_PREFIX;
 
   private final AppView<?> appView;
   private final IRConverter converter;
@@ -147,7 +146,7 @@
     // On R8 resolution is immediate, on d8 it may look-up.
     DexClass current = appView.definitionFor(method.holder);
     if (current == null) {
-     return null;
+      return null;
     }
     DexEncodedMethod dexEncodedMethod = current.lookupVirtualMethod(method);
     if (dexEncodedMethod != null) {
@@ -177,7 +176,7 @@
   }
 
   public static boolean hasRewrittenMethodPrefix(DexType clazz) {
-    return clazz.descriptor.toString().startsWith(UTILITY_CLASS_DESCRIPTOR_PREFIX);
+    return clazz.descriptor.toString().contains(UTILITY_CLASS_NAME_PREFIX);
   }
 
   public void synthesizeUtilityClasses(Builder<?> builder, ExecutorService executorService)
@@ -1425,15 +1424,19 @@
       DexItemFactory factory = appView.dexItemFactory();
       String unqualifiedName = method.holder.getName();
       // Avoid duplicate class names between core lib dex file and program dex files.
-      String desugaredLibUtilitySuffix =
-          appView.options().isDesugaredLibraryCompilation() ? "$desugaredLib" : "";
+      String desugaredLibPrefix =
+          appView
+              .options()
+              .desugaredLibraryConfiguration
+              .getSynthesizedLibraryClassesPackagePrefix(appView);
       String descriptor =
-          UTILITY_CLASS_DESCRIPTOR_PREFIX
+          "L"
+              + desugaredLibPrefix
+              + UTILITY_CLASS_NAME_PREFIX
               + '$'
               + unqualifiedName
               + '$'
               + method.proto.parameters.size()
-              + desugaredLibUtilitySuffix
               + '$'
               + methodName
               + ';';
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
index dda019e..1c11d78 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -5,6 +5,7 @@
 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.DexItemFactory;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -23,9 +24,12 @@
 
 public class DesugaredLibraryConfiguration {
 
+  public static final String FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX = "j$/";
+
   // TODO(b/134732760): should use DexString, DexType, DexMethod or so on when possible.
   private final AndroidApiLevel requiredCompilationAPILevel;
   private final boolean libraryCompilation;
+  private final String synthesizedLibraryClassesPackagePrefix;
   private final Map<String, String> rewritePrefix;
   private final Map<DexType, DexType> emulateLibraryInterface;
   private final Map<DexString, Map<DexType, DexType>> retargetCoreLibMember;
@@ -43,6 +47,7 @@
     return new DesugaredLibraryConfiguration(
         AndroidApiLevel.B,
         false,
+        FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX,
         prefix,
         ImmutableMap.of(),
         ImmutableMap.of(),
@@ -56,6 +61,7 @@
     return new DesugaredLibraryConfiguration(
         AndroidApiLevel.B,
         false,
+        FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX,
         ImmutableMap.of(),
         ImmutableMap.of(),
         ImmutableMap.of(),
@@ -68,6 +74,7 @@
   public DesugaredLibraryConfiguration(
       AndroidApiLevel requiredCompilationAPILevel,
       boolean libraryCompilation,
+      String packagePrefix,
       Map<String, String> rewritePrefix,
       Map<DexType, DexType> emulateLibraryInterface,
       Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
@@ -77,6 +84,7 @@
       List<String> extraKeepRules) {
     this.requiredCompilationAPILevel = requiredCompilationAPILevel;
     this.libraryCompilation = libraryCompilation;
+    this.synthesizedLibraryClassesPackagePrefix = packagePrefix;
     this.rewritePrefix = rewritePrefix;
     this.emulateLibraryInterface = emulateLibraryInterface;
     this.retargetCoreLibMember = retargetCoreLibMember;
@@ -100,6 +108,12 @@
     return libraryCompilation;
   }
 
+  public String getSynthesizedLibraryClassesPackagePrefix(AppView<?> appView) {
+    return appView.options().isDesugaredLibraryCompilation()
+        ? synthesizedLibraryClassesPackagePrefix
+        : "";
+  }
+
   public Map<String, String> getRewritePrefix() {
     return rewritePrefix;
   }
@@ -134,6 +148,8 @@
 
     private AndroidApiLevel requiredCompilationAPILevel;
     private boolean libraryCompilation = false;
+    private String synthesizedLibraryClassesPackagePrefix =
+        FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX;
     private Map<String, String> rewritePrefix = new HashMap<>();
     private Map<DexType, DexType> emulateLibraryInterface = new HashMap<>();
     private Map<DexString, Map<DexType, DexType>> retargetCoreLibMember = new IdentityHashMap<>();
@@ -146,6 +162,12 @@
       this.factory = dexItemFactory;
     }
 
+    public Builder setSynthesizedLibraryClassesPackagePrefix(String prefix) {
+      String replace = prefix.replace('.', '/');
+      this.synthesizedLibraryClassesPackagePrefix = replace;
+      return this;
+    }
+
     public Builder setRequiredCompilationAPILevel(AndroidApiLevel requiredCompilationAPILevel) {
       this.requiredCompilationAPILevel = requiredCompilationAPILevel;
       return this;
@@ -231,6 +253,7 @@
       return new DesugaredLibraryConfiguration(
           requiredCompilationAPILevel,
           libraryCompilation,
+          synthesizedLibraryClassesPackagePrefix,
           ImmutableMap.copyOf(rewritePrefix),
           ImmutableMap.copyOf(emulateLibraryInterface),
           ImmutableMap.copyOf(retargetCoreLibMember),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
index 4de1fe1..b66432e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -19,7 +19,7 @@
 
 public class DesugaredLibraryConfigurationParser {
 
-  private static final int MAX_SUPPORTED_VERSION = 2;
+  private static final int MAX_SUPPORTED_VERSION = 3;
 
   private final DesugaredLibraryConfiguration.Builder configurationBuilder;
   private final Reporter reporter;
@@ -67,6 +67,17 @@
                   + "distributed only in the early canary versions. Please update for "
                   + "production releases and to fix this warning."));
     }
+
+    if (jsonConfig.has("synthesized_library_classes_package_prefix")) {
+      configurationBuilder.setSynthesizedLibraryClassesPackagePrefix(
+          jsonConfig.get("synthesized_library_classes_package_prefix").getAsString());
+    } else {
+      reporter.warning(
+          new StringDiagnostic(
+              "Missing package_prefix, falling back to "
+                  + DesugaredLibraryConfiguration.FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX
+                  + " prefix, update desugared library configuration."));
+    }
     int required_compilation_api_level =
         jsonConfig.get("required_compilation_api_level").getAsInt();
     configurationBuilder.setRequiredCompilationAPILevel(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index 5d7c31f..d75b84c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -32,7 +32,6 @@
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperConversionCfCodeProvider;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Sets;
@@ -95,7 +94,6 @@
 public class DesugaredLibraryWrapperSynthesizer {
 
   public static final String WRAPPER_PREFIX = "$r8$wrapper$";
-  public static final String WRAPPER_DESCRIPTOR_PREFIX = "L" + WRAPPER_PREFIX;
   public static final String TYPE_WRAPPER_SUFFIX = "$-WRP";
   public static final String VIVIFIED_TYPE_WRAPPER_SUFFIX = "$-V-WRP";
 
@@ -120,7 +118,7 @@
   }
 
   public static boolean isSynthesizedWrapper(DexType clazz) {
-    return clazz.descriptor.toString().startsWith(WRAPPER_DESCRIPTOR_PREFIX);
+    return clazz.descriptor.toString().contains(WRAPPER_PREFIX);
   }
 
   boolean hasSynthesized(DexType type) {
@@ -152,10 +150,18 @@
   }
 
   private DexType createWrapperType(DexType type, String suffix) {
-    String prefix = appView.options().isDesugaredLibraryCompilation() ? "lib$" : "";
+    String desugaredLibPrefix =
+        appView
+            .options()
+            .desugaredLibraryConfiguration
+            .getSynthesizedLibraryClassesPackagePrefix(appView);
     return factory.createType(
-        DescriptorUtils.javaTypeToDescriptor(
-            WRAPPER_PREFIX + prefix + type.toString().replace('.', '$') + suffix));
+        "L"
+            + desugaredLibPrefix
+            + WRAPPER_PREFIX
+            + type.toString().replace('.', '$')
+            + suffix
+            + ";");
   }
 
   private DexType getWrapper(
@@ -260,7 +266,8 @@
         Collections.emptyList());
   }
 
-  private ChecksumSupplier getChecksumSupplier(DesugaredLibraryWrapperSynthesizer synthesizer, DexType keyType) {
+  private ChecksumSupplier getChecksumSupplier(
+      DesugaredLibraryWrapperSynthesizer synthesizer, DexType keyType) {
     return clazz -> {
       // The synthesized type wrappers are constructed lazily, so their lookup must be delayed
       // until the point the checksum is requested (at write time). The presence of a wrapper
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 6a9c3af..d26e953 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -474,6 +474,9 @@
           // Then, PACKAGE is more restrictive constraint.
           return one;
         }
+        if (appView.isSubtype(one.targetHolder, other.targetHolder).isTrue()) {
+          return new ConstraintWithTarget(Constraint.SAMECLASS, one.targetHolder);
+        }
         // TODO(b/128967328): towards finer-grained constraints, we need both.
         // The target method is still inlineable to methods with a holder from the same package of
         // one's holder and a subtype of other's holder.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 98f5a47..436f81b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -722,7 +722,7 @@
         if (method.isInstanceInitializer()) {
           InstanceInitializerInfo initializerInfo =
               method.getOptimizationInfo().getInstanceInitializerInfo();
-          if (!initializerInfo.isEligibleForClassInlining()) {
+          if (initializerInfo.receiverMayEscapeOutsideConstructorChain()) {
             return null;
           }
         }
@@ -838,7 +838,8 @@
   }
 
   private InliningInfo isEligibleIndirectVirtualMethodCall(DexMethod callee) {
-    DexEncodedMethod singleTarget = eligibleClassDefinition.lookupVirtualMethod(callee);
+    DexEncodedMethod singleTarget =
+        appView.appInfo().resolveMethod(eligibleClassDefinition, callee).getSingleTarget();
     if (isEligibleSingleTarget(singleTarget)) {
       return isEligibleVirtualMethodCall(
           null, callee, singleTarget, eligibility -> eligibility.returnsReceiver.isFalse());
@@ -1077,7 +1078,7 @@
     }
     InstanceInitializerInfo initializerInfo =
         encodedMethod.getOptimizationInfo().getInstanceInitializerInfo();
-    return initializerInfo.isEligibleForClassInlining();
+    return initializerInfo.receiverNeverEscapesOutsideConstructorChain();
   }
 
   private boolean exemptFromInstructionLimit(DexEncodedMethod inlinee) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index ee49c97..eac8791 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -5,6 +5,16 @@
 
 import static com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query.DIRECTLY;
 import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
+import static com.android.tools.r8.ir.code.Opcodes.ARGUMENT;
+import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_STRING;
+import static com.android.tools.r8.ir.code.Opcodes.DEX_ITEM_BASED_CONST_STRING;
+import static com.android.tools.r8.ir.code.Opcodes.GOTO;
+import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
+import static com.android.tools.r8.ir.code.Opcodes.RETURN;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -392,79 +402,78 @@
       // Defining a finalize method can observe the side-effect of Object.<init> GC registration.
       return null;
     }
+    NonTrivialInstanceInitializerInfo.Builder builder = NonTrivialInstanceInitializerInfo.builder();
     Value receiver = code.getThis();
-    for (Instruction insn : code.instructions()) {
-      if (insn.isReturn()) {
-        continue;
-      }
+    for (Instruction instruction : code.instructions()) {
+      switch (instruction.opcode()) {
+        case ARGUMENT:
+        case ASSUME:
+        case CONST_NUMBER:
+        case RETURN:
+          break;
 
-      if (insn.isAssume()) {
-        continue;
-      }
-
-      if (insn.isArgument()) {
-        continue;
-      }
-
-      if (insn.isConstInstruction()) {
-        if (insn.instructionInstanceCanThrow()) {
-          return null;
-        } else {
-          continue;
-        }
-      }
-
-      if (insn.isInvokeDirect()) {
-        InvokeDirect invokedDirect = insn.asInvokeDirect();
-        DexMethod invokedMethod = invokedDirect.getInvokedMethod();
-        if (!dexItemFactory.isConstructor(invokedMethod)) {
-          return null;
-        }
-        if (invokedMethod.holder != clazz.superType) {
-          return null;
-        }
-        // java.lang.Enum.<init>() and java.lang.Object.<init>() are considered trivial.
-        if (invokedMethod == dexItemFactory.enumMethods.constructor
-            || invokedMethod == dexItemFactory.objectMethods.constructor) {
-          continue;
-        }
-        DexEncodedMethod callTarget = appView.definitionFor(invokedMethod);
-        if (callTarget == null
-            || callTarget.getOptimizationInfo().getInstanceInitializerInfo().isDefaultInfo()
-            || invokedDirect.getReceiver() != receiver) {
-          return null;
-        }
-        for (Value value : invokedDirect.inValues()) {
-          if (value != receiver && !(value.isConstant() || value.isArgument())) {
+        case CONST_CLASS:
+        case CONST_STRING:
+        case DEX_ITEM_BASED_CONST_STRING:
+          if (instruction.instructionMayTriggerMethodInvocation(appView, clazz.type)) {
             return null;
           }
-        }
-        continue;
-      }
+          break;
 
-      if (insn.isInstancePut()) {
-        InstancePut instancePut = insn.asInstancePut();
-        DexEncodedField field = appView.appInfo().resolveFieldOn(clazz, instancePut.getField());
-        if (field == null
-            || instancePut.object() != receiver
-            || (instancePut.value() != receiver && !instancePut.value().isArgument())) {
+        case GOTO:
+          // Trivial goto to the next block.
+          if (!instruction.asGoto().isTrivialGotoToTheNextBlock(code)) {
+            return null;
+          }
+          break;
+
+        case INSTANCE_PUT:
+          {
+            InstancePut instancePut = instruction.asInstancePut();
+            DexEncodedField field = appView.appInfo().resolveFieldOn(clazz, instancePut.getField());
+            if (field == null
+                || instancePut.object() != receiver
+                || (instancePut.value() != receiver && !instancePut.value().isArgument())) {
+              return null;
+            }
+          }
+          break;
+
+        case INVOKE_DIRECT:
+          {
+            InvokeDirect invoke = instruction.asInvokeDirect();
+            DexMethod invokedMethod = invoke.getInvokedMethod();
+            if (!dexItemFactory.isConstructor(invokedMethod)) {
+              return null;
+            }
+            if (invokedMethod.holder != clazz.superType) {
+              return null;
+            }
+            // java.lang.Enum.<init>() and java.lang.Object.<init>() are considered trivial.
+            if (invokedMethod == dexItemFactory.enumMethods.constructor
+                || invokedMethod == dexItemFactory.objectMethods.constructor) {
+              break;
+            }
+            DexEncodedMethod singleTarget = appView.definitionFor(invokedMethod);
+            if (singleTarget == null
+                || singleTarget.getOptimizationInfo().getInstanceInitializerInfo().isDefaultInfo()
+                || invoke.getReceiver() != receiver) {
+              return null;
+            }
+            for (Value value : invoke.inValues()) {
+              if (value != receiver && !(value.isConstant() || value.isArgument())) {
+                return null;
+              }
+            }
+          }
+          break;
+
+        default:
+          // Other instructions make the instance initializer not eligible.
           return null;
-        }
-        continue;
       }
-
-      if (insn.isGoto()) {
-        // Trivial goto to the next block.
-        if (insn.asGoto().isTrivialGotoToTheNextBlock(code)) {
-          continue;
-        }
-        return null;
-      }
-
-      // Other instructions make the instance initializer not eligible.
-      return null;
     }
-    return NonTrivialInstanceInitializerInfo.INSTANCE;
+    return builder.build();
   }
 
   private void identifyInvokeSemanticsForInlining(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
index a75a255..e93cda7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
@@ -29,16 +29,6 @@
   }
 
   @Override
-  public boolean isEligibleForClassInlining() {
-    return false;
-  }
-
-  @Override
-  public boolean isEligibleForClassStaticizing() {
-    return false;
-  }
-
-  @Override
   public boolean instanceFieldInitializationMayDependOnEnvironment() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
index 2614eb3..24cb9af 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
@@ -14,10 +14,6 @@
    */
   public abstract AbstractFieldSet readSet();
 
-  public abstract boolean isEligibleForClassInlining();
-
-  public abstract boolean isEligibleForClassStaticizing();
-
   /**
    * Returns true if one of the instance fields on the enclosing class may be initialized with a
    * value that may depend on the runtime environment by this constructor.
@@ -47,6 +43,10 @@
    */
   public abstract boolean receiverNeverEscapesOutsideConstructorChain();
 
+  public final boolean receiverMayEscapeOutsideConstructorChain() {
+    return !receiverNeverEscapesOutsideConstructorChain();
+  }
+
   public boolean isDefaultInfo() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index 67ad395..7802e12 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -4,43 +4,108 @@
 
 package com.android.tools.r8.ir.optimize.info.initializer;
 
+import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
 
 public final class NonTrivialInstanceInitializerInfo extends InstanceInitializerInfo {
 
-  public static final NonTrivialInstanceInitializerInfo INSTANCE =
-      new NonTrivialInstanceInitializerInfo();
+  private static final int INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT = 1 << 0;
+  private static final int NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS = 1 << 1;
+  private static final int RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN = 1 << 2;
 
-  private NonTrivialInstanceInitializerInfo() {}
+  private final int data;
+  private final AbstractFieldSet readSet;
+
+  private NonTrivialInstanceInitializerInfo(int data, AbstractFieldSet readSet) {
+    assert verifyNoUnknownBits(data);
+    this.data = data;
+    this.readSet = readSet;
+  }
+
+  private static boolean verifyNoUnknownBits(int data) {
+    int knownBits =
+        INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT
+            | NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS
+            | RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN;
+    assert (data & ~knownBits) == 0;
+    return true;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
 
   @Override
   public AbstractFieldSet readSet() {
-    return EmptyFieldSet.getInstance();
-  }
-
-  @Override
-  public boolean isEligibleForClassInlining() {
-    return true;
-  }
-
-  @Override
-  public boolean isEligibleForClassStaticizing() {
-    return true;
+    return readSet;
   }
 
   @Override
   public boolean instanceFieldInitializationMayDependOnEnvironment() {
-    return false;
+    return (data & INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT) == 0;
   }
 
   @Override
   public boolean mayHaveOtherSideEffectsThanInstanceFieldAssignments() {
-    return false;
+    return (data & NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS) == 0;
   }
 
   @Override
   public boolean receiverNeverEscapesOutsideConstructorChain() {
-    return true;
+    return (data & RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN) != 0;
+  }
+
+  public static class Builder {
+
+    private int data =
+        INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT
+            | NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS
+            | RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN;
+    private AbstractFieldSet readSet = EmptyFieldSet.getInstance();
+
+    private boolean isTrivial() {
+      return data == 0 && readSet.isTop();
+    }
+
+    public Builder markFieldAsRead(DexEncodedField field) {
+      if (readSet.isKnownFieldSet()) {
+        if (readSet.isBottom()) {
+          readSet = new ConcreteMutableFieldSet(field);
+        } else {
+          readSet.asConcreteFieldSet().add(field);
+        }
+      }
+      assert readSet.contains(field);
+      return this;
+    }
+
+    public Builder markAllFieldsAsRead() {
+      readSet = UnknownFieldSet.getInstance();
+      return this;
+    }
+
+    public Builder setInstanceFieldInitializationMayDependOnEnvironment() {
+      data &= ~INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT;
+      return this;
+    }
+
+    public Builder setMayHaveOtherSideEffectsThanInstanceFieldAssignments() {
+      data &= ~NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS;
+      return this;
+    }
+
+    public Builder setReceiverMayEscapeOutsideConstructorChain() {
+      data &= ~RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN;
+      return this;
+    }
+
+    public InstanceInitializerInfo build() {
+      return isTrivial()
+          ? DefaultInstanceInitializerInfo.getInstance()
+          : new NonTrivialInstanceInitializerInfo(data, readSet);
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 12992ca..e019acd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -27,7 +27,6 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.optimize.CodeRewriter;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
 import com.android.tools.r8.ir.optimize.staticizer.ClassStaticizer.CandidateInfo;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -155,9 +154,7 @@
       // fields this should guarantee that the constructor is empty.
       assert candidateClass.instanceFields().size() == 0;
       assert constructorUsed.isProcessed();
-      InstanceInitializerInfo initializerInfo =
-          constructorUsed.getOptimizationInfo().getInstanceInitializerInfo();
-      if (!initializerInfo.isEligibleForClassStaticizing()) {
+      if (constructorUsed.getOptimizationInfo().mayHaveSideEffects()) {
         it.remove();
         continue;
       }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
new file mode 100644
index 0000000..fcf1453
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
@@ -0,0 +1,332 @@
+// 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.kotlin;
+
+import com.android.tools.r8.naming.Range;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+// This is a parser for the Kotlin-generated source debug extensions, which is a stratified map.
+// The kotlin-parser for this data is can be found here in the kotlin compiler:
+// compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPParser.kt
+public class KotlinSourceDebugExtensionParser {
+
+  private static final String SMAP_IDENTIFIER = "SMAP";
+  private static final String SMAP_SECTION_START = "*S";
+  private static final String SMAP_SECTION_KOTLIN_START = SMAP_SECTION_START + " Kotlin";
+  private static final String SMAP_FILES_IDENTIFIER = "*F";
+  private static final String SMAP_LINES_IDENTIFIER = "*L";
+  private static final String SMAP_END_IDENTIFIER = "*E";
+
+  public static class KotlinSourceDebugExtensionParserException extends Exception {
+
+    KotlinSourceDebugExtensionParserException(String message) {
+      super(message);
+    }
+  }
+
+  public static class BufferedStringReader implements Closeable {
+
+    private final BufferedReader reader;
+
+    private String readLine;
+
+    BufferedStringReader(String data) {
+      reader = new BufferedReader(new StringReader(data));
+    }
+
+    String readNextLine() throws IOException {
+      return readLine = reader.readLine();
+    }
+
+    boolean readExpectedLine(String expected) throws IOException {
+      return readNextLine().equals(expected);
+    }
+
+    void readExpectedLineOrThrow(String expected)
+        throws KotlinSourceDebugExtensionParserException, IOException {
+      if (!readExpectedLine(expected)) {
+        throw new KotlinSourceDebugExtensionParserException(
+            "The string " + readLine + " does not match the expected string " + expected);
+      }
+    }
+
+    boolean isEOF() {
+      return readLine == null;
+    }
+
+    BufferedStringReader readUntil(String terminator) throws IOException {
+      while (!terminator.equals(readLine) && !isEOF()) {
+        readNextLine();
+      }
+      if (isEOF()) {
+        return this;
+      }
+      return this;
+    }
+
+    void readUntil(
+        String terminator,
+        int linesInBlock,
+        ThrowingConsumer<List<String>, KotlinSourceDebugExtensionParserException> callback)
+        throws IOException, KotlinSourceDebugExtensionParserException {
+      if (terminator.equals(readLine)) {
+        return;
+      }
+      List<String> readStrings = new ArrayList<>();
+      readStrings.add(readNextLine());
+      int linesLeft = linesInBlock;
+      while (!terminator.equals(readLine) && !isEOF()) {
+        if (linesLeft == 1) {
+          assert readStrings.size() == linesInBlock;
+          callback.accept(readStrings);
+          linesLeft = linesInBlock;
+          readStrings = new ArrayList<>();
+        } else {
+          linesLeft -= 1;
+        }
+        readStrings.add(readNextLine());
+      }
+      if (readStrings.size() > 0 && !readStrings.get(0).equals(terminator)) {
+        throw new KotlinSourceDebugExtensionParserException(
+            "Block size does not match linesInBlock = " + linesInBlock);
+      }
+    }
+
+    @Override
+    public void close() throws IOException {
+      reader.close();
+    }
+  }
+
+  public static Result parse(String annotationData) {
+    if (annotationData == null || annotationData.isEmpty()) {
+      return null;
+    }
+
+    // The source debug smap data is on the following form:
+    // SMAP
+    // <filename>
+    // Kotlin <-- why is this even here?
+    // *S <section>
+    // *F
+    // + <file_id_i> <file_name_i>
+    // <path_i>
+    // *L
+    // <from>#<file>,<to>:<debug-line-position>
+    // <from>#<file>:<debug-line-position>
+    // *E
+    // *S KotlinDebug
+    // ***
+    // *E
+    try (BufferedStringReader reader = new BufferedStringReader(annotationData)) {
+      ResultBuilder builder = new ResultBuilder();
+      // Check for SMAP
+      if (!reader.readExpectedLine(SMAP_IDENTIFIER)) {
+        return null;
+      }
+      if (reader.readUntil(SMAP_SECTION_KOTLIN_START).isEOF()) {
+        return null;
+      }
+      // At this point we should be parsing a kotlin source debug extension, so we will throw if we
+      // read an unexpected line.
+      reader.readExpectedLineOrThrow(SMAP_FILES_IDENTIFIER);
+      // Iterate over the files section with the format:
+      // + <file_number_i> <file_name_i>
+      // <file_path_i>
+      reader.readUntil(
+          SMAP_LINES_IDENTIFIER, 2, block -> addFileToBuilder(block.get(0), block.get(1), builder));
+
+      // Ensure that we read *L.
+      if (reader.isEOF()) {
+        throw new KotlinSourceDebugExtensionParserException(
+            "Unexpected EOF - no debug line positions");
+      }
+      // Iterate over the debug line number positions:
+      // <from>#<file>,<to>:<debug-line-position>
+      // or
+      // <from>#<file>:<debug-line-position>
+      reader.readUntil(
+          SMAP_END_IDENTIFIER, 1, block -> addDebugEntryToBuilder(block.get(0), builder));
+
+      // Ensure that we read the end section *E.
+      if (reader.isEOF()) {
+        throw new KotlinSourceDebugExtensionParserException(
+            "Unexpected EOF when parsing SMAP debug entries");
+      }
+
+      return builder.build();
+    } catch (IOException | KotlinSourceDebugExtensionParserException e) {
+      return null;
+    }
+  }
+
+  private static void addFileToBuilder(String entryLine, String filePath, ResultBuilder builder)
+      throws KotlinSourceDebugExtensionParserException {
+    // + <file_number_i> <file_name_i>
+    // <file_path_i>
+    String[] entries = entryLine.trim().split(" ");
+    if (entries.length != 3 || !entries[0].equals("+")) {
+      throw new KotlinSourceDebugExtensionParserException(
+          "Wrong number of entries on line " + entryLine);
+    }
+    String fileName = entries[2];
+    if (fileName.isEmpty()) {
+      throw new KotlinSourceDebugExtensionParserException(
+          "Did not expect file name to be empty for line " + entryLine);
+    }
+    if (filePath == null || filePath.isEmpty()) {
+      throw new KotlinSourceDebugExtensionParserException(
+          "Did not expect file path to be null or empty for " + entryLine);
+    }
+    int index = asInteger(entries[1]);
+    Source source = new Source(fileName, filePath);
+    Source existingSource = builder.files.put(index, source);
+    if (existingSource != null) {
+      throw new KotlinSourceDebugExtensionParserException(
+          "File index " + index + " was already mapped to an existing source: " + source);
+    }
+  }
+
+  private static int asInteger(String numberAsString)
+      throws KotlinSourceDebugExtensionParserException {
+    int number = -1;
+    try {
+      number = Integer.parseInt(numberAsString);
+    } catch (NumberFormatException e) {
+      throw new KotlinSourceDebugExtensionParserException(
+          "Could not parse number " + numberAsString);
+    }
+    return number;
+  }
+
+  private static void addDebugEntryToBuilder(String debugEntry, ResultBuilder builder)
+      throws KotlinSourceDebugExtensionParserException {
+    // <from>#<file>,<to>:<debug-line-position>
+    // or
+    // <from>#<file>:<debug-line-position>
+    try {
+      int targetSplit = debugEntry.indexOf(':');
+      int target = Integer.parseInt(debugEntry.substring(targetSplit + 1));
+      String original = debugEntry.substring(0, targetSplit);
+      int fileIndexSplit = original.indexOf('#');
+      int originalStart = Integer.parseInt(original.substring(0, fileIndexSplit));
+      // The range may have a different end than start.
+      String fileAndEndRange = original.substring(fileIndexSplit + 1);
+      int endRangeCharPosition = fileAndEndRange.indexOf(',');
+      int originalEnd = originalStart;
+      if (endRangeCharPosition > -1) {
+        // The file should be at least one number wide.
+        assert endRangeCharPosition > 0;
+        originalEnd = Integer.parseInt(fileAndEndRange.substring(endRangeCharPosition + 1));
+      } else {
+        endRangeCharPosition = fileAndEndRange.length();
+      }
+      int fileIndex = Integer.parseInt(fileAndEndRange.substring(0, endRangeCharPosition));
+      Source thisFileSource = builder.files.get(fileIndex);
+      if (thisFileSource != null) {
+        Range range = new Range(originalStart, originalEnd);
+        Position position = new Position(thisFileSource, range);
+        Position existingPosition = builder.positions.put(target, position);
+        assert existingPosition == null
+            : "Position index "
+                + target
+                + " was already mapped to an existing position: "
+                + position;
+      }
+    } catch (NumberFormatException e) {
+      throw new KotlinSourceDebugExtensionParserException("Could not convert position to number");
+    }
+  }
+
+  public static class Result {
+
+    private final Map<Integer, Source> files;
+    private final Map<Integer, Position> positions;
+
+    private Result(Map<Integer, Source> files, Map<Integer, Position> positions) {
+      this.files = files;
+      this.positions = positions;
+    }
+
+    public Map<Integer, Source> getFiles() {
+      return files;
+    }
+
+    public Map<Integer, Position> getPositions() {
+      return positions;
+    }
+  }
+
+  public static class ResultBuilder {
+    final Map<Integer, Source> files = new HashMap<>();
+    final Map<Integer, Position> positions = new HashMap<>();
+
+    public Result build() {
+      return new Result(files, positions);
+    }
+  }
+
+  public static class Source {
+    private final String fileName;
+    private final String path;
+
+    private Source(String fileName, String path) {
+      this.fileName = fileName;
+      this.path = path;
+    }
+
+    public String getFileName() {
+      return fileName;
+    }
+
+    public String getPath() {
+      return path;
+    }
+
+    @Override
+    public String toString() {
+      return path + "(" + fileName + ")";
+    }
+  }
+
+  public static class Position {
+    private final Source source;
+    private final Range range;
+
+    public Position(Source source, Range range) {
+      this.source = source;
+      this.range = range;
+    }
+
+    public Source getSource() {
+      return source;
+    }
+
+    public Range getRange() {
+      return range;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder sb = new StringBuilder();
+      sb.append(range.from);
+      sb.append("#");
+      sb.append(source);
+      if (range.to != range.from) {
+        sb.append(",");
+        sb.append(range.to);
+      }
+      return sb.toString();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBase.java b/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
index c5284ea..2874fc8 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.TypeReference;
 
 public interface RetraceBase {
 
@@ -16,6 +17,8 @@
 
   RetraceClassResult retrace(ClassReference classReference);
 
+  RetraceTypeResult retrace(TypeReference typeReference);
+
   String retraceSourceFile(ClassReference classReference, String sourceFile);
 
   String retraceSourceFile(
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java b/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
index ff196c1..c60cd91 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.utils.Box;
 import com.google.common.collect.Sets;
 import com.google.common.io.Files;
@@ -49,6 +50,11 @@
   }
 
   @Override
+  public RetraceTypeResult retrace(TypeReference typeReference) {
+    return new RetraceTypeResult(typeReference, this);
+  }
+
+  @Override
   public String retraceSourceFile(
       ClassReference obfuscatedClass,
       String sourceFile,
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
new file mode 100644
index 0000000..b04d9a8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
@@ -0,0 +1,55 @@
+// 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.retrace;
+
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.retrace.RetraceTypeResult.Element;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+public class RetraceTypeResult extends Result<Element, RetraceTypeResult> {
+
+  private final TypeReference obfuscatedType;
+  private final RetraceBase retraceBase;
+
+  RetraceTypeResult(TypeReference obfuscatedType, RetraceBase retraceBase) {
+    this.obfuscatedType = obfuscatedType;
+    this.retraceBase = retraceBase;
+  }
+
+  public Stream<Element> stream() {
+    // Handle void and primitive types as single element results.
+    if (obfuscatedType == null || obfuscatedType.isPrimitive()) {
+      return Stream.of(new Element(obfuscatedType));
+    }
+    if (obfuscatedType.isArray()) {
+      int dimensions = obfuscatedType.asArray().getDimensions();
+      return retraceBase.retrace(obfuscatedType.asArray().getBaseType()).stream()
+          .map(base -> new Element(Reference.array(base.getTypeReference(), dimensions)));
+    }
+    return retraceBase.retrace(obfuscatedType.asClass()).stream()
+        .map(clazz -> new Element(clazz.getClassReference()));
+  }
+
+  @Override
+  public RetraceTypeResult forEach(Consumer<Element> resultConsumer) {
+    stream().forEach(resultConsumer);
+    return this;
+  }
+
+  public static class Element {
+
+    private final TypeReference typeReference;
+
+    public Element(TypeReference typeReference) {
+      this.typeReference = typeReference;
+    }
+
+    public TypeReference getTypeReference() {
+      return typeReference;
+    }
+  }
+}
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 d1028f2..69e1930 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -761,22 +761,26 @@
     return false;
   }
 
-  public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
+  public boolean isFieldOnlyWrittenInMethod(DexEncodedField field, DexEncodedMethod method) {
     assert checkIfObsolete();
     assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
     if (!isPinned(field.field)) {
-      DexEncodedMethod staticInitializer =
-          definitionFor(field.field.holder).asProgramClass().getClassInitializer();
-      if (staticInitializer != null) {
-        FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.field);
-        return fieldAccessInfo != null
-            && fieldAccessInfo.isWritten()
-            && !fieldAccessInfo.isWrittenOutside(staticInitializer);
-      }
+      FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.field);
+      return fieldAccessInfo != null
+          && fieldAccessInfo.isWritten()
+          && !fieldAccessInfo.isWrittenOutside(method);
     }
     return false;
   }
 
+  public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
+    assert checkIfObsolete();
+    assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
+    DexEncodedMethod staticInitializer =
+        definitionFor(field.field.holder).asProgramClass().getClassInitializer();
+    return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer);
+  }
+
   public boolean mayPropagateValueFor(DexReference reference) {
     assert checkIfObsolete();
     return !isPinned(reference) && !neverPropagateValue.contains(reference);
@@ -1022,7 +1026,9 @@
     DexProgramClass refinedHolder =
         (refinedReceiverIsStrictSubType ? definitionFor(refinedReceiverType) : holder)
             .asProgramClass();
-    assert refinedHolder != null;
+    if (refinedHolder == null) {
+      return null;
+    }
     assert !refinedHolder.isInterface();
     if (method.isSingleVirtualMethodCached(refinedReceiverType)) {
       return method.getSingleVirtualMethodCache(refinedReceiverType);
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 245f3c6..a9f60ec 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1907,6 +1907,12 @@
     return info != null && info.isRead();
   }
 
+  public boolean isFieldWrittenInMethodSatisfying(
+      DexEncodedField field, Predicate<DexEncodedMethod> predicate) {
+    FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.field);
+    return info != null && info.isWrittenInMethodSatisfying(predicate);
+  }
+
   public boolean isFieldWrittenOutsideDefaultConstructor(DexEncodedField field) {
     FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.field);
     if (info == null) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
index f1e06d1..0dcf1eb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
@@ -261,7 +261,7 @@
       attributes.add(SOURCE_DEBUG_EXTENSION);
     }
     if (runtimeVisibleAnnotations) {
-      attributes.add(RUNTIME_INVISIBLE_ANNOTATIONS);
+      attributes.add(RUNTIME_VISIBLE_ANNOTATIONS);
     }
     if (runtimeInvisibleAnnotations) {
       attributes.add(RUNTIME_INVISIBLE_ANNOTATIONS);
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 dc998e2..e815737 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -183,9 +183,9 @@
 
   // Flag to toggle if DEX code objects should pass-through without IR processing.
   public boolean passthroughDexCode = false;
-  // TODO(b/134705306): Currently allow merging dex files resulting from Java 8 library
-  //  desugaring until all D8 users are complient.
-  public boolean enableNeverMergePrefixes = false;
+
+  // Flag to toggle if the prefix based merge restriction should be enforced.
+  public boolean enableNeverMergePrefixes = true;
   public Set<String> neverMergePrefixes = ImmutableSet.of("j$.");
 
   public boolean libraryInterfacesMayHaveStaticInitialization = false;
@@ -246,6 +246,8 @@
   public boolean enableInitializedClassesInInstanceMethodsAnalysis = true;
   public boolean enableRedundantFieldLoadElimination = true;
   public boolean enableValuePropagation = true;
+  // TODO(b/125282093): Enable member value propagation for instance fields.
+  public boolean enableValuePropagationForInstanceFields = false;
   public boolean enableUninstantiatedTypeOptimization = true;
   // TODO(b/138917494): Disable until we have numbers on potential performance penalties.
   public boolean enableRedundantConstNumberOptimization = false;
@@ -391,6 +393,8 @@
   public boolean readCompileTimeAnnotations = true;
   public List<String> logArgumentsFilter = ImmutableList.of();
 
+  // Flag to turn on/offLoad/store optimization in the Cf back-end.
+  public boolean enableLoadStoreOptimization = true;
   // Flag to turn on/off lambda class merging in R8.
   public boolean enableLambdaMerging = false;
   // Flag to turn on/off desugaring in D8/R8.
@@ -1007,7 +1011,6 @@
     public Set<Inliner.Reason> validInliningReasons = null;
     public boolean noLocalsTableOnInput = false;
     public boolean forceNameReflectionOptimization = false;
-    public boolean disallowLoadStoreOptimization = false;
     public boolean enableNarrowingChecksInD8 = false;
     public Consumer<IRCode> irModifier = null;
     // TODO(b/129458850) When fixed, remove this and change all usages to "true".
diff --git a/src/main/keep.txt b/src/main/keep.txt
index d52b8f0..b0c58c9 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -25,3 +25,6 @@
 
 # Compatibility command line program used by the Android Platform build.
 -keep public class com.android.tools.r8.compatproguard.CompatProguard { public static void main(java.lang.String[]); }
+
+# Compatibility command line program used by in google3.
+-keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
\ No newline at end of file
diff --git a/src/test/examples/shaking18/Options.java b/src/test/examples/shaking18/Options.java
index edb9d77..7012340 100644
--- a/src/test/examples/shaking18/Options.java
+++ b/src/test/examples/shaking18/Options.java
@@ -4,9 +4,7 @@
 package shaking18;
 
 public class Options {
-  // TODO(b/138913138): member value propagation can behave same with and without initialization.
-  // public boolean alwaysFalse = false;
-  public boolean alwaysFalse;
+  public boolean alwaysFalse = false;
   public boolean dummy = false;
 
   public Options() {}
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index c913b40..d740e54 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.D8Command.Builder;
 import com.android.tools.r8.TestBase.Backend;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase.KeepRuleConsumer;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index 268709d..2b6cd3c 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -3,20 +3,19 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import static com.android.tools.r8.ToolHelper.KT_COMPILER;
-import static com.android.tools.r8.ToolHelper.KT_PRELOADER;
 import static com.android.tools.r8.ToolHelper.isWindows;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.ZipUtils;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -26,32 +25,59 @@
 
 public class KotlinCompilerTool {
 
+  public static final class KotlinCompiler {
+
+    private final String name;
+    private final Path path;
+
+    public KotlinCompiler(String name, Path path) {
+      this.name = name;
+      this.path = path;
+    }
+
+    public Path getPath() {
+      return path;
+    }
+  }
+
+  public static KotlinCompiler KOTLINC =
+      new KotlinCompiler(
+          "kotlinc",
+          Paths.get(ToolHelper.THIRD_PARTY_DIR, "kotlin", "kotlinc", "lib", "kotlin-compiler.jar"));
+
   private final CfRuntime jdk;
   private final TestState state;
-  private final String kotlincJar;
+  private final KotlinCompiler compiler;
+  private final KotlinTargetVersion targetVersion;
   private final List<Path> sources = new ArrayList<>();
   private final List<Path> classpath = new ArrayList<>();
   private Path output = null;
 
-  private KotlinCompilerTool(CfRuntime jdk, TestState state, Path kotlincJar) {
+  private KotlinCompilerTool(
+      CfRuntime jdk, TestState state, KotlinCompiler compiler, KotlinTargetVersion targetVersion) {
     this.jdk = jdk;
     this.state = state;
-    this.kotlincJar = kotlincJar == null ? KT_COMPILER : kotlincJar.toString();
+    this.compiler = compiler;
+    this.targetVersion = targetVersion;
   }
 
-  public static KotlinCompilerTool create(CfRuntime jdk, TemporaryFolder temp) {
-    return create(jdk, new TestState(temp), null);
+  public static KotlinCompilerTool create(
+      CfRuntime jdk,
+      TemporaryFolder temp,
+      KotlinCompiler kotlinCompiler,
+      KotlinTargetVersion kotlinTargetVersion) {
+    return create(jdk, new TestState(temp), kotlinCompiler, kotlinTargetVersion);
   }
 
-  public static KotlinCompilerTool create (CfRuntime jdk, TemporaryFolder temp, Path kotlincJar) {
-    return create(jdk, new TestState(temp), kotlincJar);
+  public static KotlinCompilerTool create(
+      CfRuntime jdk,
+      TestState state,
+      KotlinCompiler kotlinCompiler,
+      KotlinTargetVersion kotlinTargetVersion) {
+    return new KotlinCompilerTool(jdk, state, kotlinCompiler, kotlinTargetVersion);
   }
 
-  public static KotlinCompilerTool create(CfRuntime jdk, TestState state, Path kotlincJar) {
-    return new KotlinCompilerTool(jdk, state, kotlincJar);
-  }
-
-  public KotlinCompilerTool addSourceFiles(Path files) {
+  public KotlinCompilerTool addSourceFiles(Path... files) {
     return addSourceFiles(Arrays.asList(files));
   }
 
@@ -78,9 +104,7 @@
   }
 
   private Path getOrCreateOutputPath() throws IOException {
-    return output != null
-        ? output
-        : state.getNewTempFolder().resolve("out.jar");
+    return output != null ? output : state.getNewTempFolder().resolve("out.jar");
   }
 
   /** Compile and return the compilations process result object. */
@@ -98,15 +122,15 @@
   }
 
   private ProcessResult compileInternal(Path output) throws IOException {
-    Path outdir = Files.isDirectory(output) ? output : state.getNewTempFolder();
     List<String> cmdline = new ArrayList<>();
     cmdline.add(jdk.getJavaExecutable().toString());
-    cmdline.add("-jar");
-    cmdline.add(KT_PRELOADER);
-    cmdline.add("org.jetbrains.kotlin.preloading.Preloader");
     cmdline.add("-cp");
-    cmdline.add(kotlincJar);
+    cmdline.add(compiler.getPath().toString());
     cmdline.add(ToolHelper.K2JVMCompiler);
+    cmdline.add("-jdk-home");
+    cmdline.add(jdk.getJavaHome().toString());
+    cmdline.add("-jvm-target");
+    cmdline.add(targetVersion.getJvmTargetString());
     for (Path source : sources) {
       cmdline.add(source.toString());
     }
@@ -120,11 +144,6 @@
           .collect(Collectors.joining(isWindows() ? ";" : ":")));
     }
     ProcessBuilder builder = new ProcessBuilder(cmdline);
-    ProcessResult kotlincResult = ToolHelper.runProcess(builder);
-    if (FileUtils.isJarFile(output)) {
-      assert !outdir.equals(output);
-      ZipUtils.zip(output, outdir);
-    }
-    return kotlincResult;
+    return ToolHelper.runProcess(builder);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index a483cfa..8e3e149 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.R8Command.Builder;
 import com.android.tools.r8.TestBase.Backend;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase.KeepRuleConsumer;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.CollectingGraphConsumer;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 023e0e7..57eae6b 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -11,9 +11,11 @@
 
 import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
 import com.android.tools.r8.DataResourceProvider.Visitor;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.dex.ApplicationReader;
@@ -206,16 +208,9 @@
     return JavaCompilerTool.create(jdk, temp);
   }
 
-  public KotlinCompilerTool kotlinc(CfRuntime jdk) {
-    return KotlinCompilerTool.create(jdk, temp);
-  }
-
-  public KotlinCompilerTool kotlinc(CfRuntime jdk, Path kotlincJar) {
-    return KotlinCompilerTool.create(jdk, temp, kotlincJar);
-  }
-
-  public static KotlinCompilerTool kotlinc(CfRuntime jdk, TemporaryFolder temp) {
-    return KotlinCompilerTool.create(jdk, temp);
+  public KotlinCompilerTool kotlinc(
+      CfRuntime jdk, KotlinCompiler kotlinCompiler, KotlinTargetVersion kotlinTargetVersion) {
+    return KotlinCompilerTool.create(jdk, temp, kotlinCompiler, kotlinTargetVersion);
   }
 
   public static ClassFileTransformer transformer(Class<?> clazz) throws IOException {
@@ -1365,4 +1360,22 @@
   public JarBuilder jarBuilder() throws IOException {
     return JarBuilder.builder(temp);
   }
+
+  public Collection<Path> buildOnDexRuntime(TestParameters parameters, Collection<Path> paths)
+      throws CompilationFailedException, IOException {
+    if (parameters.isCfRuntime()) {
+      return paths;
+    }
+    return Collections.singletonList(
+        testForD8()
+            .addProgramFiles(paths)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .writeToZip());
+  }
+
+  public Collection<Path> buildOnDexRuntime(TestParameters parameters, Path... paths)
+      throws IOException, CompilationFailedException {
+    return buildOnDexRuntime(parameters, Arrays.asList(paths));
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 5d8e69b..af1b81e 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.debug.DebugTestConfig;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase.KeepRuleConsumer;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 64c32a0..c0a5ee0 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AssemblyWriter;
 import com.android.tools.r8.graph.DexApplication;
@@ -73,6 +74,7 @@
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import java.util.zip.CRC32;
 import org.junit.Assert;
 import org.junit.Assume;
@@ -165,9 +167,10 @@
       Paths.get(LIBS_DIR, "r8lib-exclude-deps.jar.map");
   public static final Path DEPS_NOT_RELOCATED = Paths.get(LIBS_DIR, "deps-not-relocated.jar");
 
+  public static final Path DESUGAR_LIB_CONVERSIONS =
+      Paths.get(LIBS_DIR, "library_desugar_conversions.zip");
   public static final Path DESUGAR_LIB_JSON_FOR_TESTING =
-      Paths.get(
-          "src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs.json");
+      Paths.get("src/library_desugar/desugar_jdk_libs.json");
 
   public static boolean isLocalDevelopment() {
     return System.getProperty("local_development", "0").equals("1");
@@ -2065,6 +2068,17 @@
     public String getFolderName() {
       return folderName;
     }
+
+    public String getJvmTargetString() {
+      switch (this) {
+        case JAVA_6:
+          return "1.6";
+        case JAVA_8:
+          return "1.8";
+        default:
+          throw new Unimplemented("JvmTarget not specified for " + this);
+      }
+    }
   }
 
   public static void disassemble(AndroidApp app, PrintStream ps)
@@ -2073,4 +2087,20 @@
         new ApplicationReader(app, new InternalOptions(), new Timing()).read().toDirect();
     new AssemblyWriter(application, new InternalOptions(), true, false).write(ps);
   }
+
+  public static Path getTestFolderForClass(Class<?> clazz) {
+    return Paths.get(ToolHelper.TESTS_DIR)
+        .resolve("java")
+        .resolve(ToolHelper.getFileNameForTestClass(clazz))
+        .getParent();
+  }
+
+  public static Collection<Path> getFilesInTestFolderRelativeToClass(
+      Class<?> clazz, String folderName, String endsWith) throws IOException {
+    Path subFolder = getTestFolderForClass(clazz).resolve(folderName);
+    assert Files.isDirectory(subFolder);
+    try (Stream<Path> walker = Files.walk(subFolder)) {
+      return walker.filter(path -> path.toString().endsWith(endsWith)).collect(Collectors.toList());
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
index cf8f3f4..f659d1c 100644
--- a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
@@ -41,7 +41,7 @@
         .noMinification()
         .noTreeShaking()
         .enableInliningAnnotations()
-        .addOptionsModification(o -> o.testing.disallowLoadStoreOptimization = true)
+        .addOptionsModification(o -> o.enableLoadStoreOptimization = false)
         .run(TryRangeTest.class)
         .assertSuccessWithOutput(StringUtils.lines("10", "7.0"));
   }
@@ -58,7 +58,7 @@
             .enableInliningAnnotations()
             .addOptionsModification(
                 o -> {
-                  o.testing.disallowLoadStoreOptimization = true;
+                  o.enableLoadStoreOptimization = false;
                   o.testing.irModifier = this::processIR;
                 })
             .run(TryRangeTestLimitRange.class)
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
index c7a6bc0..a1e32b4 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
@@ -3,12 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.bootstrap;
 
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.internal.CompilationTestBase;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -50,7 +53,7 @@
   public void testForRuntime() throws Exception {
     // Compile Hello.kt and make sure it works as expected.
     Path classPathBefore =
-        kotlinc(parameters.getRuntime().asCf())
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
             .addSourceFiles(HELLO_KT)
             .setOutputPath(temp.newFolder().toPath())
             .compile();
@@ -63,7 +66,6 @@
 
   @Ignore(
       "b/136457753: assertion error in static class merger; "
-          + "b/144861881: force-inlining of non-inlineable constructors in vertical class merger; "
           + "b/144877828: assertion error in method naming state during interface method renaming; "
           + "b/144859533: umbrella"
   )
@@ -101,8 +103,6 @@
               o.ignoreMissingClasses = true;
               // b/144861100: invoke-static on interface is allowed up to JDK 8.
               o.testing.allowInvokeErrors = true;
-              // TODO(b/144861881): force-inlining of non-inlineable constructors.
-              o.enableVerticalClassMerging = false;
             })
             .compile()
             .writeToZip();
@@ -117,7 +117,10 @@
     // TODO(b/144859533): passing `dir` as -kotlin-home.
     // Compile Hello.kt again with r8-processed kotlin-compiler.jar
     Path classPathAfter =
-        kotlinc(parameters.getRuntime().asCf(), r8ProcessedKotlinc)
+        kotlinc(
+                parameters.getRuntime().asCf(),
+                new KotlinCompiler("r8ProcessedKotlinc", r8ProcessedKotlinc),
+                KotlinTargetVersion.JAVA_8)
             .addSourceFiles(HELLO_KT)
             .setOutputPath(temp.newFolder().toPath())
             .compile();
diff --git a/src/test/java/com/android/tools/r8/classmerging/ForceInlineConstructorWithMultiPackageAccessesTest.java b/src/test/java/com/android/tools/r8/classmerging/ForceInlineConstructorWithMultiPackageAccessesTest.java
new file mode 100644
index 0000000..06344db
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/ForceInlineConstructorWithMultiPackageAccessesTest.java
@@ -0,0 +1,75 @@
+// 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.classmerging;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.classmerging.testclasses.ForceInlineConstructorWithMultiPackageAccessesTestClasses;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ForceInlineConstructorWithMultiPackageAccessesTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ForceInlineConstructorWithMultiPackageAccessesTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(ForceInlineConstructorWithMultiPackageAccessesTest.class)
+        .addInnerClasses(ForceInlineConstructorWithMultiPackageAccessesTestClasses.class)
+        .addKeepMainRule(TestClass.class)
+        .enableMergeAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    assertThat(inspector.clazz(B.class), not(isPresent()));
+    // Check that C is present to ensure that B is not only removed as a result of the entire
+    // object allocation being removed.
+    assertThat(inspector.clazz(C.class), isPresent());
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println(new C());
+    }
+  }
+
+  static class B extends ForceInlineConstructorWithMultiPackageAccessesTestClasses.A {
+
+    // B.<init>() invokes protected A.<init>() and accesses package-private field `greeting`.
+    String greeting = System.currentTimeMillis() >= 0 ? "Hello world!" : null;
+  }
+
+  static class C extends B {
+
+    @Override
+    public String toString() {
+      return greeting;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/testclasses/ForceInlineConstructorWithMultiPackageAccessesTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/testclasses/ForceInlineConstructorWithMultiPackageAccessesTestClasses.java
new file mode 100644
index 0000000..80ab1d6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/testclasses/ForceInlineConstructorWithMultiPackageAccessesTestClasses.java
@@ -0,0 +1,16 @@
+// 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.classmerging.testclasses;
+
+import com.android.tools.r8.NeverMerge;
+
+public class ForceInlineConstructorWithMultiPackageAccessesTestClasses {
+
+  @NeverMerge
+  public static class A {
+
+    protected A() {}
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
new file mode 100644
index 0000000..4ec0131
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
@@ -0,0 +1,216 @@
+// Copyright (c) 2017, 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.compatdx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class CompatDxTests {
+  private static final int MAX_METHOD_COUNT = Constants.U16BIT_MAX;
+
+  private static final String EXAMPLE_JAR_FILE1 = ToolHelper.EXAMPLES_BUILD_DIR + "arithmetic.jar";
+  private static final String EXAMPLE_JAR_FILE2 = ToolHelper.EXAMPLES_BUILD_DIR + "barray.jar";
+
+  private static final String NO_LOCALS = "--no-locals";
+  private static final String NO_POSITIONS = "--positions=none";
+  private static final String MULTIDEX = "--multi-dex";
+  private static final String NUM_THREADS_5 = "--num-threads=5";
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Test
+  public void noFilesTest() throws IOException {
+    runDexer("--no-files");
+  }
+
+  @Test
+  public void noOutputTest() throws IOException {
+    runDexerWithoutOutput(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void singleJarInputFile() throws IOException {
+    runDexer(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void multipleJarInputFiles() throws IOException {
+    runDexer(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1, EXAMPLE_JAR_FILE2);
+  }
+
+  @Test
+  public void outputZipFile() throws IOException {
+    runDexerWithOutput("foo.dex.zip", NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void useMultipleThreads() throws IOException {
+    runDexer(NUM_THREADS_5, NO_POSITIONS, NO_LOCALS, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void withPositions() throws IOException {
+    runDexer(NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void withLocals() throws IOException {
+    runDexer(NO_POSITIONS, MULTIDEX, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void withoutMultidex() throws IOException {
+    runDexer(NO_POSITIONS, NO_LOCALS, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void writeToNamedDexFile() throws IOException {
+    runDexerWithOutput("named-output.dex", EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void keepClassesSingleDexTest() throws IOException {
+    runDexerWithOutput("out.zip", "--keep-classes", EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void keepClassesMultiDexTest() throws IOException {
+    runDexerWithOutput("out.zip", "--keep-classes", "--multi-dex", EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void ignoreDexInArchiveTest() throws IOException {
+    // Create a JAR with both a .class and a .dex file (the .dex file is just empty).
+    Path jarWithClassesAndDex = temp.newFile("test.jar").toPath();
+    Files.copy(Paths.get(EXAMPLE_JAR_FILE1), jarWithClassesAndDex,
+        StandardCopyOption.REPLACE_EXISTING);
+    URI uri = URI.create("jar:" + jarWithClassesAndDex.toUri());
+    FileSystem fileSystem = FileSystems.newFileSystem(uri, ImmutableMap.of("create", "true"));
+    Path dexFile = fileSystem.getPath("classes.dex");
+    Files.newOutputStream(dexFile, StandardOpenOption.CREATE).close();
+    fileSystem.close();
+
+    // Only test this with CompatDx, as dx does not like the empty .dex file.
+    List<String> d8Args =ImmutableList.of(
+        "--output=" + temp.newFolder("out").toString(), jarWithClassesAndDex.toString());
+    CompatDx.main(d8Args.toArray(StringUtils.EMPTY_ARRAY));
+  }
+
+  private void runDexer(String... args) throws IOException {
+    runDexerWithOutput("", args);
+  }
+
+  private void runDexerWithoutOutput(String... args) throws IOException {
+    runDexerWithOutput(null, args);
+  }
+
+  private Path getOutputD8() {
+    return temp.getRoot().toPath().resolve("d8-out");
+  }
+
+  private Path getOutputDX() {
+    return temp.getRoot().toPath().resolve("dx-out");
+  }
+
+  private void runDexerWithOutput(String out, String... args) throws IOException {
+    Path d8Out = null;
+    Path dxOut = null;
+    if (out != null) {
+      Path baseD8 = getOutputD8();
+      Path baseDX = getOutputDX();
+      Files.createDirectory(baseD8);
+      Files.createDirectory(baseDX);
+      d8Out = baseD8.resolve(out);
+      dxOut = baseDX.resolve(out);
+      assertNotEquals(d8Out, dxOut);
+    }
+
+    List<String> d8Args = new ArrayList<>(args.length + 2);
+    d8Args.add("--dex");
+    if (d8Out != null) {
+      d8Args.add("--output=" + d8Out);
+    }
+    Collections.addAll(d8Args, args);
+    System.out.println("running: d8 " + StringUtils.join(d8Args, " "));
+    CompatDx.main(d8Args.toArray(StringUtils.EMPTY_ARRAY));
+
+    List<String> dxArgs = new ArrayList<>(args.length + 2);
+    if (dxOut != null) {
+      dxArgs.add("--output=" + dxOut);
+    }
+    Collections.addAll(dxArgs, args);
+    System.out.println("running: dx " + StringUtils.join(dxArgs, " "));
+    ProcessResult result = ToolHelper.runDX(dxArgs.toArray(StringUtils.EMPTY_ARRAY));
+    assertEquals(result.stderr, 0, result.exitCode);
+
+    if (out == null) {
+      // Can't check output if explicitly not writing any.
+      return;
+    }
+
+    List<Path> d8Files = Files.list(Files.isDirectory(d8Out) ? d8Out : d8Out.getParent())
+        .sorted().collect(Collectors.toList());
+    List<Path> dxFiles = Files.list(Files.isDirectory(dxOut) ? dxOut : dxOut.getParent())
+        .sorted().collect(Collectors.toList());
+    assertEquals("Out file names differ",
+        StringUtils.join(dxFiles, "\n", BraceType.NONE, (file) ->
+            file.getFileName().toString()),
+        StringUtils.join(d8Files, "\n", BraceType.NONE, (file) ->
+            file.getFileName().toString()));
+
+    for (int i = 0; i < d8Files.size(); i++) {
+      if (FileUtils.isArchive(d8Files.get(i))) {
+        compareArchiveFiles(d8Files.get(i), dxFiles.get(i));
+      }
+    }
+  }
+
+  private void compareArchiveFiles(Path d8File, Path dxFile) throws IOException {
+    ZipFile d8Zip = new ZipFile(d8File.toFile(), StandardCharsets.UTF_8);
+    ZipFile dxZip = new ZipFile(dxFile.toFile(), StandardCharsets.UTF_8);
+    // TODO(zerny): This should test resource containment too once supported.
+    Set<String> d8Content = d8Zip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
+    Set<String> dxContent = dxZip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
+    for (String entry : d8Content) {
+      assertTrue("Expected dx output to contain " + entry, dxContent.contains(entry));
+    }
+    for (String entry : dxContent) {
+      Path path = Paths.get(entry);
+      if (FileUtils.isDexFile(path) || FileUtils.isClassFile(path)) {
+        assertTrue("Expected d8 output to contain " + entry, d8Content.contains(entry));
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
index 5b5b420..eb22ea9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
@@ -17,7 +17,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class CustomCollectionSuperCallsTest extends CoreLibDesugarTestBase {
+public class CustomCollectionSuperCallsTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
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 ae8012a..6085be7 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
@@ -9,7 +9,6 @@
 import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.conversiontests.APIConversionTestBase;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -32,7 +31,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class CustomCollectionTest extends APIConversionTestBase {
+public class CustomCollectionTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
@@ -65,7 +64,7 @@
                   this.assertCustomCollectionCallsCorrect(inspector, false);
                 })
             .addDesugaredCoreLibraryRunClassPath(
-                this::buildDesugaredLibraryWithConversionExtension,
+                this::buildDesugaredLibrary,
                 parameters.getApiLevel(),
                 keepRuleConsumer.get(),
                 shrinkDesugaredLibrary)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibrary2Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibrary2Test.java
new file mode 100644
index 0000000..220147f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibrary2Test.java
@@ -0,0 +1,159 @@
+// 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * This test checks that if a default interface method in a library interface conflicts with a
+ * default method in a program interface, then deriving both leads to a ICCE.
+ *
+ * <p>In contrast to DefaultMethodOverrideConflictWithLibraryTest, in this test, the conflict is in
+ * a class with the two conflicting interfaces as the immediate interfaces.
+ */
+@RunWith(Parameterized.class)
+public class DefaultMethodOverrideConflictWithLibrary2Test extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public DefaultMethodOverrideConflictWithLibrary2Test(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private List<Class<?>> getClasses() {
+    return ImmutableList.of(
+        Main.class,
+        MyIntegerList.class,
+        MyIntegerArrayListWithOverride.class,
+        MyHasCharacteristics.class);
+  }
+
+  private List<byte[]> getTransforms() throws IOException {
+    return ImmutableList.of(
+        transformer(MySpliterator.class)
+            .setImplements(Spliterator.class, MyHasCharacteristics.class)
+            .transform());
+  }
+
+  @Test
+  public void test() throws Exception {
+    TestRuntime systemRuntime = TestRuntime.getSystemRuntime();
+    if (systemRuntime.isCf() && systemRuntime.asCf().isNewerThanOrEqual(CfVm.JDK8)) {
+      // This test assumes that the library defines default method Spliterator::hasCharacteristics.
+      Method method = Spliterator.class.getDeclaredMethod("hasCharacteristics", int.class);
+      assertNotNull(method);
+      assertTrue(method.isDefault());
+    }
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addProgramClasses(getClasses())
+          .addProgramClassFileData(getTransforms())
+          .run(parameters.getRuntime(), Main.class)
+          .apply(this::checkResult);
+    } else {
+      testForD8()
+          .addProgramClasses(getClasses())
+          .addProgramClassFileData(getTransforms())
+          .setMinApi(parameters.getApiLevel())
+          .enableCoreLibraryDesugaring(parameters.getApiLevel())
+          .compile()
+          .disassemble()
+          .addDesugaredCoreLibraryRunClassPath(
+              this::buildDesugaredLibrary, parameters.getApiLevel())
+          .run(parameters.getRuntime(), Main.class)
+          .apply(this::checkResult);
+    }
+  }
+
+  private void checkResult(TestRunResult<?> result) {
+    if (parameters.isCfRuntime() && parameters.getRuntime().asCf().getVm().equals(CfVm.JDK11)) {
+      // TODO(b/145566657): For some reason JDK11 throws AbstractMethodError.
+      result.assertFailureWithErrorThatMatches(containsString(AbstractMethodError.class.getName()));
+    } else {
+      result.assertFailureWithErrorThatMatches(
+          containsString(IncompatibleClassChangeError.class.getName()));
+    }
+  }
+
+  // Interface that will lead to a conflicting default method with Spliterator::hasCharacteristics.
+  interface MyHasCharacteristics {
+
+    default boolean hasCharacteristics(int characteristics) {
+      System.out.println("MyHasCharacteristics::hasCharacteristics");
+      return false;
+    }
+  }
+
+  // Class deriving the default methods. The resolution of which will throw ICCE.
+  static class MySpliterator implements Spliterator<Integer> /*, MyHasCharacteristics via ASM */ {
+
+    @Override
+    public boolean tryAdvance(Consumer<? super Integer> action) {
+      return false;
+    }
+
+    @Override
+    public Spliterator<Integer> trySplit() {
+      return null;
+    }
+
+    @Override
+    public long estimateSize() {
+      return 0;
+    }
+
+    @Override
+    public int characteristics() {
+      return 0;
+    }
+  }
+
+  // Custom list interface with a default method for spliterator.
+  interface MyIntegerList extends List<Integer> {
+
+    @Override
+    default Spliterator<Integer> spliterator() {
+      return new MySpliterator();
+    }
+  }
+
+  // Derived list with override of spliterator that will explicitly call the custom default method.
+  static class MyIntegerArrayListWithOverride extends ArrayList<Integer> implements MyIntegerList {
+
+    @Override
+    public Spliterator<Integer> spliterator() {
+      return MyIntegerList.super.spliterator();
+    }
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(new MyIntegerArrayListWithOverride().spliterator().hasCharacteristics(0));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibraryTest.java
new file mode 100644
index 0000000..d244909
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibraryTest.java
@@ -0,0 +1,117 @@
+// 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Predicate;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * This test checks that if a default interface method in a library is not overridden by a class
+ * method in the library, then multiple maximally specific method lead to ICCE.
+ *
+ * <p>Concretely, Collection defines a default removeIf() method which is not overridden in either
+ * the List interface or the LinkedList class. Thus, a class that has an unrelated default method
+ * for removeIf will cause a conflict throwing ICCE.
+ */
+@RunWith(Parameterized.class)
+public class DefaultMethodOverrideConflictWithLibraryTest extends DesugaredLibraryTestBase {
+
+  private static List<Class<?>> CLASSES = ImmutableList.of(Main.class, MyRemoveIf.class);
+
+  private static List<byte[]> getTransforms() throws IOException {
+    return ImmutableList.of(
+        transformer(ConflictingClass.class).setImplements(MyRemoveIf.class).transform());
+  }
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public DefaultMethodOverrideConflictWithLibraryTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    TestRuntime systemRuntime = TestRuntime.getSystemRuntime();
+    if (systemRuntime.isCf() && systemRuntime.asCf().isNewerThanOrEqual(CfVm.JDK8)) {
+      // This test assumes that the library defines a Collection class with a default removeIf.
+      Method removeIf = Collection.class.getDeclaredMethod("removeIf", Predicate.class);
+      assertNotNull(removeIf);
+      assertTrue(removeIf.isDefault());
+      // Also, the LinkedList implementation does *not* define an override of removeIf.
+      try {
+        LinkedList.class.getDeclaredMethod("removeIf", Predicate.class);
+        fail("Unexpected defintion of removeIf on LinkedList");
+      } catch (NoSuchMethodException e) {
+        // Expected.
+      }
+    }
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addProgramClasses(CLASSES)
+          .addProgramClassFileData(getTransforms())
+          .run(parameters.getRuntime(), Main.class)
+          .assertFailureWithErrorThatMatches(getExpectedError());
+    } else {
+      testForD8()
+          .setMinApi(parameters.getApiLevel())
+          .addProgramClasses(CLASSES)
+          .addProgramClassFileData(getTransforms())
+          .enableCoreLibraryDesugaring(parameters.getApiLevel())
+          .compile()
+          .addDesugaredCoreLibraryRunClassPath(
+              this::buildDesugaredLibrary, parameters.getApiLevel())
+          .run(parameters.getRuntime(), Main.class)
+          .assertFailureWithErrorThatMatches(getExpectedError());
+    }
+  }
+
+  private Matcher<String> getExpectedError() {
+    return containsString(IncompatibleClassChangeError.class.getName());
+  }
+
+  // Interface with a default method that can lead to a conflict.
+  interface MyRemoveIf {
+
+    default boolean removeIf(Predicate<? super Integer> filter) {
+      System.out.println("MyRemoveIf::removeIf");
+      return false;
+    }
+  }
+
+  // Derived list with no override of removeIf but with a default method in MyRemoveIf.
+  // The two maximally specific methods Collection::removeIf and MyRemoveIf must cause ICCE.
+  static class ConflictingClass extends LinkedList<Integer> /* implements MyRemoveIf via ASM */ {
+    // Intentionally empty.
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(new ConflictingClass().removeIf(e -> false));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java
new file mode 100644
index 0000000..7716c96
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java
@@ -0,0 +1,161 @@
+// 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * This test checks that if a default interface method in a library is overridden by a class method
+ * also in the library, then that class method remains the target of resolution and dispatch.
+ *
+ * <p>Concretely, List (and Collection) define a default spliterator() method which is overridden in
+ * the ArrayList class. Thus, any class deriving ArrayList for which spliterator is not overridden
+ * should end up targeting that of ArrayList and not other potential non-library default interface
+ * methods.
+ */
+@RunWith(Parameterized.class)
+public class DefaultMethodOverrideInLibraryTest extends DesugaredLibraryTestBase {
+
+  static final String EXPECTED = StringUtils.lines("0", "42");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public DefaultMethodOverrideInLibraryTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    TestRuntime systemRuntime = TestRuntime.getSystemRuntime();
+    if (systemRuntime.isCf() && systemRuntime.asCf().isNewerThanOrEqual(CfVm.JDK8)) {
+      // This test assumes that the library defines an ArrayList class with a declared spliterator.
+      // For that reason, resolution will find that definition prior to a default interface method.
+      Method spliterator = ArrayList.class.getDeclaredMethod("spliterator");
+      assertNotNull(spliterator);
+      assertFalse(spliterator.isDefault());
+    }
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addInnerClasses(DefaultMethodOverrideInLibraryTest.class)
+          .run(parameters.getRuntime(), Main.class)
+          .assertSuccessWithOutput(EXPECTED);
+    } else {
+      testForD8()
+          .setMinApi(parameters.getApiLevel())
+          .addInnerClasses(DefaultMethodOverrideInLibraryTest.class)
+          .enableCoreLibraryDesugaring(parameters.getApiLevel())
+          .compile()
+          .addDesugaredCoreLibraryRunClassPath(
+              this::buildDesugaredLibrary, parameters.getApiLevel())
+          .run(parameters.getRuntime(), Main.class)
+          .apply(this::checkResult);
+    }
+  }
+
+  private void checkResult(D8TestRunResult result) {
+    // TODO(b/145506767): Default method desugaring fails to generate a library forwarding method.
+    if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+      result.assertFailureWithErrorThatMatches(
+          containsString(
+              parameters
+                      .getRuntime()
+                      .asDex()
+                      .getVm()
+                      .getVersion()
+                      .isOlderThanOrEqual(Version.V4_4_4)
+                  ? "VerifyError"
+                  : AbstractMethodError.class.getName()));
+      return;
+    }
+    // TODO(b/145504401): Execution on Art 7.0.0 has the wrong runtime behavior.
+    if (parameters.isDexRuntime()
+        && parameters.getRuntime().asDex().getVm().getVersion().equals(Version.V7_0_0)) {
+      result.assertSuccessWithOutputLines("42", "42");
+      return;
+    }
+    result.assertSuccessWithOutput(EXPECTED);
+  }
+
+  // Custom spliterator, just returns 42 in estimateSize, otherwise unused.
+  static class MySpliterator implements Spliterator<Integer> {
+
+    @Override
+    public boolean tryAdvance(Consumer<? super Integer> action) {
+      return false;
+    }
+
+    @Override
+    public Spliterator<Integer> trySplit() {
+      return null;
+    }
+
+    @Override
+    public long estimateSize() {
+      return 42; // Overridden to differ from the default.
+    }
+
+    @Override
+    public int characteristics() {
+      return 0;
+    }
+  }
+
+  // Custom list interface with a default method for spliterator.
+  interface MyIntegerList extends List<Integer> {
+
+    @Override
+    default Spliterator<Integer> spliterator() {
+      return new MySpliterator();
+    }
+  }
+
+  // Derived list with no override of spliterator. The call will thus go to the super class, not
+  // the default method!
+  static class MyIntegerArrayListWithoutOverride extends ArrayList<Integer>
+      implements MyIntegerList {
+    // No override of spliterator.
+  }
+
+  // Derived list with an override of spliterator. The call must hit the classes override and that
+  // will explictly call the custom default method.
+  static class MyIntegerArrayListWithOverride extends ArrayList<Integer> implements MyIntegerList {
+
+    @Override
+    public Spliterator<Integer> spliterator() {
+      return MyIntegerList.super.spliterator();
+    }
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(new MyIntegerArrayListWithoutOverride().spliterator().estimateSize());
+      System.out.println(new MyIntegerArrayListWithOverride().spliterator().estimateSize());
+    }
+  }
+}
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 df86791..159aa8d 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
@@ -16,8 +16,6 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.nio.file.Path;
@@ -28,7 +26,7 @@
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class DesugaredLibraryContentTest extends CoreLibDesugarTestBase {
+public class DesugaredLibraryContentTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
@@ -79,18 +77,7 @@
   }
 
   private void assertCorrect(CodeInspector inspector) {
-    inspector
-        .allClasses()
-        .forEach(
-            clazz ->
-                assertTrue(
-                    clazz.getOriginalName().startsWith("j$.")
-                        || clazz
-                            .getOriginalName()
-                            .startsWith(DesugaredLibraryWrapperSynthesizer.WRAPPER_PREFIX)
-                        || clazz
-                            .getOriginalName()
-                            .contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX)));
+    inspector.allClasses().forEach(clazz -> assertTrue(clazz.getOriginalName().startsWith("j$.")));
     assertThat(inspector.clazz("j$.time.Clock"), isPresent());
     // Above N the following classes are removed instead of being desugared.
     if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CoreLibDesugarTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
similarity index 90%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CoreLibDesugarTestBase.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index e176fca..17b2a61 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CoreLibDesugarTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -24,10 +24,11 @@
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-public class CoreLibDesugarTestBase extends TestBase {
+public class DesugaredLibraryTestBase extends TestBase {
 
   protected boolean requiresEmulatedInterfaceCoreLibDesugaring(TestParameters parameters) {
     return parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel();
@@ -37,37 +38,39 @@
     return parameters.getApiLevel().getLevel() < AndroidApiLevel.O.getLevel();
   }
 
-  protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel) throws RuntimeException {
+  protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel) {
     return buildDesugaredLibrary(apiLevel, "", false);
   }
 
-  protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules)
-      throws RuntimeException {
+  protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules) {
     return buildDesugaredLibrary(apiLevel, keepRules, true);
   }
 
-  protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules, boolean shrink)
-      throws RuntimeException {
+  protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules, boolean shrink) {
     return buildDesugaredLibrary(apiLevel, keepRules, shrink, ImmutableList.of());
   }
 
   protected Path buildDesugaredLibrary(
-      AndroidApiLevel apiLevel, String keepRules, boolean shrink, List<Path> additionalProgramFiles)
-      throws RuntimeException {
+      AndroidApiLevel apiLevel,
+      String keepRules,
+      boolean shrink,
+      List<Path> additionalProgramFiles) {
     // We wrap exceptions in a RuntimeException to call this from a lambda.
     try {
       // If we compile extended library here, it means we use TestNG.
       // TestNG requires annotations, hence we disable AnnotationRemoval.
       // This implies that extra warning are generated if this is set.
       boolean disableL8AnnotationRemovalForTesting = !additionalProgramFiles.isEmpty();
+      ArrayList<Path> extraPaths = new ArrayList<>(additionalProgramFiles);
       TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
       Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs_dex.zip");
       L8Command.Builder l8Builder =
           L8Command.builder(diagnosticsHandler)
               .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
               .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+              .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
               .setMode(shrink ? CompilationMode.RELEASE : CompilationMode.DEBUG)
-              .addProgramFiles(additionalProgramFiles)
+              .addProgramFiles(extraPaths)
               .addDesugaredLibraryConfiguration(
                   StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
               .setMinApiLevel(apiLevel.getLevel())
@@ -95,6 +98,10 @@
       }
       return desugaredLib;
     } catch (Exception e) {
+      // Don't wrap assumption violation so junit can catch it.
+      if (e instanceof RuntimeException) {
+        throw ((RuntimeException) e);
+      }
       throw new RuntimeException(e);
     }
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java
index 5cc9006..3cf6d11 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java
@@ -19,7 +19,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class DisableDesugarTest extends CoreLibDesugarTestBase {
+public class DisableDesugarTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DoubleUtilityClassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DoubleUtilityClassTest.java
index 6a59664..3b12474 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DoubleUtilityClassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DoubleUtilityClassTest.java
@@ -19,7 +19,7 @@
 
 // In this test both the desugared library and the program have the same utility class.
 @RunWith(Parameterized.class)
-public class DoubleUtilityClassTest extends CoreLibDesugarTestBase {
+public class DoubleUtilityClassTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
index 80bdb4f..3abe853 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
@@ -22,7 +22,7 @@
 import java.util.zip.ZipFile;
 import org.junit.Test;
 
-public class EmptyDesugaredLibrary extends CoreLibDesugarTestBase {
+public class EmptyDesugaredLibrary extends DesugaredLibraryTestBase {
 
   private L8Command.Builder prepareL8Builder(AndroidApiLevel minApiLevel) {
     return L8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java
index c2cc678..5851353 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java
@@ -29,19 +29,19 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class EmulatedInterfacesTest extends CoreLibDesugarTestBase {
+public class EmulatedInterfacesTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
-  private final boolean shrinkCoreLibrary;
+  private final boolean shrinkDesugaredLibrary;
 
-  @Parameters(name = "{1}, shrinkCoreLibrary: {0}")
+  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
   public static List<Object[]> data() {
     return buildParameters(
         BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
   }
 
   public EmulatedInterfacesTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
-    this.shrinkCoreLibrary = shrinkDesugaredLibrary;
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
 
@@ -51,7 +51,7 @@
     CodeInspector inspector =
         new CodeInspector(
             buildDesugaredLibrary(
-                parameters.getApiLevel(), "-keep class **$-EL", shrinkCoreLibrary));
+                parameters.getApiLevel(), "-keep class **$-EL", shrinkDesugaredLibrary));
     assertEmulateInterfaceClassesPresentWithDispatchMethods(inspector);
     assertCollectionMethodsPresentWithCorrectDispatch(inspector);
   }
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 5c295c9..732c0b9 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
@@ -23,7 +23,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class JavaTimeTest extends CoreLibDesugarTestBase {
+public class JavaTimeTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
index 78617c7..7ca5301 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
@@ -23,7 +23,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class JavaUtilFunctionTest extends CoreLibDesugarTestBase {
+public class JavaUtilFunctionTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilOptionalTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilOptionalTest.java
index 31a7413..c2f83eb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilOptionalTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilOptionalTest.java
@@ -29,7 +29,7 @@
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class JavaUtilOptionalTest extends CoreLibDesugarTestBase {
+public class JavaUtilOptionalTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
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 e9f221d..bbadc1d 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
@@ -15,7 +15,7 @@
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class LinkedHashSetTest extends CoreLibDesugarTestBase {
+public class LinkedHashSetTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
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 6303b04..2275a44 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
@@ -13,13 +13,13 @@
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.desugar.desugaredlibrary.desugaredlibraryjdktests.Jdk11CoreLibTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.nio.file.Path;
 import org.junit.Test;
 
-public class MergingJ$Test extends Jdk11CoreLibTestBase {
+public class MergingJ$Test extends Jdk11DesugaredLibraryTestBase {
 
   @Test
   public void testMergingJ$() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java
index a865ee3..e2a5d8d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java
@@ -21,7 +21,7 @@
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class NeverMergeCoreLibDesugarClasses extends CoreLibDesugarTestBase {
+public class NeverMergeCoreLibDesugarClasses extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
@@ -46,7 +46,6 @@
           .addInnerClasses(NeverMergeCoreLibDesugarClasses.class)
           .addProgramDexFileData(builder.compile())
           .setMinApi(parameters.getRuntime())
-          .addOptionsModification(options -> options.enableNeverMergePrefixes = true)
           .compileWithExpectedDiagnostics(diagnostics -> {
             diagnostics.assertErrorsCount(1);
             String message = diagnostics.getErrors().get(0).getDiagnosticMessage();
@@ -72,7 +71,6 @@
           .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
           .setMinApi(parameters.getRuntime())
           .addProgramFiles(buildDesugaredLibrary(parameters.getApiLevel()))
-          .addOptionsModification(options -> options.enableNeverMergePrefixes = true)
           .compileWithExpectedDiagnostics(diagnostics -> {
             diagnostics.assertErrorsCount(1);
             String message = diagnostics.getErrors().get(0).getDiagnosticMessage();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDefaultMethodOverrideInLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDefaultMethodOverrideInLibraryTest.java
new file mode 100644
index 0000000..d9f93b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDefaultMethodOverrideInLibraryTest.java
@@ -0,0 +1,118 @@
+// 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.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Predicate;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * This test checks that if a default interface method in a library is not overridden by a class
+ * method in the library, then a program defined maximally specific method becomes the target if
+ * present.
+ *
+ * <p>Concretely, Collection defines a default removeIf() method which is not overridden in either
+ * the List interface or the LinkedList class. Thus, any class deriving LinkedList for which
+ * removeIf is overridden or a new default method is added should target those extensions.
+ */
+@RunWith(Parameterized.class)
+public class NoDefaultMethodOverrideInLibraryTest extends DesugaredLibraryTestBase {
+
+  static final String EXPECTED = StringUtils.lines("MyIntegerList::removeIf", "false", "false");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public NoDefaultMethodOverrideInLibraryTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    TestRuntime systemRuntime = TestRuntime.getSystemRuntime();
+    if (systemRuntime.isCf() && systemRuntime.asCf().isNewerThanOrEqual(CfVm.JDK8)) {
+      // This test assumes that the library defines a Collection class with a default removeIf.
+      Method removeIf = Collection.class.getDeclaredMethod("removeIf", Predicate.class);
+      assertNotNull(removeIf);
+      assertTrue(removeIf.isDefault());
+      // Also, the LinkedList implementation does *not* define an override of removeIf.
+      try {
+        LinkedList.class.getDeclaredMethod("removeIf", Predicate.class);
+        fail("Unexpected defintion of removeIf on LinkedList");
+      } catch (NoSuchMethodException e) {
+        // Expected.
+      }
+    }
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addInnerClasses(NoDefaultMethodOverrideInLibraryTest.class)
+          .run(parameters.getRuntime(), Main.class)
+          .assertSuccessWithOutput(EXPECTED);
+    } else {
+      testForD8()
+          .setMinApi(parameters.getApiLevel())
+          .addInnerClasses(NoDefaultMethodOverrideInLibraryTest.class)
+          .enableCoreLibraryDesugaring(parameters.getApiLevel())
+          .compile()
+          .addDesugaredCoreLibraryRunClassPath(
+              this::buildDesugaredLibrary, parameters.getApiLevel())
+          .run(parameters.getRuntime(), Main.class)
+          .assertSuccessWithOutput(EXPECTED);
+    }
+  }
+
+  // Custom list interface with a default method for removeIf.
+  interface MyIntegerList extends List<Integer> {
+
+    @Override
+    default boolean removeIf(Predicate<? super Integer> filter) {
+      System.out.println("MyIntegerList::removeIf");
+      return false;
+    }
+  }
+
+  // Derived list with no override of removeIf but with a default method in MyIntegerList.
+  // The call will thus go to the maximally specific method, which is MyIntegerList::removeIf.
+  static class MyIntegerLinkedListWithoutOverride extends LinkedList<Integer>
+      implements MyIntegerList {
+    // No override of spliterator.
+  }
+
+  // Derived list with an override of removeIf. The call must hit the classes override and that
+  // will explictly call the library default method (Collection.removeIf).
+  static class MyIntegerLinkedListWithOverride extends LinkedList<Integer>
+      implements MyIntegerList {
+
+    @Override
+    public boolean removeIf(Predicate<? super Integer> filter) {
+      return super.removeIf(filter);
+    }
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(new MyIntegerLinkedListWithoutOverride().removeIf(e -> false));
+      System.out.println(new MyIntegerLinkedListWithOverride().removeIf(e -> false));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
index 59db52d..69f366f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -34,7 +34,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class ProgramRewritingTest extends CoreLibDesugarTestBase {
+public class ProgramRewritingTest extends DesugaredLibraryTestBase {
 
   private static final String TEST_CLASS = "stream.ProgramRewritingTestClass";
 
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 b20a716..eebea28 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
@@ -18,7 +18,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class RetargetOverrideTest extends CoreLibDesugarTestBase {
+public class RetargetOverrideTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
index af7d1c8..725bf3f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
@@ -15,7 +15,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class StaticInterfaceMethodTest extends CoreLibDesugarTestBase {
+public class StaticInterfaceMethodTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java
index a8bf8ed..403ae3c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java
@@ -9,11 +9,12 @@
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.time.Year;
 import org.junit.Test;
 
-public class APIConversionFinalClassErrorTest extends APIConversionTestBase {
+public class APIConversionFinalClassErrorTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testFinalMethod() {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionLargeWarningTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionLargeWarningTest.java
index 6d7a9d5..fe0e1a5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionLargeWarningTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionLargeWarningTest.java
@@ -6,6 +6,7 @@
 
 import static org.hamcrest.CoreMatchers.startsWith;
 
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Path;
 import java.time.Clock;
@@ -13,7 +14,7 @@
 import java.util.stream.Stream;
 import org.junit.Test;
 
-public class APIConversionLargeWarningTest extends APIConversionTestBase {
+public class APIConversionLargeWarningTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testFinalMethod() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
index c45b403..4c2007f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.Arrays;
@@ -21,7 +22,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class APIConversionTest extends APIConversionTestBase {
+public class APIConversionTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
@@ -60,8 +61,7 @@
         .enableCoreLibraryDesugaring(parameters.getApiLevel())
         .compile()
         .assertOnlyInfos() // No warnings.
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, parameters.getApiLevel())
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
         .run(parameters.getRuntime(), Executor.class)
         .assertSuccessWithOutput(
             StringUtils.lines(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTestBase.java
deleted file mode 100644
index 29b316d..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTestBase.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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.conversiontests;
-
-import com.android.tools.r8.TestRuntime;
-import com.android.tools.r8.TestRuntime.CfRuntime;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import org.junit.Assume;
-
-public class APIConversionTestBase extends CoreLibDesugarTestBase {
-
-  private static final Path CONVERSION_FOLDER = Paths.get("src/test/desugaredLibrary");
-
-  public Path[] getConversionClasses() throws IOException {
-    Assume.assumeTrue(
-        "JDK8 javac is required to avoid dealing with modules and JDK8 is not checked-in on"
-            + " windows",
-        !ToolHelper.isWindows());
-
-    CfRuntime runtime = TestRuntime.getCheckedInJdk8();
-    Path conversionFolder = temp.newFolder("conversions").toPath();
-
-    // Compile the stubs to be able to compile the conversions.
-    Path stubsJar =
-        javac(runtime)
-            .addSourceFiles(
-                getAllFilesWithSuffixInDirectory(CONVERSION_FOLDER.resolve("stubs"), "java"))
-            .compile();
-
-    // Compile the conversions using the stubs.
-    javac(runtime)
-        .setOutputPath(conversionFolder)
-        .addClasspathFiles(stubsJar)
-        .addSourceFiles(
-            getAllFilesWithSuffixInDirectory(CONVERSION_FOLDER.resolve("conversions"), "java"))
-        .compile();
-
-    Path[] classes = getAllFilesWithSuffixInDirectory(conversionFolder, "class");
-    assert classes.length > 0
-        : "Something went wrong during compilation, check the runJavac return value for debugging.";
-    return classes;
-  }
-
-  protected Path buildDesugaredLibraryWithConversionExtension(AndroidApiLevel apiLevel) {
-    return buildDesugaredLibraryWithConversionExtension(apiLevel, "", false);
-  }
-
-  protected Path buildDesugaredLibraryWithConversionExtension(AndroidApiLevel apiLevel,String keepRules, boolean shrink) {
-    Path[] timeConversionClasses;
-    try {
-      timeConversionClasses = getConversionClasses();
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
-    ArrayList<Path> paths = new ArrayList<>();
-    Collections.addAll(paths, timeConversionClasses);
-    return buildDesugaredLibrary(apiLevel, keepRules, shrink, paths);
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
index 9eda3e2..ef30ad1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
@@ -15,7 +16,7 @@
 import java.util.OptionalLong;
 import org.junit.Test;
 
-public class AllOptionalConversionTest extends APIConversionTestBase {
+public class AllOptionalConversionTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testRewrittenAPICalls() throws Exception {
@@ -26,8 +27,7 @@
         .addLibraryClasses(CustomLibClass.class)
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
         .assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
index 1128076..db5c661 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
@@ -22,7 +23,7 @@
 import java.time.ZonedDateTime;
 import org.junit.Test;
 
-public class AllTimeConversionTest extends APIConversionTestBase {
+public class AllTimeConversionTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testRewrittenAPICalls() throws Exception {
@@ -36,8 +37,7 @@
             .enableCoreLibraryDesugaring(AndroidApiLevel.B)
             .compile();
     compileResult
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
         .assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
index f87454c..f31ad69 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
@@ -15,7 +16,7 @@
 // Longs and double take two stack indexes, this had to be dealt with in
 // CfAPIConverter*WrapperCodeProvider (See stackIndex vs index), this class tests that the
 // synthetic Cf code is correct.
-public class BasicLongDoubleConversionTest extends APIConversionTestBase {
+public class BasicLongDoubleConversionTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testRewrittenAPICalls() throws Exception {
@@ -26,8 +27,7 @@
         .addLibraryClasses(CustomLibClass.class)
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
         .assertSuccessWithOutput(StringUtils.lines("--01-16"));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
index 7c45daa..1e66f26 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -25,7 +26,7 @@
 import java.util.TimeZone;
 import org.junit.Test;
 
-public class BasicTimeConversionTest extends APIConversionTestBase {
+public class BasicTimeConversionTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testTimeGeneratedDex() throws Exception {
@@ -34,7 +35,7 @@
     L8Command.Builder l8Builder =
         L8Command.builder(diagnosticsHandler)
             .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
-            .addProgramFiles(getConversionClasses())
+            .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
             .addDesugaredLibraryConfiguration(
                 StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
             .setMinApiLevel(AndroidApiLevel.B.getLevel())
@@ -57,8 +58,7 @@
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
         .inspect(this::checkAPIRewritten)
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class);
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
index 2bdc252..38e8bac 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -18,7 +19,7 @@
 import java.util.function.Consumer;
 import org.junit.Test;
 
-public class CallBackConversionTest extends APIConversionTestBase {
+public class CallBackConversionTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testCallBack() throws Exception {
@@ -57,8 +58,7 @@
                                   .toString()
                                   .equals("java.util.function.Consumer")));
             })
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Impl.class)
         .assertSuccessWithOutput(StringUtils.lines("0", "1", "0", "1"));
@@ -76,8 +76,7 @@
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
         .inspect(this::assertLibraryOverridesThere)
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Impl.class)
         .assertSuccessWithOutput(StringUtils.lines("0", "1", "0", "1"));
@@ -94,8 +93,7 @@
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
         .inspect(this::assertLibraryOverridesThere)
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Impl.class)
         .assertSuccessWithOutput(StringUtils.lines("0", "1", "0", "1"));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java
index 17751e9..d4c7f53 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java
@@ -6,13 +6,14 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
 import java.time.Clock;
 import org.junit.Test;
 
-public class ClockAPIConversionTest extends APIConversionTestBase {
+public class ClockAPIConversionTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testClock() throws Exception {
@@ -23,8 +24,7 @@
         .addLibraryClasses(CustomLibClass.class)
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
         .assertSuccessWithOutput(StringUtils.lines("Z", "Z", "true"));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionErrorMessageTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionErrorMessageTest.java
index 66d1a72..bdd9f5d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionErrorMessageTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionErrorMessageTest.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.Arrays;
@@ -17,7 +18,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class ConversionErrorMessageTest extends APIConversionTestBase {
+public class ConversionErrorMessageTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
@@ -78,8 +79,7 @@
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel())
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, parameters.getApiLevel())
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
         .run(parameters.getRuntime(), Executor.class)
         .assertSuccessWithOutput(getExpectedResult());
   }
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 11dcdeb..b13ade6 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
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
@@ -15,7 +16,7 @@
 import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
 
-public class ConversionIntroduceInterfaceMethodTest extends APIConversionTestBase {
+public class ConversionIntroduceInterfaceMethodTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testNoInterfaceMethods() throws Exception {
@@ -30,8 +31,7 @@
         .addLibraryClasses(CustomLibClass.class)
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
         .assertSuccessWithOutput(
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 79cad2f..1f433d4 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
@@ -8,6 +8,7 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -18,7 +19,7 @@
 import java.util.function.BiConsumer;
 import org.junit.Test;
 
-public class DuplicateAPIDesugaredLibTest extends APIConversionTestBase {
+public class DuplicateAPIDesugaredLibTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testLib() throws Exception {
@@ -38,7 +39,7 @@
             .compile()
             .addDesugaredCoreLibraryRunClassPath(
                 (AndroidApiLevel api) -> {
-                  desugaredLibBox.set(this.buildDesugaredLibraryWithConversionExtension(api));
+                  desugaredLibBox.set(this.buildDesugaredLibrary(api));
                   return desugaredLibBox.get();
                 },
                 AndroidApiLevel.B)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
index 31ca3b5..2699bd3 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
@@ -8,6 +8,7 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.nio.file.Path;
@@ -16,7 +17,7 @@
 import java.util.function.BiConsumer;
 import org.junit.Test;
 
-public class DuplicateAPIProgramTest extends APIConversionTestBase {
+public class DuplicateAPIProgramTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testMap() throws Exception {
@@ -29,8 +30,7 @@
             .enableCoreLibraryDesugaring(AndroidApiLevel.B)
             .compile()
             .inspect(this::assertDupMethod)
-            .addDesugaredCoreLibraryRunClassPath(
-                this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+            .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
             .addRunClasspathFiles(customLib)
             .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
             .assertSuccess()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
index 61068b3..1dfbc73 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
@@ -8,6 +8,7 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
@@ -28,7 +29,7 @@
 import org.junit.Assert;
 import org.junit.Test;
 
-public class FunctionConversionTest extends APIConversionTestBase {
+public class FunctionConversionTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testFunctionComposition() throws Exception {
@@ -41,8 +42,7 @@
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
         .inspect(this::assertSingleWrappers)
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
         .assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/MoreFunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/MoreFunctionConversionTest.java
index 393ebca..eea96ae 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/MoreFunctionConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/MoreFunctionConversionTest.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -19,7 +20,7 @@
 import java.util.function.Function;
 import org.junit.Test;
 
-public class MoreFunctionConversionTest extends APIConversionTestBase {
+public class MoreFunctionConversionTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testFunction() throws Exception {
@@ -34,11 +35,10 @@
     Path program = compileResult.writeToZip();
     assertNoDuplicateLambdas(program, customLib);
     compileResult
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
-        .assertSuccessWithOutput(StringUtils.lines("6","6","6","6","6"));
+        .assertSuccessWithOutput(StringUtils.lines("6", "6", "6", "6", "6"));
   }
 
   // If we have the exact same lambda in both, but one implements j$..Function and the other
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
index 3e65742..bebefd1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
 import java.util.Arrays;
@@ -19,7 +20,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class SummaryStatisticsConversionTest extends APIConversionTestBase {
+public class SummaryStatisticsConversionTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
@@ -50,8 +51,7 @@
         .addLibraryClasses(CustomLibClass.class)
         .enableCoreLibraryDesugaring(parameters.getApiLevel())
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, parameters.getApiLevel())
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
         .addRunClasspathFiles(customLib)
         .run(parameters.getRuntime(), Executor.class)
         .assertSuccessWithOutput(
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 9ff6fa8..3589bb3 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
@@ -6,13 +6,14 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
 import java.time.ZoneId;
 import org.junit.Test;
 
-public class TryCatchTimeConversionTest extends APIConversionTestBase {
+public class TryCatchTimeConversionTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testBaseline() throws Exception {
@@ -23,8 +24,7 @@
         .addLibraryClasses(CustomLibClass.class)
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), BaselineExecutor.class)
         .assertSuccessWithOutput(StringUtils.lines("GMT", "GMT", "GMT", "GMT", "GMT"));
@@ -39,8 +39,7 @@
         .addLibraryClasses(CustomLibClass.class)
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), TryCatchExecutor.class)
         .assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
index 43ede3a..646d20f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
@@ -13,7 +14,7 @@
 import java.util.function.IntConsumer;
 import org.junit.Test;
 
-public class UnwrapConversionTest extends APIConversionTestBase {
+public class UnwrapConversionTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testUnwrap() throws Exception {
@@ -24,8 +25,7 @@
         .addLibraryClasses(CustomLibClass.class)
         .enableCoreLibraryDesugaring(AndroidApiLevel.B)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
         .assertSuccessWithOutput(StringUtils.lines("true", "true"));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java
index bb0c2b1..26f4629 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
@@ -18,7 +19,7 @@
 import java.util.function.IntSupplier;
 import org.junit.Test;
 
-public class WrapperMergeConflictTest extends APIConversionTestBase {
+public class WrapperMergeConflictTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testWrapperMergeConflict() throws Exception {
@@ -50,8 +51,7 @@
         .addProgramFiles(path1, path2)
         .addLibraryClasses(CustomLibClass.class)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .inspect(this::assertBigWrappersPresent)
         .addRunClasspathFiles(customLib)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor1.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java
index c93e820..4c1caf7 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java
@@ -8,6 +8,7 @@
 
 import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
@@ -16,7 +17,7 @@
 import java.util.Arrays;
 import org.junit.Test;
 
-public class WrapperMergeTest extends APIConversionTestBase {
+public class WrapperMergeTest extends DesugaredLibraryTestBase {
 
   @Test
   public void testWrapperMerge() throws Exception {
@@ -36,14 +37,11 @@
         .inspect(this::assertWrappers)
         .writeToZip();
     testForD8()
-        .addProgramFiles(path1,path2)
+        .addProgramFiles(path1, path2)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
         .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor1.class)
-        .assertSuccessWithOutput(
-            StringUtils.lines("[1, 2, 3]"));
-
+        .assertSuccessWithOutput(StringUtils.lines("[1, 2, 3]"));
   }
 
   private void assertWrappers(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11AtomicTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
similarity index 96%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11AtomicTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
index d2501e9..b161386 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11AtomicTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
@@ -2,7 +2,7 @@
 // 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
 
 import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
@@ -27,7 +27,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class Jdk11AtomicTests extends Jdk11CoreLibTestBase {
+public class Jdk11AtomicTests extends Jdk11DesugaredLibraryTestBase {
 
   private static final Path ATOMIC_TESTS_FOLDER =
       Paths.get(ToolHelper.JDK_11_TESTS_DIR + "java/util/concurrent/atomic/");
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ConcurrentMapTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
similarity index 98%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ConcurrentMapTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
index ff779f5..fa595cb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ConcurrentMapTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
@@ -2,7 +2,7 @@
 // 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
 
 import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
@@ -31,7 +31,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class Jdk11ConcurrentMapTests extends Jdk11CoreLibTestBase {
+public class Jdk11ConcurrentMapTests extends Jdk11DesugaredLibraryTestBase {
 
   private static final Path CONCURRENT_TESTS_FOLDER =
       Paths.get(ToolHelper.JDK_11_TESTS_DIR + "java/util/concurrent/ConcurrentMap/");
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11CoreLibTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11DesugaredLibraryTestBase.java
similarity index 94%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11CoreLibTestBase.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11DesugaredLibraryTestBase.java
index b3e6156..94c128e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11CoreLibTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11DesugaredLibraryTestBase.java
@@ -2,7 +2,7 @@
 // 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
 
 import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
@@ -10,7 +10,7 @@
 
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.io.File;
@@ -24,7 +24,7 @@
 // Provides convenience to use Paths/SafeVarargs which are missing on old Android but
 // required by some Jdk tests, and for java.base extensions.
 
-public class Jdk11CoreLibTestBase extends CoreLibDesugarTestBase {
+public class Jdk11DesugaredLibraryTestBase extends DesugaredLibraryTestBase {
 
   protected static Path[] JDK_11_JAVA_BASE_EXTENSION_COMPILED_FILES;
   protected static final Path JDK_11_JAVA_BASE_EXTENSION_CLASSES_DIR =
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11MathTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11MathTests.java
similarity index 98%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11MathTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11MathTests.java
index 69c3558..713061e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11MathTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11MathTests.java
@@ -2,7 +2,7 @@
 // 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
 
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ObjectsTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ObjectsTests.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ObjectsTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ObjectsTests.java
index c8a3e2d..c24839f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ObjectsTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ObjectsTests.java
@@ -2,7 +2,7 @@
 // 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
 
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
similarity index 98%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
index d885b0f..433f56a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
@@ -2,7 +2,7 @@
 // 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
 
 import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
@@ -37,7 +37,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class Jdk11StreamTests extends Jdk11CoreLibTestBase {
+public class Jdk11StreamTests extends Jdk11DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11TimeTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11TimeTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
index 58acdc7..736acc5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11TimeTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
@@ -2,7 +2,7 @@
 // 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
 
 import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
 import static org.junit.Assert.assertTrue;
@@ -21,7 +21,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class Jdk11TimeTests extends Jdk11CoreLibTestBase {
+public class Jdk11TimeTests extends Jdk11DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
index 819e6ce..91d0e98 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
@@ -20,7 +20,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.desugar.desugaredlibrary.conversiontests.APIConversionTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
@@ -33,7 +33,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class HelloWorldCompiledOnArtTest extends APIConversionTestBase {
+public class HelloWorldCompiledOnArtTest extends DesugaredLibraryTestBase {
 
   // TODO(b/142621961): Create an abstraction to easily run tests on External DexR8.
   // Manage pathMock in the abstraction.
@@ -136,8 +136,7 @@
             || diagnosticMessages.getWarnings().stream()
                 .noneMatch(x -> x.getDiagnosticMessage().contains("andThen")));
     return compile
-        .addDesugaredCoreLibraryRunClassPath(
-            this::buildDesugaredLibraryWithConversionExtension, parameters.getApiLevel())
+        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
         .withArt6Plus64BitsLib()
         .withArtFrameworks();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
index 2533619..02623a8 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
@@ -15,7 +15,7 @@
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import java.io.File;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -27,7 +27,7 @@
 
 // TODO(b/142621961): Parametrize at least L and P instead of just P.
 @RunWith(Parameterized.class)
-public class R8CompiledThroughDexTest extends CoreLibDesugarTestBase {
+public class R8CompiledThroughDexTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/EnumSetTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/EnumSetTest.java
index 6ee2045..454adc7 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/EnumSetTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/EnumSetTest.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.desugar.desugaredlibrary.shrinkingtests;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.time.DayOfWeek;
@@ -19,7 +19,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class EnumSetTest extends CoreLibDesugarTestBase {
+public class EnumSetTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java
index a0ad904..5211d3d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.desugar.desugaredlibrary.shrinkingtests;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.time.ZoneId;
@@ -16,7 +16,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class FieldAccessTest extends CoreLibDesugarTestBase {
+public class FieldAccessTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java
index 9d5f8e0..a6061c8 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.desugar.desugaredlibrary.shrinkingtests;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.time.Clock;
@@ -18,7 +18,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class InheritanceTest extends CoreLibDesugarTestBase {
+public class InheritanceTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java
index d289490..2a673bf 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.util.List;
 import java.util.Map;
@@ -19,7 +19,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class KeepRuleShrinkTest extends CoreLibDesugarTestBase {
+public class KeepRuleShrinkTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/AvoidInliningRecursiveMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/AvoidInliningRecursiveMethodTest.java
new file mode 100644
index 0000000..3a501a6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/AvoidInliningRecursiveMethodTest.java
@@ -0,0 +1,79 @@
+// 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.ir.optimize.inliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AvoidInliningRecursiveMethodTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public AvoidInliningRecursiveMethodTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(AvoidInliningRecursiveMethodTest.class)
+        .addKeepMainRule(TestClass.class)
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(TestClass.class);
+    assertThat(classSubject, isPresent());
+
+    MethodSubject mainMethodSubject = classSubject.mainMethod();
+    assertThat(mainMethodSubject, isPresent());
+
+    // TODO(b/145276800): Should not inline recursive methods.
+    assertTrue(mainMethodSubject.streamInstructions().anyMatch(InstructionSubject::isIf));
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println(recursive(getInt()));
+    }
+
+    @NeverInline
+    static int getInt() {
+      return System.currentTimeMillis() >= 0 ? 42 : 0;
+    }
+
+    static String recursive(int x) {
+      if (x > 0) {
+        return recursive(x - 1);
+      }
+      return "Hello world!";
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineNonReboundFieldTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineNonReboundFieldTest.java
index 39872ae..beb56a7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineNonReboundFieldTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineNonReboundFieldTest.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.optimize.inliner;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverClassInline;
@@ -39,9 +40,9 @@
     // since main() does not have access to the GreetingBase.greeting field.
     assertThat(greeterSubject.uniqueMethodWithName("greet"), isPresent());
 
-    // TODO(b/128967328): The method greetInternal() should be inlined into greet() since it has a
-    //  single call site and nothing prevents it from being inlined.
-    assertThat(greeterSubject.uniqueMethodWithName("greetInternal"), isPresent());
+    // The method greetInternal() should be inlined into greet() since it has a single call site and
+    // nothing prevents it from being inlined.
+    assertThat(greeterSubject.uniqueMethodWithName("greetInternal"), not(isPresent()));
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
new file mode 100644
index 0000000..58ad103
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
@@ -0,0 +1,141 @@
+// 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.ir.optimize.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InstanceFieldValuePropagationTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public InstanceFieldValuePropagationTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(InstanceFieldValuePropagationTest.class)
+        .addKeepMainRule(TestClass.class)
+        .addOptionsModification(options -> {
+          // TODO(b/125282093): Remove options modification once landed.
+          assert !options.enableValuePropagationForInstanceFields;
+          options.enableValuePropagationForInstanceFields = true;
+        })
+        .enableClassInliningAnnotations()
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(
+            StringUtils.times(StringUtils.lines("A", "42", "Hello world!"), 2));
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+    assertThat(testClassSubject, isPresent());
+
+    // Verify that all instance-get instructions in testDefinitelyNotNull() has been removed by
+    // member value propagation.
+    MethodSubject testDefinitelyNotNullMethodSubject =
+        testClassSubject.uniqueMethodWithName("testDefinitelyNotNull");
+    assertThat(testDefinitelyNotNullMethodSubject, isPresent());
+    assertTrue(
+        testDefinitelyNotNullMethodSubject
+            .streamInstructions()
+            .noneMatch(InstructionSubject::isInstanceGet));
+    // TODO(b/125282093): Should be able to remove the new-instance instruction since the instance
+    //  ends up being unused.
+    assertTrue(
+        testDefinitelyNotNullMethodSubject
+            .streamInstructions()
+            .anyMatch(InstructionSubject::isNewInstance));
+
+    // Verify that all instance-get instructions in testMaybeNull() has been removed by member value
+    // propagation.
+    MethodSubject testMaybeNullMethodSubject =
+        testClassSubject.uniqueMethodWithName("testMaybeNull");
+    assertThat(testMaybeNullMethodSubject, isPresent());
+    // TODO(b/125282093): Should synthesize a null-check and still propagate the field values even
+    //  when the receiver is nullable.
+    assertTrue(
+        testMaybeNullMethodSubject
+            .streamInstructions()
+            .anyMatch(InstructionSubject::isInstanceGet));
+    // TODO(b/125282093): Should be able to remove the new-instance instruction since the instance
+    //  ends up being unused.
+    assertTrue(
+        testMaybeNullMethodSubject
+            .streamInstructions()
+            .anyMatch(InstructionSubject::isNewInstance));
+
+    ClassSubject aClassSubject = inspector.clazz(A.class);
+    assertThat(aClassSubject, isPresent());
+    // TODO(b/125282093): Need to remove the instance-put instructions in A.<init>(). This can not
+    //  be done safely by the time we process A.<init>(), so some kind of post-processing is needed.
+    assertEquals(3, aClassSubject.allInstanceFields().size());
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      testDefinitelyNotNull();
+      testMaybeNull();
+    }
+
+    @NeverInline
+    static void testDefinitelyNotNull() {
+      A a = new A();
+      System.out.println(a.e);
+      System.out.println(a.i);
+      System.out.println(a.s);
+    }
+
+    @NeverInline
+    static void testMaybeNull() {
+      A a = System.currentTimeMillis() >= 0 ? new A() : null;
+      System.out.println(a.e);
+      System.out.println(a.i);
+      System.out.println(a.s);
+    }
+  }
+
+  @NeverClassInline
+  static class A {
+
+    MyEnum e = MyEnum.A;
+    int i = 42;
+    String s = "Hello world!";
+  }
+
+  enum MyEnum {
+    A,
+    B
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java
new file mode 100644
index 0000000..d2b739e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java
@@ -0,0 +1,87 @@
+// 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.ir.optimize.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InstanceFieldValuePropagationWithMultipleInstanceInitializersTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public InstanceFieldValuePropagationWithMultipleInstanceInitializersTest(
+      TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.class)
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+    assertThat(testClassSubject, isPresent());
+
+    // Verify that the instance-get instruction in main() is still present in main(), since the
+    // value of `a.greeting` depends on the constructor being used.
+    MethodSubject mainMethodSubject = testClassSubject.mainMethod();
+    assertThat(mainMethodSubject, isPresent());
+    assertTrue(mainMethodSubject.streamInstructions().anyMatch(InstructionSubject::isInstanceGet));
+
+    // Verify that the `greeting` field is still present.
+    ClassSubject aClassSubject = inspector.clazz(A.class);
+    assertThat(aClassSubject, isPresent());
+    assertThat(aClassSubject.uniqueFieldWithName("greeting"), isPresent());
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      A a = System.currentTimeMillis() >= 0 ? new A() : new A(new Object());
+      System.out.println(a.greeting);
+    }
+  }
+
+  @NeverClassInline
+  static class A {
+
+    String greeting;
+
+    A() {
+      this.greeting = "Hello world!";
+    }
+
+    A(Object unused) {
+      this.greeting = ":-(";
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
new file mode 100644
index 0000000..b85f583
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
@@ -0,0 +1,294 @@
+// 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.kotlin;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.kotlin.KotlinSourceDebugExtensionParser.Position;
+import com.android.tools.r8.kotlin.KotlinSourceDebugExtensionParser.Result;
+import com.android.tools.r8.kotlin.KotlinSourceDebugExtensionParser.Source;
+import com.android.tools.r8.utils.StringUtils;
+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 KotlinSourceDebugExtensionParserTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public KotlinSourceDebugExtensionParserTest(TestParameters parameters) {}
+
+  @Test
+  public void testParsingEmpty() {
+    assertNull(KotlinSourceDebugExtensionParser.parse(null));
+  }
+
+  @Test
+  public void testParsingNoInlineSources() {
+    String annotationData =
+        StringUtils.join(
+            "\n",
+            "SMAP",
+            "EnumSwitch.kt",
+            "Kotlin",
+            "*S Kotlin",
+            "*F",
+            "+ 1 EnumSwitch.kt",
+            "enumswitch/EnumSwitchKt",
+            "*L",
+            "1#1,38:1",
+            "*E");
+    Result result = KotlinSourceDebugExtensionParser.parse(annotationData);
+    assertNotNull(result);
+    assertEquals(1, result.getFiles().size());
+    Source source = result.getFiles().get(1);
+    assertEquals("EnumSwitch.kt", source.getFileName());
+    assertEquals("enumswitch/EnumSwitchKt", source.getPath());
+    assertTrue(result.getPositions().containsKey(1));
+    Position position = result.getPositions().get(1);
+    assertEquals(source, position.getSource());
+    assertEquals(1, position.getRange().from);
+    assertEquals(38, position.getRange().to);
+  }
+
+  @Test
+  public void testParsingSimpleStrata() {
+    // Taken from src/test/examplesKotlin/retrace/mainKt
+    String annotationData =
+        StringUtils.join(
+            "\n",
+            "SMAP",
+            "Main.kt",
+            "Kotlin",
+            "*S Kotlin",
+            "*F",
+            "+ 1 Main.kt",
+            "retrace/MainKt",
+            "+ 2 InlineFunction.kt",
+            "retrace/InlineFunctionKt",
+            "+ 3 InlineFunction.kt",
+            "retrace/InlineFunction",
+            "*L",
+            "1#1,22:1",
+            "7#2:23",
+            "12#3:24",
+            "*E",
+            "*S KotlinDebug",
+            "*F",
+            "+ 1 Main.kt",
+            "retrace/MainKt",
+            "*L",
+            "12#1:23",
+            "18#1:24",
+            "*E");
+    Result result = KotlinSourceDebugExtensionParser.parse(annotationData);
+    assertNotNull(result);
+    assertEquals(3, result.getFiles().size());
+    // Check that files are correctly parsed.
+    Source source1 = result.getFiles().get(1);
+    assertEquals("Main.kt", source1.getFileName());
+    assertEquals("retrace/MainKt", source1.getPath());
+
+    Source source2 = result.getFiles().get(2);
+    assertEquals("InlineFunction.kt", source2.getFileName());
+    assertEquals("retrace/InlineFunctionKt", source2.getPath());
+
+    Source source3 = result.getFiles().get(3);
+    assertEquals("InlineFunction.kt", source3.getFileName());
+    assertEquals("retrace/InlineFunction", source3.getPath());
+
+    // Check that the inline positions can be traced.
+    assertTrue(result.getPositions().containsKey(23));
+    Position position1 = result.getPositions().get(23);
+    assertEquals(source2, position1.getSource());
+    assertEquals(7, position1.getRange().from);
+    assertEquals(7, position1.getRange().to);
+
+    assertTrue(result.getPositions().containsKey(24));
+    Position position2 = result.getPositions().get(24);
+    assertEquals(source3, position2.getSource());
+    assertEquals(12, position2.getRange().from);
+    assertEquals(12, position2.getRange().to);
+  }
+
+  @Test
+  public void testNoKotlinHeader() {
+    String annotationData =
+        StringUtils.join(
+            "\n",
+            "SMAP",
+            "Main.kt",
+            "Kotlin",
+            "*F",
+            "+ 1 Main.kt",
+            "retrace/MainKt",
+            "+ 2 InlineFunction.kt",
+            "retrace/InlineFunctionKt",
+            "+ 3 InlineFunction.kt",
+            "retrace/InlineFunction",
+            "*L",
+            "1#1,22:1",
+            "7#2:23",
+            "12#3:24",
+            "*E",
+            "*S KotlinDebug",
+            "*F",
+            "+ 1 Main.kt",
+            "retrace/MainKt",
+            "*L",
+            "12#1:23",
+            "18#1:24",
+            "*E");
+    assertNull(KotlinSourceDebugExtensionParser.parse(annotationData));
+  }
+
+  @Test
+  public void testIncompleteFileBlock() {
+    String annotationData =
+        StringUtils.join(
+            "\n",
+            "SMAP",
+            "Main.kt",
+            "Kotlin",
+            "*F",
+            "+ 1 Main.kt",
+            "+ 2 InlineFunction.kt",
+            "retrace/InlineFunctionKt",
+            "+ 3 InlineFunction.kt",
+            "retrace/InlineFunction",
+            "*L",
+            "1#1,22:1",
+            "7#2:23",
+            "12#3:24",
+            "*E",
+            "*S KotlinDebug",
+            "*F",
+            "+ 1 Main.kt",
+            "retrace/MainKt",
+            "*L",
+            "12#1:23",
+            "18#1:24",
+            "*E");
+    assertNull(KotlinSourceDebugExtensionParser.parse(annotationData));
+  }
+
+  @Test
+  public void testDuplicateFileIndex() {
+    String annotationData =
+        StringUtils.join(
+            "\n",
+            "SMAP",
+            "Main.kt",
+            "Kotlin",
+            "*S Kotlin",
+            "*F",
+            "+ 1 Main.kt",
+            "retrace/MainKt",
+            "+ 2 InlineFunction.kt",
+            "retrace/InlineFunctionKt",
+            "+ 1 InlineFunction.kt",
+            "retrace/InlineFunction",
+            "*L",
+            "1#1,22:1",
+            "7#2:23",
+            "12#3:24",
+            "*E",
+            "*S KotlinDebug",
+            "*F",
+            "+ 1 Main.kt",
+            "retrace/MainKt",
+            "*L",
+            "12#1:23",
+            "18#1:24",
+            "*E");
+    assertNull(KotlinSourceDebugExtensionParser.parse(annotationData));
+  }
+
+  @Test
+  public void testNoDebugEntries() {
+    String annotationData =
+        StringUtils.join(
+            "\n",
+            "SMAP",
+            "Main.kt",
+            "Kotlin",
+            "*S Kotlin",
+            "*F",
+            "+ 1 Main.kt",
+            "retrace/MainKt",
+            "+ 2 InlineFunction.kt",
+            "retrace/InlineFunctionKt",
+            "+ 3 InlineFunction.kt",
+            "retrace/InlineFunction",
+            "*E");
+    assertNull(KotlinSourceDebugExtensionParser.parse(annotationData));
+  }
+
+  @Test
+  public void testInvalidRanges() {
+    String annotationData =
+        StringUtils.join(
+            "\n",
+            "SMAP",
+            "Main.kt",
+            "Kotlin",
+            "*S Kotlin",
+            "*F",
+            "+ 1 Main.kt",
+            "retrace/MainKt",
+            "+ 2 InlineFunction.kt",
+            "retrace/InlineFunctionKt",
+            "+ 3 InlineFunction.kt",
+            "retrace/InlineFunction",
+            "*L",
+            "1#bar,22:1",
+            "7#2:23",
+            "12#3:foo",
+            "*E");
+    assertNull(KotlinSourceDebugExtensionParser.parse(annotationData));
+  }
+
+  @Test
+  public void testNoSourceFileForEntry() {
+    String annotationData =
+        StringUtils.join(
+            "\n",
+            "SMAP",
+            "Main.kt",
+            "Kotlin",
+            "*S Kotlin",
+            "*F",
+            "+ 1 Main.kt",
+            "retrace/MainKt",
+            "+ 2 InlineFunction.kt",
+            "retrace/InlineFunctionKt",
+            "+ 3 InlineFunction.kt",
+            "retrace/InlineFunction",
+            "*L",
+            "1#1,22:1",
+            "7#2:23",
+            "12#4:24", // <-- non-existing file index
+            "*E");
+    Result parsedResult = KotlinSourceDebugExtensionParser.parse(annotationData);
+    assertNotNull(parsedResult);
+
+    assertEquals(2, parsedResult.getPositions().size());
+    assertTrue(parsedResult.getPositions().containsKey(1));
+    assertTrue(parsedResult.getPositions().containsKey(23));
+    assertFalse(parsedResult.getPositions().containsKey(24));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
index 3ba5ecd..e256e37 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
 import static org.hamcrest.CoreMatchers.containsString;
@@ -18,6 +19,7 @@
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import java.nio.file.Path;
 import java.util.Collection;
@@ -70,7 +72,7 @@
             // Keep the BKt extension method which requires metadata
             // to be called with Kotlin syntax from other kotlin code.
             .addKeepRules("-keep class **.BKt { <methods>; }")
-            .addKeepAttributes("*Annotation*")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String superClassName = pkg + ".extension_lib.Super";
@@ -93,7 +95,7 @@
 
     String appFolder = PKG_PREFIX + "/extension_app";
     ProcessResult kotlinTestCompileResult =
-        kotlinc(parameters.getRuntime().asCf())
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
             .addClasspathFiles(r8ProcessedLibZip)
             .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
             .setOutputPath(temp.newFolder().toPath())
@@ -117,7 +119,7 @@
             // Keep the BKt extension method which requires metadata
             // to be called with Kotlin syntax from other kotlin code.
             .addKeepRules("-keep class **.BKt { <methods>; }")
-            .addKeepAttributes("*Annotation*")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String superClassName = pkg + ".extension_lib.Super";
@@ -136,19 +138,19 @@
       assertThat(metadata.toString(), not(containsString("Super")));
     });
 
-    Path r8ProcessedLibZip = temp.newFile("r8-lib.zip").toPath();
-    compileResult.writeToZip(r8ProcessedLibZip);
+    Path libJar = temp.newFile("lib.jar").toPath();
+    compileResult.writeToZip(libJar);
 
     String appFolder = PKG_PREFIX + "/extension_app";
     Path output =
-        kotlinc(parameters.getRuntime().asCf())
-            .addClasspathFiles(r8ProcessedLibZip)
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addClasspathFiles(libJar)
             .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
             .setOutputPath(temp.newFolder().toPath())
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), r8ProcessedLibZip)
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), pkg + ".extension_app.MainKt")
         .assertSuccessWithOutputLines("do stuff", "do stuff");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParametertypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParametertypeTest.java
new file mode 100644
index 0000000..1030339
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParametertypeTest.java
@@ -0,0 +1,110 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRenameInParametertypeTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRenameInParametertypeTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static Path parameterLibJar;
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String paramLibFolder = PKG_PREFIX + "/parametertype_lib";
+    parameterLibJar = getStaticTemp().newFile("param_lib.jar").toPath();
+    ProcessResult processResult =
+        ToolHelper.runKotlinc(
+            null,
+            parameterLibJar,
+            null,
+            getKotlinFileInTest(paramLibFolder, "lib")
+        );
+    assertEquals(0, processResult.exitCode);
+  }
+
+  @Test
+  public void testMetadataInParameter_renamed() throws Exception {
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(parameterLibJar)
+            // Keep non-private members of Impl
+            .addKeepRules("-keep public class **.Impl { !private *; }")
+            // Keep Itf, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.Itf")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile();
+    String pkg = getClass().getPackage().getName();
+    final String itfClassName = pkg + ".parametertype_lib.Itf";
+    final String implClassName = pkg + ".parametertype_lib.Impl";
+    compileResult.inspect(inspector -> {
+      ClassSubject itf = inspector.clazz(itfClassName);
+      assertThat(itf, isPresent());
+      assertThat(itf, isRenamed());
+
+      ClassSubject impl = inspector.clazz(implClassName);
+      assertThat(impl, isPresent());
+      assertThat(impl, not(isRenamed()));
+      // API entry is kept, hence the presence of Metadata.
+      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
+      assertNotNull(metadata);
+      // TODO(b/70169921): should not refer to Itf
+      assertThat(metadata.toString(), containsString("Itf"));
+    });
+
+    Path libJar = temp.newFile("lib.jar").toPath();
+    compileResult.writeToZip(libJar);
+
+    String appFolder = PKG_PREFIX + "/parametertype_app";
+    ProcessResult processResult =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            // TODO(b/70169921): update to just .compile() once fixed.
+            .compileRaw();
+    // TODO(b/70169921): should be able to compile!
+    assertNotEquals(0, processResult.exitCode);
+    assertThat(
+        processResult.stderr,
+        containsString("cannot access class '" + pkg + ".parametertype_lib.Itf'"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
new file mode 100644
index 0000000..fe815d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
@@ -0,0 +1,107 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRenameInPropertyTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRenameInPropertyTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static Path propertyLibJar;
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String propertyLibFolder = PKG_PREFIX + "/propertytype_lib";
+    propertyLibJar = getStaticTemp().newFile("property_lib.jar").toPath();
+    ProcessResult processResult =
+        ToolHelper.runKotlinc(
+            null,
+            propertyLibJar,
+            null,
+            getKotlinFileInTest(propertyLibFolder, "lib")
+        );
+    assertEquals(0, processResult.exitCode);
+  }
+
+  @Test
+  public void testMetadataInProperty_renamed() throws Exception {
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(propertyLibJar)
+            // Keep non-private members of Impl
+            .addKeepRules("-keep public class **.Impl { !private *; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile();
+    String pkg = getClass().getPackage().getName();
+    final String itfClassName = pkg + ".propertytype_lib.Itf";
+    final String implClassName = pkg + ".propertytype_lib.Impl";
+    compileResult.inspect(inspector -> {
+      ClassSubject itf = inspector.clazz(itfClassName);
+      assertThat(itf, isPresent());
+      assertThat(itf, isRenamed());
+
+      ClassSubject impl = inspector.clazz(implClassName);
+      assertThat(impl, isPresent());
+      assertThat(impl, not(isRenamed()));
+      // API entry is kept, hence the presence of Metadata.
+      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
+      assertNotNull(metadata);
+      // TODO(b/70169921): should not refer to Itf
+      assertThat(metadata.toString(), containsString("Itf"));
+    });
+
+    Path libJar = temp.newFile("lib.jar").toPath();
+    compileResult.writeToZip(libJar);
+
+    String appFolder = PKG_PREFIX + "/propertytype_app";
+    ProcessResult processResult =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compileRaw();
+    // TODO(b/70169921): should be able to compile!
+    assertNotEquals(0, processResult.exitCode);
+    assertThat(
+        processResult.stderr,
+        containsString("cannot access class '" + pkg + ".propertytype_lib.Itf'"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturntypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturntypeTest.java
new file mode 100644
index 0000000..da6bf76
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturntypeTest.java
@@ -0,0 +1,110 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRenameInReturntypeTest extends KotlinMetadataTestBase {
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRenameInReturntypeTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static Path returntypeLibJar;
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String returntypeLibFolder = PKG_PREFIX + "/returntype_lib";
+    returntypeLibJar = getStaticTemp().newFile("returntype_lib.jar").toPath();
+    ProcessResult processResult =
+        ToolHelper.runKotlinc(
+            null,
+            returntypeLibJar,
+            null,
+            getKotlinFileInTest(returntypeLibFolder, "lib")
+        );
+    assertEquals(0, processResult.exitCode);
+  }
+
+  @Test
+  public void testmetadataInReturnType_renamed() throws Exception {
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(returntypeLibJar)
+            // Keep non-private members of Impl
+            .addKeepRules("-keep public class **.Impl { !private *; }")
+            // Keep Itf, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.Itf")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile();
+    String pkg = getClass().getPackage().getName();
+    final String itfClassName = pkg + ".returntype_lib.Itf";
+    final String implClassName = pkg + ".returntype_lib.Impl";
+    compileResult.inspect(inspector -> {
+      ClassSubject itf = inspector.clazz(itfClassName);
+      assertThat(itf, isPresent());
+      assertThat(itf, isRenamed());
+
+      ClassSubject impl = inspector.clazz(implClassName);
+      assertThat(impl, isPresent());
+      assertThat(impl, not(isRenamed()));
+      // API entry is kept, hence the presence of Metadata.
+      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
+      assertNotNull(metadata);
+      // TODO(b/70169921): should not refer to Itf
+      assertThat(metadata.toString(), containsString("Itf"));
+    });
+
+    Path libJar = temp.newFile("lib.jar").toPath();
+    compileResult.writeToZip(libJar);
+
+    String appFolder = PKG_PREFIX + "/returntype_app";
+    ProcessResult processResult =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            // TODO(b/70169921): update to just .compile() once fixed.
+            .compileRaw();
+    // TODO(b/70169921): should be able to compile!
+    assertNotEquals(0, processResult.exitCode);
+    assertThat(
+        processResult.stderr,
+        containsString("cannot access class '" + pkg + ".returntype_lib.Itf'"));
+  }
+}
+
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java
index 7001909..dc29f2c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
 import static org.hamcrest.CoreMatchers.containsString;
@@ -17,6 +18,7 @@
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import java.nio.file.Path;
 import java.util.Collection;
@@ -66,7 +68,7 @@
             .addProgramFiles(supertypeLibJar)
             // Keep non-private members except for ones in `internal` definitions.
             .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
-            .addKeepAttributes("*Annotation*")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String itfClassName = pkg + ".supertype_lib.internal.Itf";
@@ -90,7 +92,7 @@
 
     String appFolder = PKG_PREFIX + "/supertype_app";
     Path output =
-        kotlinc(parameters.getRuntime().asCf())
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
             .addClasspathFiles(r8ProcessedLibZip)
             .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
             .setOutputPath(temp.newFolder().toPath())
@@ -112,7 +114,7 @@
             .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
             // Keep `internal` definitions, but allow minification.
             .addKeepRules("-keep,allowobfuscation class **.internal.** { *; }")
-            .addKeepAttributes("*Annotation*")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String itfClassName = pkg + ".supertype_lib.internal.Itf";
@@ -133,19 +135,19 @@
       assertThat(metadata.toString(), containsString("a/a"));
     });
 
-    Path r8ProcessedLibZip = temp.newFile("r8-lib.zip").toPath();
-    compileResult.writeToZip(r8ProcessedLibZip);
+    Path libJar = temp.newFile("lib.jar").toPath();
+    compileResult.writeToZip(libJar);
 
     String appFolder = PKG_PREFIX + "/supertype_app";
     Path output =
-        kotlinc(parameters.getRuntime().asCf())
-            .addClasspathFiles(r8ProcessedLibZip)
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addClasspathFiles(libJar)
             .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
             .setOutputPath(temp.newFolder().toPath())
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), r8ProcessedLibZip)
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), pkg + ".supertype_app.MainKt")
         .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index fa46140..00b5606 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.util.Collection;
@@ -49,7 +50,7 @@
             .addProgramFiles(getJavaJarFile(folder))
             .addProgramFiles(ToolHelper.getKotlinReflectJar())
             .addKeepMainRule(mainClassName)
-            .addKeepAttributes("*Annotation*")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
             .addKeepRules("-keep class kotlin.Metadata")
             // TODO(b/145090972): Should never need to exit gracefully during testing.
             .allowClassInlinerGracefulExit()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_app/main.kt
new file mode 100644
index 0000000..17a6457
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_app/main.kt
@@ -0,0 +1,18 @@
+// 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.kotlin.metadata.parametertype_app
+
+import com.android.tools.r8.kotlin.metadata.parametertype_lib.Impl
+
+class ProgramClass : Impl() {
+  override fun bar() {
+    super.bar()
+    println("Program::bar")
+  }
+}
+
+fun main() {
+  val instance = ProgramClass()
+  instance.foo(instance)
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_lib/lib.kt
new file mode 100644
index 0000000..75c3d78
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_lib/lib.kt
@@ -0,0 +1,19 @@
+// 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.kotlin.metadata.parametertype_lib
+
+interface Itf {
+  fun foo(arg : Itf)
+  fun bar()
+}
+
+open class Impl : Itf {
+  override fun foo(arg : Itf) {
+    arg.bar()
+  }
+
+  override fun bar() {
+    println("Impl::bar")
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_app/main.kt
new file mode 100644
index 0000000..214300c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_app/main.kt
@@ -0,0 +1,14 @@
+// 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.kotlin.metadata.propertytype_app
+
+import com.android.tools.r8.kotlin.metadata.propertytype_lib.Impl
+
+class ProgramClass : Impl(8) {
+}
+
+fun main() {
+  val instance : Impl = ProgramClass()
+  println(instance.prop1)
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_lib/lib.kt
new file mode 100644
index 0000000..40aa801
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_lib/lib.kt
@@ -0,0 +1,18 @@
+// 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.kotlin.metadata.propertytype_lib
+
+interface Itf {
+  val prop1: Itf
+}
+
+open class Impl(val id: Int) : Itf {
+
+  override val prop1: Itf
+    get() = this
+
+  override fun toString(): String {
+    return "Impl::$id"
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_app/main.kt
new file mode 100644
index 0000000..37d446e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_app/main.kt
@@ -0,0 +1,20 @@
+// 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.kotlin.metadata.returntype_app
+
+import com.android.tools.r8.kotlin.metadata.returntype_lib.Impl
+import com.android.tools.r8.kotlin.metadata.returntype_lib.Itf
+
+class ProgramClass : Impl() {
+  override fun foo(): Itf {
+    super.foo()
+    println("Program::foo")
+    return this
+  }
+}
+
+fun main() {
+  val instance = ProgramClass()
+  println(instance == instance.foo())
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_lib/lib.kt
new file mode 100644
index 0000000..c24e292
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_lib/lib.kt
@@ -0,0 +1,15 @@
+// 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.kotlin.metadata.returntype_lib
+
+interface Itf {
+  fun foo() : Itf
+}
+
+open class Impl : Itf {
+  override fun foo() : Itf {
+    println("Impl::foo")
+    return this
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt
index 4192774..309b756 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt
@@ -12,6 +12,6 @@
   }
 }
 
-fun main(args: Array<String>) {
+fun main() {
   ProgramClass().foo()
 }
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java b/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
new file mode 100644
index 0000000..db1864c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
@@ -0,0 +1,113 @@
+// 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.resolution;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LibraryExtendsProgramRefinedReceiverIsLibraryClass extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    // Only test on CF, as the test use additional runtime classpath classes.
+    return getTestParameters().withCfRuntimes().build();
+  }
+
+  // Regression test for b/145645482. Resolution issue related to lookupSingleVirtualTarget for
+  // library extending program. The concrete issue hit in the Android Platform build this was in
+  // a debug build, where the "debug write" instruction introduces an "alias" causing the
+  // "receiver lower bound" to be unknown (null). With a receiver type of ProgramClass and a
+  // "refined receiver type" of LibraryClass (lattice type of the debug write instruction) the
+  // issue appeared.
+  //
+  // However, with a phi the same could happen in release mode. Again the "refined receiver type"
+  // becomes LibraryClass (lattice type of the phi).
+
+  public LibraryExtendsProgramRefinedReceiverIsLibraryClass(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void regression145645482DebugMode() throws Exception {
+    testForR8Compat(parameters.getBackend())
+        .addLibraryFiles(runtimeJar(parameters.getBackend()))
+        .addLibraryClasses(LibraryClass.class)
+        .addProgramClasses(ProgramClass.class, ProgramTestRunnerWithoutPhi.class)
+        .enableInliningAnnotations()
+        .addKeepClassRules(ProgramClass.class)
+        .addKeepMainRule(ProgramTestRunnerWithoutPhi.class)
+        .setMinApi(parameters.getApiLevel())
+        .debug()
+        .compile()
+        .addRunClasspathClasses(LibraryClass.class)
+        .run(parameters.getRuntime(), ProgramTestRunnerWithoutPhi.class)
+        .assertSuccessWithOutput(StringUtils.lines("SUCCESS"));
+  }
+
+  @Test
+  public void regression145645482ReleaseMode() throws Exception {
+    testForR8Compat(parameters.getBackend())
+        .addLibraryFiles(runtimeJar(parameters.getBackend()))
+        .addLibraryClasses(LibraryClass.class)
+        .addProgramClasses(ProgramClass.class, ProgramTestRunnerWithPhi.class)
+        .enableInliningAnnotations()
+        .addKeepClassRules(ProgramClass.class)
+        .addKeepMainRule(ProgramTestRunnerWithPhi.class)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .addRunClasspathClasses(LibraryClass.class)
+        .run(parameters.getRuntime(), ProgramTestRunnerWithPhi.class)
+        .assertSuccessWithOutput(StringUtils.lines("SUCCESS"));
+  }
+
+  static class ProgramClass {
+    public void addTestSuite(Class<?> suite) {
+    }
+  }
+
+  static class LibraryClass extends ProgramClass {
+    @Override
+    public void addTestSuite(Class<?> suite) {
+      super.addTestSuite(suite);
+    }
+  }
+
+  static class ProgramTestRunnerWithoutPhi {
+    @NeverInline
+    public static ProgramClass test() {
+      ProgramClass suite = new LibraryClass();
+      suite.addTestSuite(ProgramTestRunnerWithoutPhi.class);
+      return suite;
+    }
+
+    public static void main(String[] args) {
+      ProgramTestRunnerWithoutPhi.test();
+      System.out.println("SUCCESS");
+    }
+  }
+
+  static class ProgramTestRunnerWithPhi {
+    @NeverInline
+    public static ProgramClass test(ProgramClass otherSuite) {
+      ProgramClass suite = System.currentTimeMillis() > 0 ? new LibraryClass(): otherSuite;
+      suite.addTestSuite(ProgramTestRunnerWithPhi.class);
+      return suite;
+    }
+
+    public static void main(String[] args) {
+      ProgramTestRunnerWithPhi.test(null);
+      System.out.println("SUCCESS");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
new file mode 100644
index 0000000..cba0f8b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -0,0 +1,137 @@
+// 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.retrace;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 KotlinInlineFunctionRetraceTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    // TODO(b/141817471): Extend with compilation modes.
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public KotlinInlineFunctionRetraceTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRuntime() throws ExecutionException, CompilationFailedException, IOException {
+    testForRuntime(parameters)
+        .addProgramFiles(
+            kotlinc(TestRuntime.getCheckedInJdk8(), KOTLINC, KotlinTargetVersion.JAVA_8)
+                .addSourceFiles(
+                    getFilesInTestFolderRelativeToClass(
+                        KotlinInlineFunctionRetraceTest.class, "kt", ".kt"))
+                .compile())
+        .addRunClasspathFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar()))
+        .run(parameters.getRuntime(), "retrace.MainKt")
+        .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
+        .assertFailureWithErrorThatMatches(containsString("at retrace.MainKt.main(Main.kt:15)"));
+  }
+
+  @Test
+  public void testRetraceKotlinInlineStaticFunction()
+      throws ExecutionException, CompilationFailedException, IOException {
+    String main = "retrace.MainKt";
+    R8TestRunResult result =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(
+                kotlinc(TestRuntime.getCheckedInJdk8(), KOTLINC, KotlinTargetVersion.JAVA_8)
+                    .addSourceFiles(
+                        getFilesInTestFolderRelativeToClass(
+                            KotlinInlineFunctionRetraceTest.class, "kt", ".kt"))
+                    .compile())
+            .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+            .addKeepAttributes("SourceFile", "LineNumberTable")
+            .setMode(CompilationMode.RELEASE)
+            .addKeepMainRule(main)
+            .setMinApi(parameters.getApiLevel())
+            .run(parameters.getRuntime(), main)
+            .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
+            .assertFailureWithErrorThatMatches(containsString("at retrace.MainKt.main(Main.kt:2)"));
+    List<String> retrace = result.retrace();
+    // TODO(b/141817471): Change the tracing information when solved.
+    // assertThat(retrace.get(1), containsString("at retrace.MainKt.main(Main.kt:15)"));
+  }
+
+  @Test
+  public void testRetraceKotlinInlineInstanceFunction()
+      throws ExecutionException, CompilationFailedException, IOException {
+    String main = "retrace.MainInstanceKt";
+    R8TestRunResult result =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(
+                kotlinc(TestRuntime.getCheckedInJdk8(), KOTLINC, KotlinTargetVersion.JAVA_8)
+                    .addSourceFiles(
+                        getFilesInTestFolderRelativeToClass(
+                            KotlinInlineFunctionRetraceTest.class, "kt", ".kt"))
+                    .compile())
+            .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+            .addKeepAttributes("SourceFile", "LineNumberTable")
+            .setMode(CompilationMode.RELEASE)
+            .addKeepMainRule(main)
+            .setMinApi(parameters.getApiLevel())
+            .run(parameters.getRuntime(), main)
+            .assertFailureWithErrorThatMatches(containsString("inlineExceptionInstance"))
+            .assertFailureWithErrorThatMatches(
+                containsString("at retrace.MainInstanceKt.main(MainInstance.kt:2)"));
+    List<String> retrace = result.retrace();
+    // TODO(b/141817471): Change the tracing information when solved.
+    // assertThat(retrace.get(1), containsString("at
+    // retrace.MainInstanceKt.main(MainInstance.kt:13)"));
+  }
+
+  @Test
+  public void testRetraceKotlinNestedInlineFunction()
+      throws ExecutionException, CompilationFailedException, IOException {
+    // TODO(b/141817471): Change the tracing information when solved.
+    int lineNumber = parameters.isCfRuntime() ? 4 : 3;
+    String main = "retrace.MainNestedKt";
+    R8TestRunResult result =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(
+                kotlinc(TestRuntime.getCheckedInJdk8(), KOTLINC, KotlinTargetVersion.JAVA_8)
+                    .addSourceFiles(
+                        getFilesInTestFolderRelativeToClass(
+                            KotlinInlineFunctionRetraceTest.class, "kt", ".kt"))
+                    .compile())
+            .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+            .addKeepAttributes("SourceFile", "LineNumberTable")
+            .setMode(CompilationMode.RELEASE)
+            .addKeepMainRule(main)
+            .setMinApi(parameters.getApiLevel())
+            .run(parameters.getRuntime(), main)
+            .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
+            .assertFailureWithErrorThatMatches(
+                containsString("at retrace.MainNestedKt.main(MainNested.kt:" + lineNumber + ")"));
+    List<String> retrace = result.retrace();
+    // TODO(b/141817471): Change the tracing information when solved.
+    // assertThat(retrace.get(1), containsString("at retrace.MainNestedKt.main(MainNested.kt:19)"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/InlineFunction.kt b/src/test/java/com/android/tools/r8/retrace/kt/InlineFunction.kt
new file mode 100644
index 0000000..ceb2656
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/InlineFunction.kt
@@ -0,0 +1,18 @@
+// 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 retrace
+
+inline fun inlineExceptionStatic(f: () -> Unit) {
+  println("in inlineExceptionStatic")
+  throw java.lang.Exception("inlineExceptionStatic")
+  println("will not be printed")
+}
+
+class InlineFunction {
+  inline fun inlineExceptionInstance(f: () -> Unit) {
+    println("in inlineExceptionInstance")
+    throw java.lang.Exception("inlineExceptionInstance")
+    println("will not be printed")
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/Main.kt b/src/test/java/com/android/tools/r8/retrace/kt/Main.kt
new file mode 100644
index 0000000..1c97080
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/Main.kt
@@ -0,0 +1,12 @@
+// 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 retrace
+
+// Adding a few spaces to better see where the debug information is positioned.
+
+fun main(args: Array<String>) {
+  inlineExceptionStatic {
+    throw Exception("Never get's here")
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/MainInstance.kt b/src/test/java/com/android/tools/r8/retrace/kt/MainInstance.kt
new file mode 100644
index 0000000..38a781f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/MainInstance.kt
@@ -0,0 +1,10 @@
+// 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 retrace
+
+fun main(args: Array<String>) {
+  InlineFunction().inlineExceptionInstance {
+    throw Exception("Never get's here")
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/MainNested.kt b/src/test/java/com/android/tools/r8/retrace/kt/MainNested.kt
new file mode 100644
index 0000000..f8329be
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/MainNested.kt
@@ -0,0 +1,14 @@
+// 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 retrace
+
+// Some spaces to better see if retrace is working as expected.
+
+
+fun main(args: Array<String>) {
+  nestedInline {
+    throw Exception("Never get's here")
+  }
+}
+
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/MainWithMultipleInlines.kt b/src/test/java/com/android/tools/r8/retrace/kt/MainWithMultipleInlines.kt
new file mode 100644
index 0000000..c968da4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/MainWithMultipleInlines.kt
@@ -0,0 +1,18 @@
+// 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 retrace
+
+fun main(args: Array<String>) {
+  println("Before")
+  inlineExceptionStatic {
+    throw Exception("Never get's here")
+  }
+  println("Middle")
+  inlineExceptionStatic {
+    throw Exception("Never get's here")
+  }
+  println("After")
+}
+
+
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/NestedInlineFunction.kt b/src/test/java/com/android/tools/r8/retrace/kt/NestedInlineFunction.kt
new file mode 100644
index 0000000..d8e4736
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/NestedInlineFunction.kt
@@ -0,0 +1,11 @@
+// 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 retrace
+
+inline fun nestedInline(f: () -> Unit) {
+  println("in nestedInline")
+  inlineExceptionStatic(f)
+  println("will never be printed")
+}
+
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index 5d110e4..94c6be6 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -48,6 +48,10 @@
 
   private void configure(InternalOptions options) {
     options.enableEnumValueOptimization = enableOptimization;
+
+    // TODO(b/125282093): Remove options modification once landed.
+    assert !options.enableValuePropagationForInstanceFields;
+    options.enableValuePropagationForInstanceFields = true;
   }
 
   @Test
@@ -82,6 +86,7 @@
       assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inlined"), 1);
       assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inSwitch"), 11);
       assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), 1);
+      assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), 1);
     } else {
       assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("simple"));
       assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("local"));
@@ -89,12 +94,12 @@
       assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("inlined"));
       assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("inSwitch"));
       assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("differentTypeStaticField"));
+      assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
     }
 
     assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
     assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
     assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("phi"));
-    assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
   }
 
   @Test
@@ -125,12 +130,14 @@
       assertNameReplacedWithConst(clazz.uniqueMethodWithName("multipleUsages"), expectedConst);
       assertNameReplacedWithConst(clazz.uniqueMethodWithName("inlined"), "TWO");
       assertNameReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), "DOWN");
+      assertNameReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), "TWO");
     } else {
       assertNameWasNotReplaced(clazz.uniqueMethodWithName("simple"));
       assertNameWasNotReplaced(clazz.uniqueMethodWithName("local"));
       assertNameWasNotReplaced(clazz.uniqueMethodWithName("multipleUsages"));
       assertNameWasNotReplaced(clazz.uniqueMethodWithName("inlined"));
       assertNameWasNotReplaced(clazz.uniqueMethodWithName("differentTypeStaticField"));
+      assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
     }
 
     // TODO(jakew) this should be allowed!
@@ -138,7 +145,6 @@
 
     assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
     assertNameWasNotReplaced(clazz.uniqueMethodWithName("phi"));
-    assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
   }
 
   @Test
@@ -178,6 +184,7 @@
       assertToStringReplacedWithConst(clazz.uniqueMethodWithName("nonValueStaticField"), "TWO");
       assertToStringReplacedWithConst(
           clazz.uniqueMethodWithName("differentTypeStaticField"), "DOWN");
+      assertToStringReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), "TWO");
     } else {
       assertToStringWasNotReplaced(clazz.uniqueMethodWithName("noToString"));
       assertToStringWasNotReplaced(clazz.uniqueMethodWithName("local"));
@@ -185,11 +192,11 @@
       assertToStringWasNotReplaced(clazz.uniqueMethodWithName("inlined"));
       assertToStringWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
       assertToStringWasNotReplaced(clazz.uniqueMethodWithName("differentTypeStaticField"));
+      assertToStringWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
     }
 
     assertToStringWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
     assertToStringWasNotReplaced(clazz.uniqueMethodWithName("phi"));
-    assertToStringWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
   }
 
   private static void assertOrdinalReplacedWithConst(MethodSubject method, int expectedConst) {
diff --git a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
index ea96ead..3985648 100644
--- a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
@@ -51,48 +51,61 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(EffectivelyFinalInstanceFieldsTest.class)
         .addKeepMainRule(MAIN)
+        .addOptionsModification(
+            options -> {
+              // TODO(b/125282093): Remove options modification once landed.
+              assert !options.enableValuePropagationForInstanceFields;
+              options.enableValuePropagationForInstanceFields = true;
+            })
         .enableInliningAnnotations()
         .enableClassInliningAnnotations()
         .enableMergeAnnotations()
         .setMinApi(parameters.getRuntime())
         .compile()
-        .inspect(codeInspector -> {
-          ClassSubject main = codeInspector.clazz(MAIN);
-          assertThat(main, isPresent());
+        .inspect(
+            codeInspector -> {
+              ClassSubject main = codeInspector.clazz(MAIN);
+              assertThat(main, isPresent());
 
-          MethodSubject mainMethod = main.mainMethod();
-          assertThat(mainMethod, isPresent());
+              MethodSubject mainMethod = main.mainMethod();
+              assertThat(mainMethod, isPresent());
 
-          assertTrue(
-              mainMethod.streamInstructions().noneMatch(
-                  i -> i.isConstString("Dead code: 1", JumboStringMode.ALLOW)));
-          // TODO(b/138913138): effectively final, and default value is set.
-          assertFalse(
-              mainMethod.streamInstructions().noneMatch(
-                  i -> i.isConstString("Dead code: 2", JumboStringMode.ALLOW)));
-          // TODO(b/138913138): not trivial; assigned only once in <init>
-          assertFalse(
-              mainMethod.streamInstructions().noneMatch(
-                  i -> i.isConstString("Dead code: 3", JumboStringMode.ALLOW)));
-          assertTrue(
-              mainMethod.streamInstructions().noneMatch(
-                  i -> i.isConstString("Dead code: 4", JumboStringMode.ALLOW)));
-          // TODO(b/138913138): effectively final, and default value is set.
-          assertFalse(
-              mainMethod.streamInstructions().noneMatch(
-                  i -> i.isConstString("Dead code: 5", JumboStringMode.ALLOW)));
-          // TODO(b/138913138): not trivial; assigned multiple times, but within a certain range.
-          assertFalse(
-              mainMethod.streamInstructions().noneMatch(
-                  i -> i.isConstString("Dead code: 6", JumboStringMode.ALLOW)));
-          assertTrue(
-              mainMethod.streamInstructions().noneMatch(
-                  i -> i.isConstString("Dead code: 7", JumboStringMode.ALLOW)));
-          // TODO(b/138913138): effectively final, and default value is set.
-          assertFalse(
-              mainMethod.streamInstructions().noneMatch(
-                  i -> i.isConstString("Dead code: 8", JumboStringMode.ALLOW)));
-        })
+              assertTrue(
+                  mainMethod
+                      .streamInstructions()
+                      .noneMatch(i -> i.isConstString("Dead code: 1", JumboStringMode.ALLOW)));
+              assertTrue(
+                  mainMethod
+                      .streamInstructions()
+                      .noneMatch(i -> i.isConstString("Dead code: 2", JumboStringMode.ALLOW)));
+              // TODO(b/138913138): not trivial; assigned only once in <init>
+              assertFalse(
+                  mainMethod
+                      .streamInstructions()
+                      .noneMatch(i -> i.isConstString("Dead code: 3", JumboStringMode.ALLOW)));
+              assertTrue(
+                  mainMethod
+                      .streamInstructions()
+                      .noneMatch(i -> i.isConstString("Dead code: 4", JumboStringMode.ALLOW)));
+              assertTrue(
+                  mainMethod
+                      .streamInstructions()
+                      .noneMatch(i -> i.isConstString("Dead code: 5", JumboStringMode.ALLOW)));
+              // TODO(b/138913138): not trivial; assigned multiple times, but within a certain
+              // range.
+              assertFalse(
+                  mainMethod
+                      .streamInstructions()
+                      .noneMatch(i -> i.isConstString("Dead code: 6", JumboStringMode.ALLOW)));
+              assertTrue(
+                  mainMethod
+                      .streamInstructions()
+                      .noneMatch(i -> i.isConstString("Dead code: 7", JumboStringMode.ALLOW)));
+              assertTrue(
+                  mainMethod
+                      .streamInstructions()
+                      .noneMatch(i -> i.isConstString("Dead code: 8", JumboStringMode.ALLOW)));
+            })
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutputLines("The end");
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
index f28f242..564902a 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
@@ -40,7 +40,12 @@
         TreeShaking18Test::unusedRemoved,
         null,
         null,
-        ImmutableList.of("src/test/examples/shaking18/keep-rules.txt"));
+        ImmutableList.of("src/test/examples/shaking18/keep-rules.txt"),
+        options -> {
+          // TODO(b/125282093): Remove options modification once landed.
+          assert !options.enableValuePropagationForInstanceFields;
+          options.enableValuePropagationForInstanceFields = true;
+        });
   }
 
   private static void unusedRemoved(CodeInspector inspector) {
diff --git a/tools/archive.py b/tools/archive.py
index ff0f3db..c24babb 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -140,6 +140,7 @@
     utils.D8,
     utils.R8LIB,
     utils.R8LIB_NO_DEPS,
+    utils.LIBRARY_DESUGAR_CONVERSIONS,
     '-Pno_internal'
   ])
 
@@ -235,7 +236,9 @@
         with utils.TempDir() as tmp_dir:
           desugar_jdk_libs_configuration_jar = os.path.join(tmp_dir, jar_name)
           create_maven_release.generate_jar_with_desugar_configuration(
-              utils.DESUGAR_CONFIGURATION, desugar_jdk_libs_configuration_jar)
+              utils.DESUGAR_CONFIGURATION,
+              utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP,
+              desugar_jdk_libs_configuration_jar)
 
           if options.dry_run:
             print('Dry run, not actually creating maven repo for '
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index 72186a8..cc36693 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -16,6 +16,7 @@
 from string import Template
 import tempfile
 import utils
+import zipfile
 
 DEPENDENCYTEMPLATE = Template(
 """
@@ -333,16 +334,29 @@
       out)
 
 # Write the desugaring configuration of a jar file with the following content:
-#  META-INF
-#    desugar
-#      d8
+#  java/
+#    util/
+#      <java.util conversions classes>
+#    time/
+#      <java.time conversions classes>
+#  META-INF/
+#    desugar/
+#      d8/
 #        desugar.json
-def generate_jar_with_desugar_configuration(configuration, destination):
+#        lint/
+#          <lint files>
+def generate_jar_with_desugar_configuration(configuration, conversions, destination):
   with utils.TempDir() as tmp_dir:
+    # Add conversion classes.
+    with zipfile.ZipFile(conversions, 'r') as conversions_zip:
+      conversions_zip.extractall(tmp_dir)
+
+    # Add configuration
     configuration_dir = join(tmp_dir, 'META-INF', 'desugar', 'd8')
     makedirs(configuration_dir)
     copyfile(configuration, join(configuration_dir, 'desugar.json'))
 
+    # Add lint configuartion.
     lint_dir = join(configuration_dir, 'lint')
     makedirs(lint_dir)
     cmd = [
@@ -367,7 +381,9 @@
   # Generate the jar with the configuration file.
   jar_file = 'desugar_configuration.jar'
   generate_jar_with_desugar_configuration(
-      utils.DESUGAR_CONFIGURATION, jar_file)
+      utils.DESUGAR_CONFIGURATION,
+      utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP,
+      jar_file)
   # Write the maven zip file.
   generate_maven_zip(
       'desugar_jdk_libs_configuration', version, pom_file, jar_file, out)
diff --git a/tools/r8_release.py b/tools/r8_release.py
index e661dc6..39f60c0 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -156,22 +156,33 @@
 
 def release_studio_or_aosp(path, options, git_message):
   with utils.ChangedWorkingDirectory(path):
-    subprocess.call(['repo', 'abandon', 'update-r8'])
+    if not options.use_existing_work_branch:
+      subprocess.call(['repo', 'abandon', 'update-r8'])
     if not options.no_sync:
       subprocess.check_call(['repo', 'sync', '-cq', '-j', '16'])
 
     prebuilts_r8 = os.path.join(path, 'prebuilts', 'r8')
 
-    with utils.ChangedWorkingDirectory(prebuilts_r8):
-      subprocess.check_call(['repo', 'start', 'update-r8'])
+    if not options.use_existing_work_branch:
+      with utils.ChangedWorkingDirectory(prebuilts_r8):
+        subprocess.check_call(['repo', 'start', 'update-r8'])
 
     update_prebuilds(options.version, path)
 
     with utils.ChangedWorkingDirectory(prebuilts_r8):
-      subprocess.check_call(['git', 'commit', '-a', '-m', git_message])
-      process = subprocess.Popen(['repo', 'upload', '.', '--verify'],
-                                 stdin=subprocess.PIPE)
-      return process.communicate(input='y\n')[0]
+      if not options.use_existing_work_branch:
+        subprocess.check_call(['git', 'commit', '-a', '-m', git_message])
+      else:
+        print ('Not committing when --use-existing-work-branch. '
+            + 'Commit message should be:\n\n'
+            + git_message
+            + '\n')
+      # Don't upload if requested not to, or if changes are not committed due
+      # to --use-existing-work-branch
+      if not options.no_upload and not options.use_existing_work_branch:
+        process = subprocess.Popen(['repo', 'upload', '.', '--verify'],
+                                   stdin=subprocess.PIPE)
+        return process.communicate(input='y\n')[0]
 
 
 def prepare_aosp(args):
@@ -278,9 +289,10 @@
 def prepare_google3(args):
   assert args.version
   # Check if an existing client exists.
-  if ':update-r8:' in subprocess.check_output('g4 myclients', shell=True):
-    print "Remove the existing 'update-r8' client before continuing."
-    sys.exit(1)
+  if not options.use_existing_work_branch:
+    if ':update-r8:' in subprocess.check_output('g4 myclients', shell=True):
+      print "Remove the existing 'update-r8' client before continuing."
+      sys.exit(1)
 
   def release_google3(options):
     print "Releasing for Google 3"
@@ -354,7 +366,8 @@
 
       assert options.version in blaze_result
 
-      return g4_change(new_version, options.version)
+      if not options.no_upload:
+        return g4_change(new_version, options.version)
 
   return release_google3
 
@@ -517,6 +530,14 @@
                       default=False,
                       action='store_true',
                       help='Release for google 3')
+  result.add_argument('--use-existing-work-branch', '--use_existing_work_branch',
+                      default=False,
+                      action='store_true',
+                      help='Use existing work branch/CL in aosp/studio/google3')
+  result.add_argument('--no-upload', '--no_upload',
+                      default=False,
+                      action='store_true',
+                      help="Don't upload for code review")
   result.add_argument('--dry-run',
                       default=False,
                       action='store_true',
@@ -549,7 +570,7 @@
       sys.exit(1)
     targets_to_run.append(prepare_release(args))
 
-  if args.google3 or args.studio:
+  if args.google3 or (args.studio and not args.no_sync):
     utils.check_prodacces()
 
   if args.google3:
diff --git a/tools/retrace.py b/tools/retrace.py
index 44120f2..6f3593f 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -24,6 +24,10 @@
       help='Version to download r8lib map file for.',
       default=None)
   parser.add_argument(
+      '--tag',
+      help='Tag to download r8lib map file for.',
+      default=None)
+  parser.add_argument(
       '--map',
       help='Path to r8lib map.',
       default=utils.R8LIB_JAR + '.map')
@@ -34,20 +38,36 @@
   return parser.parse_args()
 
 
+def find_version_or_hash_from_tag(tag_or_hash):
+  info = subprocess.check_output([
+      'git',
+      'show',
+      tag_or_hash,
+      '-s',
+      '--format=oneline']).splitlines()[-1].split()
+  # The info should be on the following form [hash,"Version",version]
+  if len(info) == 3 and len(info[0]) == 40 and info[1] == "Version":
+    return info[2]
+  return None
+
+
 def main():
   args = parse_arguments()
   r8lib_map_path = args.map
-  hashOrVersion = args.commit_hash or args.version
-  if hashOrVersion:
+  if args.tag:
+    hash_or_version = find_version_or_hash_from_tag(args.tag)
+  else:
+    hash_or_version = args.commit_hash or args.version
+  if hash_or_version:
     download_path = archive.GetUploadDestination(
-        hashOrVersion,
+        hash_or_version,
         'r8lib.jar.map',
         args.commit_hash is not None)
     if utils.file_exists_on_cloud_storage(download_path):
       r8lib_map_path = tempfile.NamedTemporaryFile().name
       utils.download_file_from_cloud_storage(download_path, r8lib_map_path)
     else:
-      print('Could not find map file from argument: %s.' % hashOrVersion)
+      print('Could not find map file from argument: %s.' % hash_or_version)
       return 1
 
   retrace_args = [
diff --git a/tools/utils.py b/tools/utils.py
index fe0ca5e..51c5a12 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -41,6 +41,7 @@
 R8LIB = 'r8lib'
 R8LIB_NO_DEPS = 'r8LibNoDeps'
 R8_SRC = 'sourceJar'
+LIBRARY_DESUGAR_CONVERSIONS = 'buildLibraryDesugarConversions'
 
 D8_JAR = os.path.join(LIBS, 'd8.jar')
 R8_JAR = os.path.join(LIBS, 'r8.jar')
@@ -50,16 +51,10 @@
 R8_FULL_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-full-exclude-deps.jar')
 MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
 MAVEN_ZIP_LIB = os.path.join(LIBS, 'r8lib.zip')
-# TODO(b/134732760): The JSON configuration should be moved.
+LIBRARY_DESUGAR_CONVERSIONS_ZIP = os.path.join(LIBS, 'library_desugar_conversions.zip')
+
 DESUGAR_CONFIGURATION = os.path.join(
-      TEST_ROOT,
-      'com',
-      'android',
-      'tools',
-      'r8',
-      'desugar',
-      'desugaredlibrary',
-      'desugar_jdk_libs.json')
+      'src', 'library_desugar', 'desugar_jdk_libs.json')
 DESUGAR_CONFIGURATION_MAVEN_ZIP = os.path.join(
   LIBS, 'desugar_jdk_libs_configuration.zip')
 GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
@@ -561,10 +556,6 @@
     configuration_json = json.loads(f.read())
     configuration_format_version = \
         configuration_json.get('configuration_format_version')
-    if (configuration_format_version > 2):
-      raise Exception(
-          'Unsupported "configuration_format_version" '
-              + str(configuration_format_version))
     version = configuration_json.get('version')
     if not version:
       raise Exception(