Merge commit 'b093b67b098827ca76be39196de7af7122b1fb16' into dev-release
diff --git a/.gitignore b/.gitignore
index 0629bac..aa4c9ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -135,12 +135,16 @@
 third_party/protobuf-lite/
 third_party/r8
 third_party/r8.tar.gz
+third_party/r8-releases/2.0.74
+third_party/r8-releases/2.0.74.tar.gz
 third_party/r8mappings
 third_party/r8mappings.tar.gz
 third_party/remapper
 third_party/remapper.tar.gz
 third_party/retrace_benchmark
 third_party/retrace_benchmark.tar.gz
+third_party/retrace_internal
+third_party/retrace_internal.tar.gz
 third_party/rhino-1.7.10
 third_party/rhino-1.7.10.tar.gz
 third_party/rhino-android-1.1.1
diff --git a/build.gradle b/build.gradle
index 6050eda..4428f58 100644
--- a/build.gradle
+++ b/build.gradle
@@ -335,6 +335,7 @@
                 "proguard/proguard5.2.1",
                 "proguard/proguard6.0.1",
                 "r8",
+                "r8-releases/2.0.74",
                 "r8mappings",
                 "tachiyomi"
         ],
@@ -427,6 +428,7 @@
         "proguardsettings",
         "proto",
         "protobuf-lite",
+        "retrace_internal",
         "youtube/youtube.android_12.10",
         "youtube/youtube.android_12.17",
         "youtube/youtube.android_12.22",
@@ -1050,12 +1052,6 @@
     destinationDir file('build/libs')
 }
 
-task buildD8ApiUsageSample(type: Jar) {
-    from sourceSets.apiUsageSample.output
-    baseName 'd8_api_usage_sample'
-    destinationDir file('tests')
-}
-
 task buildR8ApiUsageSample(type: Jar) {
     from sourceSets.apiUsageSample.output
     baseName 'r8_api_usage_sample'
@@ -1063,7 +1059,6 @@
 }
 
 task buildApiSampleJars {
-    dependsOn buildD8ApiUsageSample
     dependsOn buildR8ApiUsageSample
 }
 
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 87562ad..afa5f60 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -29,6 +29,7 @@
 builder_mixins {
   name: "internal"
   dimensions: "internal:true"
+  dimensions: "cores:2"
 }
 
 builder_mixins {
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index 8c023aa..5bdc91e 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -1,10 +1,73 @@
 {
-  "configuration_format_version": 4,
+  "configuration_format_version": 3,
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs",
-  "version": "0.12.0",
+  "version": "1.0.9",
   "required_compilation_api_level": 26,
   "synthesized_library_classes_package_prefix": "j$.",
+  "common_flags": [
+    {
+      "api_level_below_or_equal": 25,
+      "wrapper_conversion": [
+        "java.time.Clock"
+      ]
+    },
+    {
+      "api_level_below_or_equal": 23,
+      "wrapper_conversion": [
+        "java.util.PrimitiveIterator$OfDouble",
+        "java.util.PrimitiveIterator$OfInt",
+        "java.util.PrimitiveIterator$OfLong",
+        "java.util.Spliterator",
+        "java.util.Spliterator$OfDouble",
+        "java.util.Spliterator$OfInt",
+        "java.util.Spliterator$OfLong",
+        "java.util.Spliterator$OfPrimitive",
+        "java.util.function.BiConsumer",
+        "java.util.function.BiFunction",
+        "java.util.function.BiPredicate",
+        "java.util.function.BinaryOperator",
+        "java.util.function.Consumer",
+        "java.util.function.DoubleBinaryOperator",
+        "java.util.function.DoubleConsumer",
+        "java.util.function.DoubleFunction",
+        "java.util.function.DoublePredicate",
+        "java.util.function.DoubleToIntFunction",
+        "java.util.function.DoubleToLongFunction",
+        "java.util.function.DoubleUnaryOperator",
+        "java.util.function.Function",
+        "java.util.function.IntBinaryOperator",
+        "java.util.function.IntConsumer",
+        "java.util.function.IntFunction",
+        "java.util.function.IntPredicate",
+        "java.util.function.IntToDoubleFunction",
+        "java.util.function.IntToLongFunction",
+        "java.util.function.IntUnaryOperator",
+        "java.util.function.LongBinaryOperator",
+        "java.util.function.LongConsumer",
+        "java.util.function.LongFunction",
+        "java.util.function.LongPredicate",
+        "java.util.function.LongToDoubleFunction",
+        "java.util.function.LongToIntFunction",
+        "java.util.function.LongUnaryOperator",
+        "java.util.function.ObjDoubleConsumer",
+        "java.util.function.ObjIntConsumer",
+        "java.util.function.ObjLongConsumer",
+        "java.util.function.Predicate",
+        "java.util.function.Supplier",
+        "java.util.function.ToDoubleFunction",
+        "java.util.function.ToIntFunction",
+        "java.util.function.ToLongFunction",
+        "java.util.function.UnaryOperator",
+        "java.util.stream.BaseStream",
+        "java.util.stream.Collector",
+        "java.util.stream.DoubleStream",
+        "java.util.stream.IntStream",
+        "java.util.stream.LongStream",
+        "java.util.stream.Stream"
+      ]
+    }
+  ],
   "library_flags": [
     {
       "api_level_below_or_equal": 25,
@@ -22,6 +85,14 @@
       "retarget_lib_member": {
         "java.util.Date#toInstant": "java.util.DesugarDate",
         "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar"
+      },
+      "custom_conversion": {
+        "java.time.ZonedDateTime": "java.time.TimeConversions",
+        "java.time.LocalDate": "java.time.TimeConversions",
+        "java.time.Duration": "java.time.TimeConversions",
+        "java.time.ZoneId": "java.time.TimeConversions",
+        "java.time.MonthDay": "java.time.TimeConversions",
+        "java.time.Instant": "java.time.TimeConversions"
       }
     },
     {
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 7d5b31e..dcf33f1 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -69,7 +69,7 @@
 
   private final Set<DexMethod> parallelMethods = Sets.newIdentityHashSet();
 
-  private GenerateLintFiles(String desugarConfigurationPath, String outputDirectory) {
+  public GenerateLintFiles(String desugarConfigurationPath, String outputDirectory) {
     this.desugaredLibraryConfiguration =
         readDesugaredLibraryConfiguration(desugarConfigurationPath);
     this.outputDirectory =
@@ -356,20 +356,24 @@
         apiLevel >= desugaredLibraryConfiguration.getRequiredCompilationApiLevel().getLevel();
         apiLevel--) {
       System.out.println("Generating lint files for compile API " + apiLevel);
-      generateLintFiles(
-          AndroidApiLevel.getAndroidApiLevel(apiLevel),
-          minApiLevel -> minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B,
-          (minApiLevel, method) -> {
-            assert minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B;
-            if (minApiLevel == AndroidApiLevel.L) {
-              return true;
-            }
-            assert minApiLevel == AndroidApiLevel.B;
-            return !parallelMethods.contains(method.method);
-          });
+      run(apiLevel);
     }
   }
 
+  public void run(int apiLevel) throws Exception {
+    generateLintFiles(
+        AndroidApiLevel.getAndroidApiLevel(apiLevel),
+        minApiLevel -> minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B,
+        (minApiLevel, method) -> {
+          assert minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B;
+          if (minApiLevel == AndroidApiLevel.L) {
+            return true;
+          }
+          assert minApiLevel == AndroidApiLevel.B;
+          return !parallelMethods.contains(method.method);
+        });
+  }
+
   public static void main(String[] args) throws Exception {
     if (args.length != 2) {
       System.out.println("Usage: GenerateLineFiles <desuage configuration> <output directory>");
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 4bef8cec..4b35d17 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.LazyLoadedDexApplication;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
 import com.android.tools.r8.jar.CfApplicationWriter;
@@ -118,11 +119,13 @@
       // on it.
       options.enableLoadStoreOptimization = false;
 
-      DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
+      LazyLoadedDexApplication lazyApp =
+          new ApplicationReader(inputApp, options, timing).read(executor);
+
       PrefixRewritingMapper rewritePrefix =
           options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options);
 
-      app = new L8TreePruner(options).prune(app, rewritePrefix);
+      DexApplication app = new L8TreePruner(options).prune(lazyApp, rewritePrefix);
       AppInfo appInfo = new AppInfo(app);
 
       AppView<?> appView = AppView.createForL8(appInfo, options, rewritePrefix);
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 9b0f9de..e116676 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -290,7 +290,7 @@
 
       if (isShrinking()) {
         R8Command.Builder r8Builder =
-            R8Command.builder(getReporter())
+            new CompatProguardCommandBuilder(true, getReporter())
                 .addProgramResourceProvider(desugaredLibrary)
                 .setSynthesizedClassesPrefix(
                     libraryConfiguration.getSynthesizedLibraryClassesPackagePrefix())
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8f6bcca..5998450 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -51,6 +51,7 @@
 import com.android.tools.r8.ir.optimize.enums.EnumValueInfoMapCollector;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.jar.CfApplicationWriter;
+import com.android.tools.r8.kotlin.KotlinMetadataRewriter;
 import com.android.tools.r8.kotlin.KotlinMetadataUtils;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.Minifier;
@@ -805,6 +806,10 @@
         namingLens = NamingLens.getIdentityLens();
       }
 
+      timing.begin("MinifyKotlinMetadata");
+      new KotlinMetadataRewriter(appView, namingLens).run(executorService);
+      timing.end();
+
       timing.begin("Line number remapping");
       // When line number optimization is turned off the identity mapping for line numbers is
       // used. We still run the line number optimizer to collect line numbers and inline frame
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java
index 57151fa..f472800 100644
--- a/src/main/java/com/android/tools/r8/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -38,6 +38,11 @@
   }
 
   @Override
+  public CheckCast asCheckCast() {
+    return this;
+  }
+
+  @Override
   public boolean isCheckCast() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/code/InstanceOf.java b/src/main/java/com/android/tools/r8/code/InstanceOf.java
index 11b2d5c..4550406 100644
--- a/src/main/java/com/android/tools/r8/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/code/InstanceOf.java
@@ -23,6 +23,16 @@
   }
 
   @Override
+  public InstanceOf asInstanceOf() {
+    return this;
+  }
+
+  @Override
+  public boolean isInstanceOf() {
+    return true;
+  }
+
+  @Override
   public String getName() {
     return NAME;
   }
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index b6b1f9c..86abac6 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -127,10 +127,22 @@
     this.offset = offset;
   }
 
+  public CheckCast asCheckCast() {
+    return null;
+  }
+
   public boolean isCheckCast() {
     return false;
   }
 
+  public InstanceOf asInstanceOf() {
+    return null;
+  }
+
+  public boolean isInstanceOf() {
+    return false;
+  }
+
   public ConstString asConstString() {
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index ede2193..ca70ab0 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -74,11 +74,11 @@
     this.inputApp = inputApp;
   }
 
-  public DexApplication read() throws IOException {
+  public LazyLoadedDexApplication read() throws IOException {
     return read((StringResource) null);
   }
 
-  public DexApplication read(StringResource proguardMap) throws IOException {
+  public LazyLoadedDexApplication read(StringResource proguardMap) throws IOException {
     ExecutorService executor = Executors.newSingleThreadExecutor();
     try {
       return read(proguardMap, executor);
@@ -87,20 +87,20 @@
     }
   }
 
-  public final DexApplication read(ExecutorService executorService) throws IOException {
+  public final LazyLoadedDexApplication read(ExecutorService executorService) throws IOException {
     return read(
         null, executorService, ProgramClassCollection.defaultConflictResolver(options.reporter));
   }
 
-  public final DexApplication read(StringResource proguardMap, ExecutorService executorService)
-      throws IOException {
+  public final LazyLoadedDexApplication read(
+      StringResource proguardMap, ExecutorService executorService) throws IOException {
     return read(
         proguardMap,
         executorService,
         ProgramClassCollection.defaultConflictResolver(options.reporter));
   }
 
-  public final DexApplication read(
+  public final LazyLoadedDexApplication read(
       StringResource proguardMap,
       ExecutorService executorService,
       ProgramClassConflictResolver resolver)
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index f327f21..9eb7caa 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -64,7 +64,14 @@
     }
 
     private boolean shouldKeep(DexType type) {
-      return namingLens.prefixRewrittenType(type) != null || potentialTypesToKeep.contains(type);
+      return namingLens.prefixRewrittenType(type) != null
+          || potentialTypesToKeep.contains(type)
+          // TODO(b/158632510): This should prefix match on DexString.
+          || type.toDescriptorString()
+              .startsWith(
+                  "L"
+                      + options.desugaredLibraryConfiguration
+                          .getSynthesizedLibraryClassesPackagePrefix());
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
index ef1b81b..037d769 100644
--- a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
+++ b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
@@ -112,6 +112,10 @@
         desugaredLibraryCodeToKeep.recordMethod(method);
       } else if (insn.isConstClass()) {
         desugaredLibraryCodeToKeep.recordClass(insn.asConstClass().getType());
+      } else if (insn.isInstanceOf()) {
+        desugaredLibraryCodeToKeep.recordClass(insn.asInstanceOf().getType());
+      } else if (insn.isCheckCast()) {
+        desugaredLibraryCodeToKeep.recordClass(insn.asCheckCast().getType());
       }
       insn.write(shortBuffer, mapping);
     }
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 867f32d..0047bf8 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -519,6 +519,7 @@
         for (TypeAddrPair pair : handler.pairs) {
           dest.putUleb128(mapping.getOffsetFor(pair.type));
           dest.putUleb128(pair.addr);
+          desugaredLibraryCodeToKeep.recordClass(pair.type);
         }
         if (hasCatchAll) {
           dest.putUleb128(handler.catchAllAddr);
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index a6a9471..a0a1b99 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -3,20 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import static kotlinx.metadata.FlagsKt.flagsOf;
-
 import com.android.tools.r8.dex.Constants;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BooleanSupplier;
-import kotlinx.metadata.Flag;
 
 /** Access flags common to classes, methods and fields. */
 public abstract class AccessFlags<T extends AccessFlags<T>> {
 
-  protected Flag[] EMPTY_FLAG = {};
-
   protected static final int BASE_FLAGS
       = Constants.ACC_PUBLIC
       | Constants.ACC_PRIVATE
@@ -71,26 +65,6 @@
 
   public abstract int getAsDexAccessFlags();
 
-  public int getAsKotlinFlags() {
-    List<Flag> flags = new ArrayList<>();
-    if (isPrivate()) {
-      flags.add(Flag.IS_PRIVATE);
-    }
-    if (isProtected()) {
-      flags.add(Flag.IS_PROTECTED);
-    }
-    if (isPublic()) {
-      flags.add(Flag.IS_PUBLIC);
-    }
-    if (isFinal()) {
-      flags.add(Flag.IS_FINAL);
-    }
-    if (isOpen()) {
-      flags.add(Flag.IS_OPEN);
-    }
-    return flagsOf(flags.toArray(EMPTY_FLAG));
-  }
-
   public final int getOriginalAccessFlags() {
     return originalFlags;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index e33792c..4dcc108 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -3,14 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import static kotlinx.metadata.FlagsKt.flagsOf;
-
 import com.android.tools.r8.dex.Constants;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BooleanSupplier;
-import kotlinx.metadata.Flag;
 
 public class ClassAccessFlags extends AccessFlags<ClassAccessFlags> {
 
@@ -102,28 +98,6 @@
     return flags;
   }
 
-  @Override
-  public int getAsKotlinFlags() {
-    int flag = super.getAsKotlinFlags();
-    List<Flag> flags = new ArrayList<>();
-    if (isAbstract()) {
-      flags.add(Flag.IS_ABSTRACT);
-    }
-    if (isClass()) {
-      flags.add(Flag.Class.IS_CLASS);
-    }
-    if (isInterface()) {
-      flags.add(Flag.Class.IS_INTERFACE);
-    }
-    if (isAnnotation()) {
-      flags.add(Flag.Class.IS_ANNOTATION_CLASS);
-    }
-    if (isEnum()) {
-      flags.add(Flag.Class.IS_ENUM_CLASS);
-    }
-    return flag | flagsOf(flags.toArray(EMPTY_FLAG));
-  }
-
   /**
    * Checks whether the constraints from
    * https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 74a31bf..5cbe06a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1312,7 +1312,7 @@
       accessFlags = from.accessFlags.copy();
       annotations = from.annotations();
       code = from.code;
-      compilationState = from.compilationState;
+      compilationState = CompilationState.NOT_PROCESSED;
       optimizationInfo = from.optimizationInfo.mutableCopy();
       kotlinMemberInfo = from.kotlinMemberInfo;
       classFileVersion = from.classFileVersion;
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
index 652228c..bb39765 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
@@ -77,11 +77,6 @@
     return materialize();
   }
 
-  @Override
-  public int getAsKotlinFlags() {
-    return super.getAsKotlinFlags();
-  }
-
   public boolean isVolatile() {
     return isSet(Constants.ACC_VOLATILE);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index 9e96ab4..7127794 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -88,6 +88,11 @@
     return programClasses.get(type);
   }
 
+  public DexLibraryClass libraryDefintionFor(DexType type) {
+    assert type.isClassType() : "Cannot lookup library definition for type: " + type;
+    return libraryClasses.get(type);
+  }
+
   static class AllClasses {
 
     // Mapping of all types to their definitions.
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
index c91b62d..8c9c20e 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
@@ -3,14 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import static kotlinx.metadata.FlagsKt.flagsOf;
-
 import com.android.tools.r8.dex.Constants;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BooleanSupplier;
-import kotlinx.metadata.Flag;
 
 public class MethodAccessFlags extends AccessFlags<MethodAccessFlags> {
 
@@ -110,16 +106,6 @@
     return copy.materialize();
   }
 
-  @Override
-  public int getAsKotlinFlags() {
-    int flag = super.getAsKotlinFlags();
-    List<Flag> flags = new ArrayList<>();
-    if (isAbstract()) {
-      flags.add(Flag.IS_ABSTRACT);
-    }
-    return flag | flagsOf(flags.toArray(EMPTY_FLAG));
-  }
-
   public boolean isSynchronized() {
     return isSet(Constants.ACC_SYNCHRONIZED);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
index 96b5de8..1fa68dd 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
@@ -4,28 +4,23 @@
 package com.android.tools.r8.graph.analysis;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClasspathClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode;
-import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import java.util.ArrayList;
 import java.util.IdentityHashMap;
-import java.util.List;
 import java.util.Map;
+import java.util.function.Consumer;
 
 public class DesugaredLibraryConversionWrapperAnalysis extends EnqueuerAnalysis
     implements EnqueuerInvokeAnalysis {
 
   private final AppView<?> appView;
   private final DesugaredLibraryAPIConverter converter;
-  private Map<DexType, DexProgramClass> synthesizedWrappers = new IdentityHashMap<>();
+  private Map<DexType, DexClasspathClass> synthesizedWrappers = new IdentityHashMap<>();
 
   public DesugaredLibraryConversionWrapperAnalysis(AppView<?> appView) {
     this.appView = appView;
@@ -71,34 +66,7 @@
     return converter.generateCallbackMethods();
   }
 
-  public List<DexProgramClass> generateWrappers() {
-    return converter.synthesizeWrappers(synthesizedWrappers);
-  }
-
-  // Generate a mock classpath class for all vivified types.
-  // Types will be available at runtime in the desugared library dex file.
-  public List<DexClasspathClass> generateWrappersSuperTypeMock(List<DexProgramClass> wrappers) {
-    List<DexClasspathClass> classpathClasses = new ArrayList<>();
-    for (DexProgramClass wrapper : wrappers) {
-      boolean mockIsInterface = wrapper.interfaces.size() == 1;
-      DexType mockType = mockIsInterface ? wrapper.interfaces.values[0] : wrapper.superType;
-      if (appView.definitionFor(mockType) == null) {
-        assert DesugaredLibraryAPIConverter.isVivifiedType(mockType);
-        assert wrapper.instanceFields().size() == 1;
-        DexType typeToMock = wrapper.instanceFields().get(0).field.type;
-        DexClass classToMock = appView.definitionFor(typeToMock);
-        assert classToMock != null;
-        DexClasspathClass mockedSuperClass =
-            converter.synthesizeClasspathMock(classToMock, mockType, mockIsInterface);
-        classpathClasses.add(mockedSuperClass);
-        for (DexEncodedMethod virtualMethod : wrapper.virtualMethods()) {
-          // The mock is generated at the end of the enqueuing phase, so we need to manually set the
-          // library override.
-          assert mockedSuperClass.lookupVirtualMethod(virtualMethod.method) != null;
-          virtualMethod.setLibraryMethodOverride(OptionalBool.TRUE);
-        }
-      }
-    }
-    return classpathClasses;
+  public void generateWrappers(Consumer<DexClasspathClass> synthesizedCallback) {
+    converter.synthesizeWrappers(synthesizedWrappers, synthesizedCallback);
   }
 }
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 c53b686..5f6f83b 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
@@ -1195,20 +1195,16 @@
       timing.end();
     }
 
-    if (method.isProcessed()) {
-      // We loose locals information when processing dex code, so if in debug mode only process
-      // synthesized methods.
-      // TODO(b/158818229): Check if the synthesized check is needed.
-      assert !isDebugMode || method.isD8R8Synthesized();
-      assert !appView.enableWholeProgramOptimizations()
-          || !appView.appInfo().withLiveness().neverReprocess.contains(method.method);
-    } else {
-      if (lambdaRewriter != null) {
-        timing.begin("Desugar lambdas");
-        lambdaRewriter.desugarLambdas(code);
-        timing.end();
-        assert code.isConsistentSSA();
-      }
+    assert !method.isProcessed() || !isDebugMode;
+    assert !method.isProcessed()
+        || !appView.enableWholeProgramOptimizations()
+        || !appView.appInfo().withLiveness().neverReprocess.contains(method.method);
+
+    if (!method.isProcessed() && lambdaRewriter != null) {
+      timing.begin("Desugar lambdas");
+      lambdaRewriter.desugarLambdas(code);
+      timing.end();
+      assert code.isConsistentSSA();
     }
 
     if (lambdaMerger != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index a91c1aa..401297e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -45,8 +45,8 @@
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
 
-// TODO(b/134732760): In progress.
 // I convert library calls with desugared parameters/return values so they can work normally.
 // In the JSON of the desugared library, one can specify conversions between desugared and
 // non-desugared types. If no conversion is specified, D8/R8 simply generate wrapper classes around
@@ -287,7 +287,9 @@
     }
     SortedProgramMethodSet callbacks = generateCallbackMethods();
     irConverter.processMethodsConcurrently(callbacks, executorService);
-    wrapperSynthesizor.finalizeWrappersForD8(builder, irConverter, executorService);
+    if (appView.options().isDesugaredLibraryCompilation()) {
+      wrapperSynthesizor.finalizeWrappersForL8(builder, irConverter, executorService);
+    }
   }
 
   public SortedProgramMethodSet generateCallbackMethods() {
@@ -313,14 +315,10 @@
     return allCallbackMethods;
   }
 
-  public List<DexProgramClass> synthesizeWrappers(
-      Map<DexType, DexProgramClass> synthesizedWrappers) {
-    return wrapperSynthesizor.synthesizeWrappers(synthesizedWrappers);
-  }
-
-  public DexClasspathClass synthesizeClasspathMock(
-      DexClass classToMock, DexType mockType, boolean mockIsInterface) {
-    return wrapperSynthesizor.synthesizeClasspathMock(classToMock, mockType, mockIsInterface);
+  public void synthesizeWrappers(
+      Map<DexType, DexClasspathClass> synthesizedWrappers,
+      Consumer<DexClasspathClass> synthesizedCallback) {
+    wrapperSynthesizor.synthesizeWrappersForClasspath(synthesizedWrappers, synthesizedCallback);
   }
 
   private ProgramMethod generateCallbackMethod(
@@ -352,20 +350,22 @@
 
   public void reportInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) {
     DexType desugaredType = appView.rewritePrefix.rewrittenType(type, appView);
-    appView
-        .options()
-        .reporter
-        .info(
-            new StringDiagnostic(
-                "Invoke to "
-                    + invokedMethod.holder
-                    + "#"
-                    + invokedMethod.name
-                    + " may not work correctly at runtime (Cannot convert "
-                    + debugString
-                    + "type "
-                    + desugaredType
-                    + ")."));
+    StringDiagnostic diagnostic =
+        new StringDiagnostic(
+            "Invoke to "
+                + invokedMethod.holder
+                + "#"
+                + invokedMethod.name
+                + " may not work correctly at runtime (Cannot convert "
+                + debugString
+                + "type "
+                + desugaredType
+                + ").");
+    if (appView.options().isDesugaredLibraryCompilation()) {
+      throw appView.options().reporter.fatalError(diagnostic);
+    } else {
+      appView.options().reporter.info(diagnostic);
+    }
   }
 
   public static DexType vivifiedTypeFor(DexType type, AppView<?> appView) {
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 a7d5252..494831e 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
@@ -12,24 +12,32 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
+import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 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.
+  // TODO(b/158632510): should use DexString, DexType, DexMethod or so on when possible.
   private final AndroidApiLevel requiredCompilationAPILevel;
   private final boolean libraryCompilation;
   private final String synthesizedLibraryClassesPackagePrefix;
@@ -41,9 +49,10 @@
   private final Map<DexType, DexType> customConversions;
   private final List<Pair<DexType, DexString>> dontRewriteInvocation;
   private final List<String> extraKeepRules;
+  private final Set<DexType> wrapperConversions;
 
-  public static Builder builder(DexItemFactory dexItemFactory) {
-    return new Builder(dexItemFactory);
+  public static Builder builder(DexItemFactory dexItemFactory, Reporter reporter, Origin origin) {
+    return new Builder(dexItemFactory, reporter, origin);
   }
 
   public static DesugaredLibraryConfiguration withOnlyRewritePrefixForTesting(
@@ -58,6 +67,7 @@
         ImmutableMap.of(),
         ImmutableMap.of(),
         ImmutableMap.of(),
+        ImmutableSet.of(),
         ImmutableList.of(),
         ImmutableList.of());
   }
@@ -73,11 +83,12 @@
         ImmutableMap.of(),
         ImmutableMap.of(),
         ImmutableMap.of(),
+        ImmutableSet.of(),
         ImmutableList.of(),
         ImmutableList.of());
   }
 
-  public DesugaredLibraryConfiguration(
+  private DesugaredLibraryConfiguration(
       AndroidApiLevel requiredCompilationAPILevel,
       boolean libraryCompilation,
       String packagePrefix,
@@ -87,6 +98,7 @@
       Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
       Map<DexType, DexType> backportCoreLibraryMember,
       Map<DexType, DexType> customConversions,
+      Set<DexType> wrapperConversions,
       List<Pair<DexType, DexString>> dontRewriteInvocation,
       List<String> extraKeepRules) {
     this.requiredCompilationAPILevel = requiredCompilationAPILevel;
@@ -98,6 +110,7 @@
     this.retargetCoreLibMember = retargetCoreLibMember;
     this.backportCoreLibraryMember = backportCoreLibraryMember;
     this.customConversions = customConversions;
+    this.wrapperConversions = wrapperConversions;
     this.dontRewriteInvocation = dontRewriteInvocation;
     this.extraKeepRules = extraKeepRules;
   }
@@ -158,6 +171,10 @@
     return customConversions;
   }
 
+  public Set<DexType> getWrapperConversions() {
+    return wrapperConversions;
+  }
+
   public List<Pair<DexType, DexString>> getDontRewriteInvocation() {
     return dontRewriteInvocation;
   }
@@ -169,6 +186,8 @@
   public static class Builder {
 
     private final DexItemFactory factory;
+    private final Reporter reporter;
+    private final Origin origin;
 
     private AndroidApiLevel requiredCompilationAPILevel;
     private boolean libraryCompilation = false;
@@ -176,15 +195,34 @@
         FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX;
     private String identifier;
     private Map<String, String> rewritePrefix = new HashMap<>();
-    private Map<DexType, DexType> emulateLibraryInterface = new HashMap<>();
+    private Map<DexType, DexType> emulateLibraryInterface = new IdentityHashMap<>();
     private Map<DexString, Map<DexType, DexType>> retargetCoreLibMember = new IdentityHashMap<>();
-    private Map<DexType, DexType> backportCoreLibraryMember = new HashMap<>();
-    private Map<DexType, DexType> customConversions = new HashMap<>();
+    private Map<DexType, DexType> backportCoreLibraryMember = new IdentityHashMap<>();
+    private Map<DexType, DexType> customConversions = new IdentityHashMap<>();
+    private Set<DexType> wrapperConversions = Sets.newIdentityHashSet();
     private List<Pair<DexType, DexString>> dontRewriteInvocation = new ArrayList<>();
     private List<String> extraKeepRules = Collections.emptyList();
 
-    public Builder(DexItemFactory dexItemFactory) {
+    private Builder(DexItemFactory dexItemFactory, Reporter reporter, Origin origin) {
       this.factory = dexItemFactory;
+      this.reporter = reporter;
+      this.origin = origin;
+    }
+
+    // Utility to set values. Currently assumes the key is fresh.
+    private <K, V> void put(Map<K, V> map, K key, V value, String desc) {
+      if (map.containsKey(key)) {
+        throw reporter.fatalError(
+            new StringDiagnostic(
+                "Invalid desugared library configuration. "
+                    + " Duplicate assignment of key: '"
+                    + key
+                    + "' in sections for '"
+                    + desc
+                    + "'",
+                origin));
+      }
+      map.put(key, value);
     }
 
     public Builder setSynthesizedLibraryClassesPackagePrefix(String prefix) {
@@ -218,7 +256,11 @@
     }
 
     public Builder putRewritePrefix(String prefix, String rewrittenPrefix) {
-      rewritePrefix.put(prefix, rewrittenPrefix);
+      put(
+          rewritePrefix,
+          prefix,
+          rewrittenPrefix,
+          DesugaredLibraryConfigurationParser.REWRITE_PREFIX_KEY);
       return this;
     }
 
@@ -226,14 +268,28 @@
         String emulateLibraryItf, String rewrittenEmulateLibraryItf) {
       DexType interfaceType = stringClassToDexType(emulateLibraryItf);
       DexType rewrittenType = stringClassToDexType(rewrittenEmulateLibraryItf);
-      emulateLibraryInterface.put(interfaceType, rewrittenType);
+      put(
+          emulateLibraryInterface,
+          interfaceType,
+          rewrittenType,
+          DesugaredLibraryConfigurationParser.EMULATE_INTERFACE_KEY);
       return this;
     }
 
     public Builder putCustomConversion(String type, String conversionHolder) {
       DexType dexType = stringClassToDexType(type);
       DexType conversionType = stringClassToDexType(conversionHolder);
-      customConversions.put(dexType, conversionType);
+      put(
+          customConversions,
+          dexType,
+          conversionType,
+          DesugaredLibraryConfigurationParser.CUSTOM_CONVERSION_KEY);
+      return this;
+    }
+
+    public Builder addWrapperConversion(String type) {
+      DexType dexType = stringClassToDexType(type);
+      wrapperConversions.add(dexType);
       return this;
     }
 
@@ -245,14 +301,22 @@
       DexType originalType = stringClassToDexType(retarget.substring(0, index));
       DexType finalType = stringClassToDexType(rewrittenRetarget);
       assert !typeMap.containsKey(originalType);
-      typeMap.put(originalType, finalType);
+      put(
+          typeMap,
+          originalType,
+          finalType,
+          DesugaredLibraryConfigurationParser.RETARGET_LIB_MEMBER_KEY);
       return this;
     }
 
     public Builder putBackportCoreLibraryMember(String backport, String rewrittenBackport) {
       DexType backportType = stringClassToDexType(backport);
       DexType rewrittenBackportType = stringClassToDexType(rewrittenBackport);
-      backportCoreLibraryMember.put(backportType, rewrittenBackportType);
+      put(
+          backportCoreLibraryMember,
+          backportType,
+          rewrittenBackportType,
+          DesugaredLibraryConfigurationParser.BACKPORT_KEY);
       return this;
     }
 
@@ -279,6 +343,7 @@
     }
 
     public DesugaredLibraryConfiguration build() {
+      validate();
       return new DesugaredLibraryConfiguration(
           requiredCompilationAPILevel,
           libraryCompilation,
@@ -289,8 +354,22 @@
           ImmutableMap.copyOf(retargetCoreLibMember),
           ImmutableMap.copyOf(backportCoreLibraryMember),
           ImmutableMap.copyOf(customConversions),
+          ImmutableSet.copyOf(wrapperConversions),
           ImmutableList.copyOf(dontRewriteInvocation),
           ImmutableList.copyOf(extraKeepRules));
     }
+
+    private void validate() {
+      SetView<DexType> dups = Sets.intersection(customConversions.keySet(), wrapperConversions);
+      if (!dups.isEmpty()) {
+        throw reporter.fatalError(
+            new StringDiagnostic(
+                "Invalid desugared library configuration. "
+                    + "Duplicate types in custom conversions and wrapper conversions: "
+                    + String.join(
+                        ", ", dups.stream().map(DexType::toString).collect(Collectors.toSet())),
+                origin));
+      }
+    }
   }
 }
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 a659cc6..a1af388 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
@@ -6,7 +6,9 @@
 
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.gson.JsonArray;
@@ -19,142 +21,183 @@
 
 public class DesugaredLibraryConfigurationParser {
 
-  private static final int MAX_SUPPORTED_VERSION = 4;
+  public static final int MAX_SUPPORTED_VERSION = 4;
 
-  private final DesugaredLibraryConfiguration.Builder configurationBuilder;
+  private static final String MIN_DESUGARED_LIBRARY_WITH_COMMON_FLAGS = "1.0.9";
+
+  private static final String UNSUPPORTED_MESSAGE =
+      "Unsupported desugared library version, please upgrade the"
+          + " desugared library to at least version "
+          + MIN_DESUGARED_LIBRARY_WITH_COMMON_FLAGS
+          + ".";
+
+  static final String CONFIGURATION_FORMAT_VERSION_KEY = "configuration_format_version";
+  static final String VERSION_KEY = "version";
+  static final String GROUP_ID_KEY = "group_id";
+  static final String ARTIFACT_ID_KEY = "artifact_id";
+  static final String REQUIRED_COMPILATION_API_LEVEL_KEY = "required_compilation_api_level";
+  static final String SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY =
+      "synthesized_library_classes_package_prefix";
+
+  static final String COMMON_FLAGS_KEY = "common_flags";
+  static final String LIBRARY_FLAGS_KEY = "library_flags";
+  static final String PROGRAM_FLAGS_KEY = "program_flags";
+
+  static final String API_LEVEL_BELOW_OR_EQUAL_KEY = "api_level_below_or_equal";
+  static final String WRAPPER_CONVERSION_KEY = "wrapper_conversion";
+  static final String CUSTOM_CONVERSION_KEY = "custom_conversion";
+  static final String REWRITE_PREFIX_KEY = "rewrite_prefix";
+  static final String RETARGET_LIB_MEMBER_KEY = "retarget_lib_member";
+  static final String EMULATE_INTERFACE_KEY = "emulate_interface";
+  static final String DONT_REWRITE_KEY = "dont_rewrite";
+  static final String BACKPORT_KEY = "backport";
+  static final String SHRINKER_CONFIG_KEY = "shrinker_config";
+
+  private final DexItemFactory dexItemFactory;
   private final Reporter reporter;
   private final boolean libraryCompilation;
   private final int minAPILevel;
 
+  private DesugaredLibraryConfiguration.Builder configurationBuilder = null;
+  private Origin origin;
+
   public DesugaredLibraryConfigurationParser(
       DexItemFactory dexItemFactory,
       Reporter reporter,
       boolean libraryCompilation,
       int minAPILevel) {
+    this.dexItemFactory = dexItemFactory;
     this.reporter = reporter;
-    this.configurationBuilder = DesugaredLibraryConfiguration.builder(dexItemFactory);
     this.minAPILevel = minAPILevel;
     this.libraryCompilation = libraryCompilation;
+  }
+
+  private JsonElement required(JsonObject json, String key) {
+    return required(
+        json,
+        key,
+        "Invalid desugared library configuration. " + "Expected required key '" + key + "'");
+  }
+
+  private JsonElement required(JsonObject json, String key, String message) {
+    if (!json.has(key)) {
+      throw reporter.fatalError(new StringDiagnostic(message, origin));
+    }
+    return json.get(key);
+  }
+
+  public DesugaredLibraryConfiguration parse(StringResource stringResource) {
+    origin = stringResource.getOrigin();
+    assert origin != null;
+    configurationBuilder = DesugaredLibraryConfiguration.builder(dexItemFactory, reporter, origin);
     if (libraryCompilation) {
       configurationBuilder.setLibraryCompilation();
     } else {
       configurationBuilder.setProgramCompilation();
     }
-  }
-
-  public DesugaredLibraryConfiguration parse(StringResource stringResource) {
-    String jsonConfigString;
+    JsonObject jsonConfig;
     try {
-      jsonConfigString = stringResource.getString();
+      String jsonConfigString = stringResource.getString();
+      JsonParser parser = new JsonParser();
+      jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
     } catch (Exception e) {
-      throw reporter.fatalError(
-          new StringDiagnostic("Cannot access desugared library configuration."));
+      throw reporter.fatalError(new ExceptionDiagnostic(e, origin));
     }
-    JsonParser parser = new JsonParser();
-    JsonObject jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
 
-    int formatVersion = jsonConfig.get("configuration_format_version").getAsInt();
+    JsonElement formatVersionElement = required(jsonConfig, CONFIGURATION_FORMAT_VERSION_KEY);
+    int formatVersion = formatVersionElement.getAsInt();
     if (formatVersion > MAX_SUPPORTED_VERSION) {
       throw reporter.fatalError(
           new StringDiagnostic(
               "Unsupported desugared library configuration version, please upgrade the D8/R8"
-                  + " compiler."));
-    }
-    if (formatVersion == 1) {
-      reporter.warning(
-          new StringDiagnostic(
-              "You are using an experimental version of the desugared library configuration, "
-                  + "distributed only in the early canary versions. Please update for "
-                  + "production releases and to fix this warning."));
+                  + " compiler.",
+              origin));
     }
 
-    String version = jsonConfig.get("version").getAsString();
-    String groupID;
-    String artifactID;
-    if (formatVersion < 4) {
-      groupID = "com.tools.android";
-      artifactID = "desugar_jdk_libs";
-    } else {
-      groupID = jsonConfig.get("group_id").getAsString();
-      artifactID = jsonConfig.get("artifact_id").getAsString();
-    }
+    String version = required(jsonConfig, VERSION_KEY).getAsString();
+    String groupID = required(jsonConfig, GROUP_ID_KEY).getAsString();
+    String artifactID = required(jsonConfig, ARTIFACT_ID_KEY).getAsString();
     String identifier = String.join(":", groupID, artifactID, version);
     configurationBuilder.setDesugaredLibraryIdentifier(identifier);
+    configurationBuilder.setSynthesizedLibraryClassesPackagePrefix(
+        required(jsonConfig, SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY).getAsString());
 
-    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();
+        required(jsonConfig, REQUIRED_COMPILATION_API_LEVEL_KEY).getAsInt();
     configurationBuilder.setRequiredCompilationAPILevel(
         AndroidApiLevel.getAndroidApiLevel(required_compilation_api_level));
-    JsonArray jsonFlags =
-        libraryCompilation
-            ? jsonConfig.getAsJsonArray("library_flags")
-            : jsonConfig.getAsJsonArray("program_flags");
-    for (JsonElement jsonFlagSet : jsonFlags) {
-      int api_level_below_or_equal =
-          jsonFlagSet.getAsJsonObject().get("api_level_below_or_equal").getAsInt();
-      if (minAPILevel > api_level_below_or_equal) {
-        continue;
-      }
-      parseFlags(jsonFlagSet.getAsJsonObject());
-    }
-    if (jsonConfig.has("shrinker_config")) {
-      JsonArray jsonKeepRules = jsonConfig.get("shrinker_config").getAsJsonArray();
+    JsonElement commonFlags = required(jsonConfig, COMMON_FLAGS_KEY, UNSUPPORTED_MESSAGE);
+    JsonElement libraryFlags = required(jsonConfig, LIBRARY_FLAGS_KEY);
+    JsonElement programFlags = required(jsonConfig, PROGRAM_FLAGS_KEY);
+    parseFlagsList(commonFlags.getAsJsonArray());
+    parseFlagsList(
+        libraryCompilation ? libraryFlags.getAsJsonArray() : programFlags.getAsJsonArray());
+    if (jsonConfig.has(SHRINKER_CONFIG_KEY)) {
+      JsonArray jsonKeepRules = jsonConfig.get(SHRINKER_CONFIG_KEY).getAsJsonArray();
       List<String> extraKeepRules = new ArrayList<>(jsonKeepRules.size());
       for (JsonElement keepRule : jsonKeepRules) {
         extraKeepRules.add(keepRule.getAsString());
       }
       configurationBuilder.setExtraKeepRules(extraKeepRules);
     }
-    return configurationBuilder.build();
+    DesugaredLibraryConfiguration config = configurationBuilder.build();
+    configurationBuilder = null;
+    origin = null;
+    return config;
+  }
+
+  private void parseFlagsList(JsonArray jsonFlags) {
+    for (JsonElement jsonFlagSet : jsonFlags) {
+      JsonObject flag = jsonFlagSet.getAsJsonObject();
+      int api_level_below_or_equal = required(flag, API_LEVEL_BELOW_OR_EQUAL_KEY).getAsInt();
+      if (minAPILevel <= api_level_below_or_equal) {
+        parseFlags(flag);
+      }
+    }
   }
 
   private void parseFlags(JsonObject jsonFlagSet) {
-    if (jsonFlagSet.has("rewrite_prefix")) {
+    if (jsonFlagSet.has(REWRITE_PREFIX_KEY)) {
       for (Map.Entry<String, JsonElement> rewritePrefix :
-          jsonFlagSet.get("rewrite_prefix").getAsJsonObject().entrySet()) {
+          jsonFlagSet.get(REWRITE_PREFIX_KEY).getAsJsonObject().entrySet()) {
         configurationBuilder.putRewritePrefix(
             rewritePrefix.getKey(), rewritePrefix.getValue().getAsString());
       }
     }
-    if (jsonFlagSet.has("retarget_lib_member")) {
+    if (jsonFlagSet.has(RETARGET_LIB_MEMBER_KEY)) {
       for (Map.Entry<String, JsonElement> retarget :
-          jsonFlagSet.get("retarget_lib_member").getAsJsonObject().entrySet()) {
+          jsonFlagSet.get(RETARGET_LIB_MEMBER_KEY).getAsJsonObject().entrySet()) {
         configurationBuilder.putRetargetCoreLibMember(
             retarget.getKey(), retarget.getValue().getAsString());
       }
     }
-    if (jsonFlagSet.has("backport")) {
+    if (jsonFlagSet.has(BACKPORT_KEY)) {
       for (Map.Entry<String, JsonElement> backport :
-          jsonFlagSet.get("backport").getAsJsonObject().entrySet()) {
+          jsonFlagSet.get(BACKPORT_KEY).getAsJsonObject().entrySet()) {
         configurationBuilder.putBackportCoreLibraryMember(
             backport.getKey(), backport.getValue().getAsString());
       }
     }
-    if (jsonFlagSet.has("emulate_interface")) {
+    if (jsonFlagSet.has(EMULATE_INTERFACE_KEY)) {
       for (Map.Entry<String, JsonElement> itf :
-          jsonFlagSet.get("emulate_interface").getAsJsonObject().entrySet()) {
+          jsonFlagSet.get(EMULATE_INTERFACE_KEY).getAsJsonObject().entrySet()) {
         configurationBuilder.putEmulateLibraryInterface(itf.getKey(), itf.getValue().getAsString());
       }
     }
-    if (jsonFlagSet.has("custom_conversion")) {
+    if (jsonFlagSet.has(CUSTOM_CONVERSION_KEY)) {
       for (Map.Entry<String, JsonElement> conversion :
-          jsonFlagSet.get("custom_conversion").getAsJsonObject().entrySet()) {
+          jsonFlagSet.get(CUSTOM_CONVERSION_KEY).getAsJsonObject().entrySet()) {
         configurationBuilder.putCustomConversion(
             conversion.getKey(), conversion.getValue().getAsString());
       }
     }
-    if (jsonFlagSet.has("dont_rewrite")) {
-      JsonArray dontRewrite = jsonFlagSet.get("dont_rewrite").getAsJsonArray();
+    if (jsonFlagSet.has(WRAPPER_CONVERSION_KEY)) {
+      for (JsonElement wrapper : jsonFlagSet.get(WRAPPER_CONVERSION_KEY).getAsJsonArray()) {
+        configurationBuilder.addWrapperConversion(wrapper.getAsString());
+      }
+    }
+    if (jsonFlagSet.has(DONT_REWRITE_KEY)) {
+      JsonArray dontRewrite = jsonFlagSet.get(DONT_REWRITE_KEY).getAsJsonArray();
       for (JsonElement rewrite : dontRewrite) {
         configurationBuilder.addDontRewriteInvocation(rewrite.getAsString());
       }
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 5fbf3df..23a6465 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
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.ClassKind;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
@@ -33,7 +34,6 @@
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperConversionCfCodeProvider;
 import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
@@ -48,6 +48,8 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
 // I am responsible for the generation of wrappers used to call library APIs when desugaring
 // libraries. Wrappers can be both ways, wrapping the desugarType as a type, or the type as
@@ -110,15 +112,19 @@
   private final Set<DexType> invalidWrappers = Sets.newConcurrentHashSet();
   private final DexItemFactory factory;
   private final DesugaredLibraryAPIConverter converter;
-  private final DexString vivifiedSourceFile;
 
   DesugaredLibraryWrapperSynthesizer(AppView<?> appView, DesugaredLibraryAPIConverter converter) {
     this.appView = appView;
     this.factory = appView.dexItemFactory();
-    dexWrapperPrefixString = "L" + appView.options().synthesizedClassPrefix + WRAPPER_PREFIX;
+    dexWrapperPrefixString =
+        "L"
+            + appView
+                .options()
+                .desugaredLibraryConfiguration
+                .getSynthesizedLibraryClassesPackagePrefix()
+            + WRAPPER_PREFIX;
     dexWrapperPrefixDexString = factory.createString(dexWrapperPrefixString);
     this.converter = converter;
-    this.vivifiedSourceFile = appView.dexItemFactory().createString("vivified");
   }
 
   boolean hasSynthesized(DexType type) {
@@ -126,11 +132,7 @@
   }
 
   boolean canGenerateWrapper(DexType type) {
-    DexClass dexClass = appView.definitionFor(type);
-    if (dexClass == null || dexClass.accessFlags.isFinal()) {
-      return false;
-    }
-    return dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation();
+    return appView.options().desugaredLibraryConfiguration.getWrapperConversions().contains(type);
   }
 
   DexType getTypeWrapper(DexType type) {
@@ -155,9 +157,10 @@
     return wrappers.computeIfAbsent(
         type,
         t -> {
+          assert canGenerateWrapper(type) : type;
           DexType wrapperType = createWrapperType(type, suffix);
           assert converter.canGenerateWrappersAndCallbacks()
-                  || appView.definitionForProgramType(wrapperType) != null
+                  || appView.definitionFor(wrapperType).isClasspathClass()
               : "Wrapper " + wrapperType + " should have been generated in the enqueuer.";
           return wrapperType;
         });
@@ -176,10 +179,12 @@
     return DesugaredLibraryAPIConverter.vivifiedTypeFor(type, appView);
   }
 
-  private DexProgramClass generateTypeWrapper(DexClass dexClass, DexType typeWrapperType) {
+  private DexClass generateTypeWrapper(
+      ClassKind classKind, DexClass dexClass, DexType typeWrapperType) {
     DexType type = dexClass.type;
     DexEncodedField wrapperField = synthesizeWrappedValueEncodedField(typeWrapperType, type);
     return synthesizeWrapper(
+        classKind,
         vivifiedTypeFor(type),
         dexClass,
         synthesizeVirtualMethodsForTypeWrapper(dexClass, wrapperField),
@@ -187,12 +192,13 @@
         wrapperField);
   }
 
-  private DexProgramClass generateVivifiedTypeWrapper(
-      DexClass dexClass, DexType vivifiedTypeWrapperType) {
+  private DexClass generateVivifiedTypeWrapper(
+      ClassKind classKind, DexClass dexClass, DexType vivifiedTypeWrapperType) {
     DexType type = dexClass.type;
     DexEncodedField wrapperField =
         synthesizeWrappedValueEncodedField(vivifiedTypeWrapperType, vivifiedTypeFor(type));
     return synthesizeWrapper(
+        classKind,
         type,
         dexClass,
         synthesizeVirtualMethodsForVivifiedTypeWrapper(dexClass, wrapperField),
@@ -200,7 +206,8 @@
         wrapperField);
   }
 
-  private DexProgramClass synthesizeWrapper(
+  private DexClass synthesizeWrapper(
+      ClassKind classKind,
       DexType wrappingType,
       DexClass clazz,
       DexEncodedMethod[] virtualMethods,
@@ -210,9 +217,9 @@
     DexType superType = isItf ? factory.objectType : wrappingType;
     DexTypeList interfaces =
         isItf ? new DexTypeList(new DexType[] {wrappingType}) : DexTypeList.empty();
-    return new DexProgramClass(
+    return classKind.create(
         wrapperField.holder(),
-        null,
+        Kind.CF,
         new SynthesizedOrigin("Desugared library API Converter", getClass()),
         ClassAccessFlags.fromSharedAccessFlags(
             Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
@@ -229,8 +236,7 @@
         new DexEncodedMethod[] {synthesizeConstructor(wrapperField.field), conversionMethod},
         virtualMethods,
         factory.getSkipNameValidationForTesting(),
-        DexProgramClass::checksumFromType,
-        Collections.emptyList());
+        DexProgramClass::checksumFromType);
   }
 
   private DexEncodedMethod[] synthesizeVirtualMethodsForVivifiedTypeWrapper(
@@ -325,51 +331,6 @@
     return finalizeWrapperMethods(generatedMethods, finalMethods);
   }
 
-  private DexEncodedMethod[] synthesizeVirtualMethodsForClasspathMock(
-      DexClass dexClass, DexType mockType) {
-    List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
-    List<DexEncodedMethod> generatedMethods = new ArrayList<>();
-    // Generate only abstract methods for library override detection.
-    for (DexEncodedMethod dexEncodedMethod : dexMethods) {
-      DexClass holderClass = appView.definitionFor(dexEncodedMethod.holder());
-      assert holderClass != null || appView.options().isDesugaredLibraryCompilation();
-      if (!dexEncodedMethod.isFinal()) {
-        DexMethod methodToInstall =
-            DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
-                dexEncodedMethod.method, mockType, appView);
-        DexEncodedMethod newDexEncodedMethod =
-            newSynthesizedMethod(methodToInstall, dexEncodedMethod, null);
-        generatedMethods.add(newDexEncodedMethod);
-      }
-    }
-    return generatedMethods.toArray(DexEncodedMethod.EMPTY_ARRAY);
-  }
-
-  DexClasspathClass synthesizeClasspathMock(
-      DexClass classToMock, DexType mockType, boolean mockIsInterface) {
-    return new DexClasspathClass(
-        mockType,
-        Kind.CF,
-        new SynthesizedOrigin("Desugared library wrapper super class ", getClass()),
-        ClassAccessFlags.fromDexAccessFlags(
-            Constants.ACC_SYNTHETIC
-                | Constants.ACC_PUBLIC
-                | (BooleanUtils.intValue(mockIsInterface) * Constants.ACC_INTERFACE)),
-        appView.dexItemFactory().objectType,
-        DexTypeList.empty(),
-        vivifiedSourceFile,
-        null,
-        Collections.emptyList(),
-        null,
-        Collections.emptyList(),
-        DexAnnotationSet.empty(),
-        DexEncodedField.EMPTY_ARRAY,
-        DexEncodedField.EMPTY_ARRAY,
-        DexEncodedMethod.EMPTY_ARRAY,
-        synthesizeVirtualMethodsForClasspathMock(classToMock, mockType),
-        appView.dexItemFactory().getSkipNameValidationForTesting());
-  }
-
   private DexEncodedMethod[] finalizeWrapperMethods(
       List<DexEncodedMethod> generatedMethods, Set<DexMethod> finalMethods) {
     if (finalMethods.isEmpty()) {
@@ -492,36 +453,69 @@
         true);
   }
 
-  void finalizeWrappersForD8(
+  void finalizeWrappersForL8(
       DexApplication.Builder<?> builder, IRConverter irConverter, ExecutorService executorService)
       throws ExecutionException {
-    List<DexProgramClass> synthesizedWrappers = synthesizeWrappers(new IdentityHashMap<>());
+    List<DexProgramClass> synthesizedWrappers = synthesizeWrappers();
     registerAndProcessWrappers(builder, irConverter, executorService, synthesizedWrappers);
   }
 
-  List<DexProgramClass> synthesizeWrappers(Map<DexType, DexProgramClass> synthesizedWrappers) {
+  private List<DexProgramClass> synthesizeWrappers() {
+    DesugaredLibraryConfiguration conf = appView.options().desugaredLibraryConfiguration;
+    for (DexType type : conf.getWrapperConversions()) {
+      assert !conf.getCustomConversions().containsKey(type);
+      getTypeWrapper(type);
+    }
+    Map<DexType, DexClass> synthesizedWrappers = new IdentityHashMap<>();
     List<DexProgramClass> additions = new ArrayList<>();
-    // Generating a wrapper may require other wrappers to be generated, iterate until fix point.
-    while (synthesizedWrappers.size() != typeWrappers.size() + vivifiedTypeWrappers.size()) {
+    int total = typeWrappers.size() + vivifiedTypeWrappers.size();
+    generateWrappers(
+        ClassKind.PROGRAM,
+        synthesizedWrappers.keySet(),
+        (type, wrapper) -> {
+          synthesizedWrappers.put(type, wrapper);
+          additions.add(wrapper.asProgramClass());
+        });
+    assert total == typeWrappers.size() + vivifiedTypeWrappers.size() : "unexpected additions";
+    return additions;
+  }
+
+  void synthesizeWrappersForClasspath(
+      Map<DexType, DexClasspathClass> synthesizedWrappers,
+      Consumer<DexClasspathClass> synthesizedCallback) {
+    generateWrappers(
+        ClassKind.CLASSPATH,
+        synthesizedWrappers.keySet(),
+        (type, wrapper) -> {
+          DexClasspathClass classpathWrapper = wrapper.asClasspathClass();
+          synthesizedWrappers.put(type, classpathWrapper);
+          synthesizedCallback.accept(classpathWrapper);
+        });
+  }
+
+  private void generateWrappers(
+      ClassKind classKind,
+      Set<DexType> synthesized,
+      BiConsumer<DexType, DexClass> generatedCallback) {
+    while (synthesized.size() != typeWrappers.size() + vivifiedTypeWrappers.size()) {
       for (DexType type : typeWrappers.keySet()) {
         DexType typeWrapperType = typeWrappers.get(type);
-        if (!synthesizedWrappers.containsKey(typeWrapperType)) {
-          DexProgramClass wrapper = generateTypeWrapper(getValidClassToWrap(type), typeWrapperType);
-          synthesizedWrappers.put(typeWrapperType, wrapper);
-          additions.add(wrapper);
+        if (!synthesized.contains(typeWrapperType)) {
+          DexClass wrapper =
+              generateTypeWrapper(classKind, getValidClassToWrap(type), typeWrapperType);
+          generatedCallback.accept(typeWrapperType, wrapper);
         }
       }
       for (DexType type : vivifiedTypeWrappers.keySet()) {
         DexType vivifiedTypeWrapperType = vivifiedTypeWrappers.get(type);
-        if (!synthesizedWrappers.containsKey(vivifiedTypeWrapperType)) {
-          DexProgramClass wrapper =
-              generateVivifiedTypeWrapper(getValidClassToWrap(type), vivifiedTypeWrapperType);
-          synthesizedWrappers.put(vivifiedTypeWrapperType, wrapper);
-          additions.add(wrapper);
+        if (!synthesized.contains(vivifiedTypeWrapperType)) {
+          DexClass wrapper =
+              generateVivifiedTypeWrapper(
+                  classKind, getValidClassToWrap(type), vivifiedTypeWrapperType);
+          generatedCallback.accept(vivifiedTypeWrapperType, wrapper);
         }
       }
     }
-    return additions;
   }
 
   private void registerAndProcessWrappers(
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 73da99f..97dbb2e 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
@@ -8,6 +8,7 @@
 
 import com.android.tools.r8.androidapi.AvailableApiExceptions;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AccessControl;
 import com.android.tools.r8.graph.AccessFlags;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -64,6 +65,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -981,6 +983,16 @@
             continue;
           }
 
+          OptionalBool methodAccessible =
+              AccessControl.isMethodAccessible(
+                  singleTarget.getDefinition(),
+                  singleTarget.getHolder().asDexClass(),
+                  context.getHolder(),
+                  appView.withClassHierarchy().appInfo());
+          if (!methodAccessible.isTrue()) {
+            continue;
+          }
+
           DexEncodedMethod singleTargetMethod = singleTarget.getDefinition();
           WhyAreYouNotInliningReporter whyAreYouNotInliningReporter =
               oracle.isForcedInliningOracle()
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 b3bbbfc..4e52743 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
@@ -9,6 +9,7 @@
 import static com.google.common.base.Predicates.alwaysFalse;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AccessControl;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -58,6 +59,7 @@
 import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
@@ -307,6 +309,13 @@
             return user; // Not eligible.
           }
 
+          OptionalBool methodAccessible =
+              AccessControl.isMethodAccessible(
+                  singleTargetMethod, singleTarget.getHolder().asDexClass(), method, appView);
+
+          if (!methodAccessible.isTrue()) {
+            return user; // Not eligible.
+          }
           // Eligible constructor call (for new instance roots only).
           if (user.isInvokeDirect()) {
             InvokeDirect invoke = user.asInvokeDirect();
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 29250dc..8dbb64a 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
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+import static com.android.tools.r8.utils.PredicateUtils.not;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DebugLocalInfo;
@@ -762,7 +763,8 @@
       if (candidateClass.type != hostType) {
         DexProgramClass hostClass = asProgramClassOrNull(appView.definitionFor(hostType));
         assert hostClass != null;
-        if (!classMembersConflict(candidateClass, hostClass)) {
+        if (!classMembersConflict(candidateClass, hostClass)
+            && !hasMembersNotStaticized(candidateClass, staticizedMethods)) {
           // Move all members of the candidate class into host class.
           moveMembersIntoHost(staticizedMethods,
               candidateClass, hostType, hostClass, methodMapping, fieldMapping);
@@ -783,6 +785,16 @@
         || Streams.stream(a.methods()).anyMatch(method -> b.lookupMethod(method.method) != null);
   }
 
+  private boolean hasMembersNotStaticized(
+      DexProgramClass candidateClass, ProgramMethodSet staticizedMethods) {
+    // TODO(b/159174309): Refine the analysis to allow for fields.
+    if (candidateClass.hasFields()) {
+      return true;
+    }
+    // TODO(b/158018192): Activate again when picking up all references.
+    return candidateClass.methods(not(staticizedMethods::contains)).iterator().hasNext();
+  }
+
   private void moveMembersIntoHost(
       ProgramMethodSet staticizedMethods,
       DexProgramClass candidateClass,
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index b16b8b6..ef44b4b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -9,11 +9,8 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
-import java.util.Map;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -21,17 +18,6 @@
 /** Class provides basic information about symbols related to Kotlin support. */
 public final class Kotlin {
 
-  // Simply "kotlin", but to avoid being renamed by Shadow.relocate in build.gradle.
-  public static final String NAME = String.join("", ImmutableList.of("k", "o", "t", "l", "i", "n"));
-
-  // Simply "Lkotlin/", but to avoid being renamed by Shadow.relocate in build.gradle.
-  private static final String KOTLIN =
-      String.join("", ImmutableList.of("L", "k", "o", "t", "l", "i", "n", "/"));
-
-  static String addKotlinPrefix(String str) {
-    return KOTLIN + str;
-  }
-
   public final DexItemFactory factory;
 
   public final Functional functional;
@@ -39,83 +25,22 @@
   public final Metadata metadata;
   public final _Assertions assertions;
 
+  public static final String NAME = "kotlin";
+  public static final String PACKAGE_PREFIX = "L" + NAME + "/";
+
   public static final class ClassClassifiers {
 
     public static final String arrayBinaryName = NAME + "/Array";
-    public static final String anyDescriptor = "L" + NAME + "/Any;";
+    public static final String anyDescriptor = PACKAGE_PREFIX + "Any;";
     public static final String anyName = NAME + "/Any";
   }
 
-  // Mappings from JVM types to Kotlin types (of type DexType)
-  final Map<DexType, DexType> knownTypeConversion;
-
   public Kotlin(DexItemFactory factory) {
     this.factory = factory;
-
     this.functional = new Functional();
     this.intrinsics = new Intrinsics();
     this.metadata = new Metadata();
     this.assertions = new _Assertions();
-
-    // See {@link org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite}
-    this.knownTypeConversion =
-        ImmutableMap.<DexType, DexType>builder()
-            // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/index.html
-            // Boxed primitives and arrays
-            .put(factory.booleanType, factory.createType(addKotlinPrefix("Boolean;")))
-            .put(factory.boxedBooleanType, factory.createType(addKotlinPrefix("Boolean;")))
-            .put(factory.booleanArrayType, factory.createType(addKotlinPrefix("BooleanArray;")))
-            .put(factory.byteType, factory.createType(addKotlinPrefix("Byte;")))
-            .put(factory.byteArrayType, factory.createType(addKotlinPrefix("ByteArray;")))
-            .put(factory.charType, factory.createType(addKotlinPrefix("Char;")))
-            .put(factory.charArrayType, factory.createType(addKotlinPrefix("CharArray;")))
-            .put(factory.shortType, factory.createType(addKotlinPrefix("Short;")))
-            .put(factory.shortArrayType, factory.createType(addKotlinPrefix("ShortArray;")))
-            .put(factory.intType, factory.createType(addKotlinPrefix("Int;")))
-            .put(factory.boxedIntType, factory.createType(addKotlinPrefix("Int;")))
-            .put(factory.intArrayType, factory.createType(addKotlinPrefix("IntArray;")))
-            .put(factory.longType, factory.createType(addKotlinPrefix("Long;")))
-            .put(factory.longArrayType, factory.createType(addKotlinPrefix("LongArray;")))
-            .put(factory.floatType, factory.createType(addKotlinPrefix("Float;")))
-            .put(factory.floatArrayType, factory.createType(addKotlinPrefix("FloatArray;")))
-            .put(factory.doubleType, factory.createType(addKotlinPrefix("Double;")))
-            .put(factory.doubleArrayType, factory.createType(addKotlinPrefix("DoubleArray;")))
-            // Other intrinsics
-            .put(factory.voidType, factory.createType(addKotlinPrefix("Unit;")))
-            .put(factory.objectType, factory.createType(addKotlinPrefix("Any;")))
-            .put(factory.boxedVoidType, factory.createType(addKotlinPrefix("Nothing;")))
-            .put(factory.stringType, factory.createType(addKotlinPrefix("String;")))
-            .put(factory.charSequenceType, factory.createType(addKotlinPrefix("CharSequence;")))
-            .put(factory.throwableType, factory.createType(addKotlinPrefix("Throwable;")))
-            .put(factory.cloneableType, factory.createType(addKotlinPrefix("Cloneable;")))
-            .put(factory.boxedNumberType, factory.createType(addKotlinPrefix("Number;")))
-            .put(factory.comparableType, factory.createType(addKotlinPrefix("Comparable;")))
-            .put(factory.enumType, factory.createType(addKotlinPrefix("Enum;")))
-            // Collections
-            .put(factory.iteratorType, factory.createType(addKotlinPrefix("collections/Iterator;")))
-            .put(
-                factory.collectionType,
-                factory.createType(addKotlinPrefix("collections/Collection;")))
-            .put(factory.listType, factory.createType(addKotlinPrefix("collections/List;")))
-            .put(factory.setType, factory.createType(addKotlinPrefix("collections/Set;")))
-            .put(factory.mapType, factory.createType(addKotlinPrefix("collections/Map;")))
-            .put(
-                factory.listIteratorType,
-                factory.createType(addKotlinPrefix("collections/ListIterator;")))
-            .put(factory.iterableType, factory.createType(addKotlinPrefix("collections/Iterable;")))
-            .put(
-                factory.mapEntryType, factory.createType(addKotlinPrefix("collections/Map$Entry;")))
-            // .../jvm/functions/FunctionN -> .../FunctionN
-            .putAll(
-                IntStream.rangeClosed(0, 22)
-                    .boxed()
-                    .collect(
-                        Collectors.toMap(
-                            i ->
-                                factory.createType(
-                                    addKotlinPrefix("jvm/functions/Function" + i + ";")),
-                            i -> factory.createType(addKotlinPrefix("Function" + i + ";")))))
-            .build();
   }
 
   public final class Functional {
@@ -129,12 +54,15 @@
     //   > "Error: A JNI error has occurred, please check your installation and try again"
     //
     // This implementation just ignores lambdas with arity > 22.
-    private final Object2IntMap<DexType> functions = new Object2IntArrayMap<>(
-        IntStream.rangeClosed(0, 22).boxed().collect(
-            Collectors.toMap(
-                i -> factory.createType(addKotlinPrefix("jvm/functions/Function") + i + ";"),
-                Function.identity()))
-    );
+    private final Object2IntMap<DexType> functions =
+        new Object2IntArrayMap<>(
+            IntStream.rangeClosed(0, 22)
+                .boxed()
+                .collect(
+                    Collectors.toMap(
+                        i ->
+                            factory.createType(PACKAGE_PREFIX + "jvm/functions/Function" + i + ";"),
+                        Function.identity())));
 
     private Functional() {
     }
@@ -142,8 +70,8 @@
     public final DexString kotlinStyleLambdaInstanceName = factory.createString("INSTANCE");
 
     public final DexType functionBase =
-        factory.createType(addKotlinPrefix("jvm/internal/FunctionBase;"));
-    public final DexType lambdaType = factory.createType(addKotlinPrefix("jvm/internal/Lambda;"));
+        factory.createType(PACKAGE_PREFIX + "jvm/internal/FunctionBase;");
+    public final DexType lambdaType = factory.createType(PACKAGE_PREFIX + "jvm/internal/Lambda;");
 
     public final DexMethod lambdaInitializerMethod = factory.createMethod(
         lambdaType,
@@ -173,7 +101,7 @@
   }
 
   public final class _Assertions {
-    public final DexType type = factory.createType(addKotlinPrefix("_Assertions;"));
+    public final DexType type = factory.createType(PACKAGE_PREFIX + "_Assertions;");
     public final DexString enabledFieldName = factory.createString("ENABLED");
     public final DexField enabledField =
         factory.createField(type, factory.booleanType, enabledFieldName);
@@ -181,7 +109,7 @@
 
   // kotlin.jvm.internal.Intrinsics class
   public final class Intrinsics {
-    public final DexType type = factory.createType(addKotlinPrefix("jvm/internal/Intrinsics;"));
+    public final DexType type = factory.createType(PACKAGE_PREFIX + "jvm/internal/Intrinsics;");
     public final DexMethod throwParameterIsNullException = factory.createMethod(type,
         factory.createProto(factory.voidType, factory.stringType), "throwParameterIsNullException");
     public final DexMethod checkParameterIsNotNull = factory.createMethod(type,
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
index 6a9b23d..953766c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Box;
 import com.google.common.collect.ImmutableList;
@@ -30,8 +29,7 @@
   private static final Map<String, KotlinAnnotationArgumentInfo> EMPTY_ARGUMENTS =
       ImmutableMap.of();
 
-  abstract KmAnnotationArgument<?> rewrite(
-      AppView<AppInfoWithLiveness> appView, NamingLens namingLens);
+  abstract KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens);
 
   private static KotlinAnnotationArgumentInfo createArgument(
       KmAnnotationArgument<?> arg, DexItemFactory factory) {
@@ -77,7 +75,7 @@
     }
 
     @Override
-    KmAnnotationArgument<?> rewrite(AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens) {
       return new KClassValue(
           value.toRenamedBinaryNameOrDefault(appView, namingLens, ClassClassifiers.anyName));
     }
@@ -105,7 +103,7 @@
     }
 
     @Override
-    KmAnnotationArgument<?> rewrite(AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens) {
       return new EnumValue(
           enumClassName.toRenamedBinaryNameOrDefault(appView, namingLens, ClassClassifiers.anyName),
           enumEntryName);
@@ -132,7 +130,7 @@
     }
 
     @Override
-    KmAnnotationArgument<?> rewrite(AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens) {
       Box<KmAnnotation> rewrittenAnnotation = new Box<>();
       value.rewrite(rewrittenAnnotation::set, appView, namingLens);
       if (rewrittenAnnotation.isSet()) {
@@ -172,7 +170,7 @@
     }
 
     @Override
-    KmAnnotationArgument<?> rewrite(AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens) {
       List<KmAnnotationArgument<?>> rewrittenArguments = new ArrayList<>();
       for (KotlinAnnotationArgumentInfo kotlinAnnotationArgumentInfo : value) {
         KmAnnotationArgument<?> rewrittenArg =
@@ -203,7 +201,7 @@
     }
 
     @Override
-    KmAnnotationArgument<?> rewrite(AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    KmAnnotationArgument<?> rewrite(AppView<?> appView, NamingLens namingLens) {
       return argument;
     }
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
index fd4d087..45adb46 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
@@ -51,7 +50,7 @@
 
   public void rewrite(
       KmVisitorProviders.KmAnnotationVisitorProvider visitorProvider,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     String renamedDescriptor =
         annotationType.toRenamedDescriptorOrDefault(appView, namingLens, null);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index c32bcc4..24947bf 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
@@ -44,7 +43,6 @@
   private final List<KotlinTypeInfo> superTypes;
   private final List<KotlinTypeReference> sealedSubClasses;
   private final List<KotlinTypeReference> nestedClasses;
-  // TODO(b/154347404): Understand enum entries.
   private final List<String> enumEntries;
   private final KotlinVersionRequirementInfo versionRequirements;
   private final KotlinTypeReference anonymousObjectOrigin;
@@ -201,8 +199,7 @@
   }
 
   @Override
-  public KotlinClassHeader rewrite(
-      DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+  public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
     KmClass kmClass = new KmClass();
     // TODO(b/154348683): Set flags.
     kmClass.setFlags(flags);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
index 1a24001..fdb326e 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import kotlinx.metadata.jvm.KotlinClassHeader;
 
@@ -57,8 +56,7 @@
     return null;
   }
 
-  KotlinClassHeader rewrite(
-      DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens);
+  KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens);
 
   String getPackageName();
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
index ac19e28..c80ed32 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.Reporter;
@@ -47,8 +46,7 @@
     }
   }
 
-  abstract void rewrite(
-      KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens);
+  abstract void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens);
 
   public static class KotlinClassClassifierInfo extends KotlinClassifierInfo {
 
@@ -61,8 +59,7 @@
     }
 
     @Override
-    void rewrite(
-        KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
       String descriptor =
           type.toRenamedDescriptorOrDefault(appView, namingLens, ClassClassifiers.anyDescriptor);
       // For local or anonymous classes, the classifier is prefixed with '.' and inner classes are
@@ -89,8 +86,7 @@
     }
 
     @Override
-    void rewrite(
-        KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
       visitor.visitTypeParameter(typeId);
     }
 
@@ -109,8 +105,7 @@
     }
 
     @Override
-    void rewrite(
-        KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
       visitor.visitTypeAlias(typeAlias);
     }
 
@@ -128,8 +123,7 @@
     }
 
     @Override
-    void rewrite(
-        KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
       visitor.visitClass(classifier);
     }
 
@@ -147,8 +141,7 @@
     }
 
     @Override
-    void rewrite(
-        KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens) {
       visitor.visitTypeAlias(classifier);
     }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
index c28b4a7..2e4ee98 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
 import java.util.List;
 import kotlinx.metadata.KmClass;
@@ -51,10 +50,7 @@
   }
 
   public void rewrite(
-      KmClass kmClass,
-      DexEncodedMethod method,
-      AppView<AppInfoWithLiveness> appView,
-      NamingLens namingLens) {
+      KmClass kmClass, DexEncodedMethod method, AppView<?> appView, NamingLens namingLens) {
     // Note that JvmExtensionsKt.setSignature does not have an overload for KmConstructorVisitor,
     // thus we rely on creating the KmConstructor manually.
     // TODO(b/154348683): Check for special flags to pass in.
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
index 21fb43a..fd0d60d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
@@ -52,7 +51,7 @@
 
   public void rewrite(
       KmVisitorProviders.KmContractVisitorProvider visitorProvider,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     if (this == NO_EFFECT) {
       return;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
index a83f77a..79b4173 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.kotlin.KotlinMetadataUtils.KmPropertyProcessor;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
@@ -155,7 +154,7 @@
       KmVisitorProviders.KmPropertyVisitorProvider propertyProvider,
       KmVisitorProviders.KmTypeAliasVisitorProvider typeAliasProvider,
       DexClass clazz,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     // Type aliases only have a representation here, so we can generate them directly.
     for (KotlinTypeAliasInfo typeAlias : typeAliases) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
index d3ce846..d6664d1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.kotlin.KmVisitorProviders.KmEffectExpressionVisitorProvider;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
@@ -87,9 +86,7 @@
   }
 
   public void rewrite(
-      KmEffectExpressionVisitorProvider provider,
-      AppView<AppInfoWithLiveness> appView,
-      NamingLens namingLens) {
+      KmEffectExpressionVisitorProvider provider, AppView<?> appView, NamingLens namingLens) {
     if (this == NO_EXPRESSION) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
index e38f158..3b1c748 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.kotlin.KmVisitorProviders.KmEffectVisitorProvider;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import java.util.List;
@@ -52,10 +51,7 @@
     conclusion.trace(definitionSupplier);
   }
 
-  void rewrite(
-      KmEffectVisitorProvider visitorProvider,
-      AppView<AppInfoWithLiveness> appView,
-      NamingLens namingLens) {
+  void rewrite(KmEffectVisitorProvider visitorProvider, AppView<?> appView, NamingLens namingLens) {
     KmEffectVisitor kmEffectVisitor = visitorProvider.get(type, invocationKind);
     conclusion.rewrite(kmEffectVisitor::visitConclusionOfConditionalEffect, appView, namingLens);
     for (KotlinEffectExpressionInfo constructorArgument : constructorArguments) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
index 94617e1..886281f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
 import java.util.function.Consumer;
 import kotlinx.metadata.KmPackage;
@@ -53,8 +52,7 @@
   }
 
   @Override
-  public KotlinClassHeader rewrite(
-      DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+  public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
     KotlinClassMetadata.FileFacade.Writer writer = new KotlinClassMetadata.FileFacade.Writer();
     KmPackage kmPackage = new KmPackage();
     packageInfo.rewrite(kmPackage, clazz, appView, namingLens);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
index da2e960..91413c7 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
 import java.util.List;
 import kotlinx.metadata.KmFlexibleTypeUpperBound;
@@ -65,7 +64,7 @@
 
   public void rewrite(
       KmVisitorProviders.KmFlexibleUpperBoundVisitorProvider visitorProvider,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     if (this == NO_FLEXIBLE_UPPER_BOUND) {
       // Nothing to do.
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
index dfcbac5..f5ce062 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
 import java.util.List;
 import kotlinx.metadata.KmFunction;
@@ -110,7 +109,7 @@
   public void rewrite(
       KmVisitorProviders.KmFunctionVisitorProvider visitorProvider,
       DexEncodedMethod method,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     // TODO(b/154348683): Check method for flags to pass in.
     String finalName = this.name;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
index 6daf3d7..974e4a8 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import kotlinx.metadata.jvm.JvmFieldSignature;
 
@@ -38,7 +37,7 @@
   }
 
   public JvmFieldSignature rewrite(
-      DexEncodedField field, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+      DexEncodedField field, AppView<?> appView, NamingLens namingLens) {
     String finalName = name;
     if (field != null) {
       String fieldName = field.field.name.toString();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
index f4674a1..2469a54 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
@@ -73,7 +72,7 @@
   }
 
   public JvmMethodSignature rewrite(
-      DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+      DexEncodedMethod method, AppView<?> appView, NamingLens namingLens) {
     if (invalidDescriptor != null) {
       return new JvmMethodSignature(name, invalidDescriptor);
     }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
index 459599b..cc215d5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import kotlinx.metadata.KmLambda;
@@ -53,7 +52,7 @@
   boolean rewrite(
       KmVisitorProviders.KmLambdaVisitorProvider visitorProvider,
       DexClass clazz,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     if (!hasBacking) {
       function.rewrite(visitorProvider.get()::visitFunction, null, appView, namingLens);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
index cab770f..fca17cf 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.kotlin.KmVisitorProviders.KmPropertyVisitorProvider;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
@@ -54,9 +53,7 @@
   }
 
   public void rewrite(
-      KmPropertyVisitorProvider visitorProvider,
-      AppView<AppInfoWithLiveness> appView,
-      NamingLens namingLens) {
+      KmPropertyVisitorProvider visitorProvider, AppView<?> appView, NamingLens namingLens) {
     for (KotlinPropertyInfo propertyInfo : propertyInfos) {
       propertyInfo.rewrite(visitorProvider, null, null, null, appView, namingLens);
     }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index dfddc1d..d709ea4 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.graph.DexValue.DexValueInt;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ThreadUtils;
 import java.util.ArrayList;
 import java.util.List;
@@ -26,12 +25,12 @@
 
 public class KotlinMetadataRewriter {
 
-  private final AppView<AppInfoWithLiveness> appView;
+  private final AppView<?> appView;
   private final NamingLens lens;
   private final DexItemFactory factory;
   private final Kotlin kotlin;
 
-  public KotlinMetadataRewriter(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+  public KotlinMetadataRewriter(AppView<?> appView, NamingLens lens) {
     this.appView = appView;
     this.lens = lens;
     this.factory = appView.dexItemFactory();
@@ -54,7 +53,8 @@
           }
           if (oldMeta == null
               || kotlinInfo == NO_KOTLIN_INFO
-              || !appView.appInfo().isPinned(clazz.type)) {
+              || (appView.appInfo().hasLiveness()
+                  && !appView.withLiveness().appInfo().isPinned(clazz.type))) {
             // Remove @Metadata in DexAnnotation when there is no kotlin info and the type is not
             // missing.
             if (oldMeta != null) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
index 1ee78a5..6ef7a0d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.shaking.ProguardKeepRule;
@@ -49,8 +48,7 @@
     }
 
     @Override
-    public KotlinClassHeader rewrite(
-        DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+    public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
       throw new Unreachable("Should never be called");
     }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
index 1ccd35f..34c5248 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.List;
@@ -51,8 +50,7 @@
   }
 
   @Override
-  public KotlinClassHeader rewrite(
-      DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+  public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
     KotlinClassMetadata.MultiFileClassFacade.Writer writer =
         new KotlinClassMetadata.MultiFileClassFacade.Writer();
     List<String> partClassNameStrings = new ArrayList<>(partClassNames.size());
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
index 9470082..ca97d0b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
 import java.util.function.Consumer;
 import kotlinx.metadata.KmPackage;
@@ -57,8 +56,7 @@
   }
 
   @Override
-  public KotlinClassHeader rewrite(
-      DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+  public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
     KotlinClassMetadata.MultiFileClassPart.Writer writer =
         new KotlinClassMetadata.MultiFileClassPart.Writer();
     KmPackage kmPackage = new KmPackage();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
index 3fab975..1ac03db 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import java.util.HashMap;
@@ -63,10 +62,7 @@
   }
 
   public void rewrite(
-      KmPackage kmPackage,
-      DexClass clazz,
-      AppView<AppInfoWithLiveness> appView,
-      NamingLens namingLens) {
+      KmPackage kmPackage, DexClass clazz, AppView<?> appView, NamingLens namingLens) {
     containerInfo.rewrite(
         kmPackage::visitFunction,
         kmPackage::visitProperty,
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
index c718992..6fceb06 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
 import java.util.List;
 import kotlinx.metadata.KmProperty;
@@ -146,7 +145,7 @@
       DexEncodedField field,
       DexEncodedMethod getter,
       DexEncodedMethod setter,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     // TODO(b/154348683): Flags again.
     KmPropertyVisitor kmProperty = visitorProvider.get(flags, name, getterFlags, setterFlags);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
index 5af2a4c..79054f4 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
 import kotlinx.metadata.KmLambda;
 import kotlinx.metadata.jvm.KotlinClassHeader;
@@ -78,8 +77,7 @@
   }
 
   @Override
-  public KotlinClassHeader rewrite(
-      DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+  public KotlinClassHeader rewrite(DexClass clazz, AppView<?> appView, NamingLens namingLens) {
     Writer writer = new Writer();
     if (lambda != null) {
       KmLambda kmLambda = new KmLambda();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
index bb75b7f..3ecbc2c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import java.util.List;
@@ -61,7 +60,7 @@
 
   void rewrite(
       KmVisitorProviders.KmTypeAliasVisitorProvider visitorProvider,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     KmTypeAliasVisitor kmTypeAliasVisitor = visitorProvider.get(flags, name);
     underlyingType.rewrite(kmTypeAliasVisitor::visitUnderlyingType, appView, namingLens);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
index f21db98..4e74391 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
@@ -32,7 +31,7 @@
   private final KotlinTypeInfo outerType;
   private final List<KotlinTypeProjectionInfo> arguments;
   private final List<KotlinAnnotationInfo> annotations;
-  private final KotlinFlexibleTypeUpperBoundInfo flexibleTypeUpperBoundInfo;
+  private final KotlinFlexibleTypeUpperBoundInfo flexibleTypeUpperBound;
 
   KotlinTypeInfo(
       int flags,
@@ -41,14 +40,14 @@
       KotlinTypeInfo outerType,
       List<KotlinTypeProjectionInfo> arguments,
       List<KotlinAnnotationInfo> annotations,
-      KotlinFlexibleTypeUpperBoundInfo flexibleTypeUpperBoundInfo) {
+      KotlinFlexibleTypeUpperBoundInfo flexibleTypeUpperBound) {
     this.flags = flags;
     this.classifier = classifier;
     this.abbreviatedType = abbreviatedType;
     this.outerType = outerType;
     this.arguments = arguments;
     this.annotations = annotations;
-    this.flexibleTypeUpperBoundInfo = flexibleTypeUpperBoundInfo;
+    this.flexibleTypeUpperBound = flexibleTypeUpperBound;
   }
 
   static KotlinTypeInfo create(KmType kmType, DexItemFactory factory, Reporter reporter) {
@@ -80,7 +79,7 @@
 
   public void rewrite(
       KmVisitorProviders.KmTypeVisitorProvider visitorProvider,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     // TODO(b/154348683): Check for correct flags
     KmTypeVisitor kmTypeVisitor = visitorProvider.get(flags);
@@ -95,8 +94,7 @@
       argument.rewrite(
           kmTypeVisitor::visitArgument, kmTypeVisitor::visitStarProjection, appView, namingLens);
     }
-    flexibleTypeUpperBoundInfo.rewrite(
-        kmTypeVisitor::visitFlexibleTypeUpperBound, appView, namingLens);
+    flexibleTypeUpperBound.rewrite(kmTypeVisitor::visitFlexibleTypeUpperBound, appView, namingLens);
     if (annotations.isEmpty()) {
       return;
     }
@@ -119,7 +117,7 @@
       outerType.trace(definitionSupplier);
     }
     forEachApply(arguments, argument -> argument::trace, definitionSupplier);
-    flexibleTypeUpperBoundInfo.trace(definitionSupplier);
+    flexibleTypeUpperBound.trace(definitionSupplier);
     forEachApply(annotations, annotation -> annotation::trace, definitionSupplier);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
index 53c2eb2..d8457ed 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
@@ -87,7 +86,7 @@
 
   void rewrite(
       KmVisitorProviders.KmTypeParameterVisitorProvider visitorProvider,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     KmTypeParameterVisitor kmTypeParameterVisitor = visitorProvider.get(flags, name, id, variance);
     for (KotlinTypeInfo originalUpperBound : originalUpperBounds) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
index 9ab3de0..1f2ce01 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import kotlinx.metadata.KmTypeProjection;
@@ -39,7 +38,7 @@
   public void rewrite(
       KmVisitorProviders.KmTypeProjectionVisitorProvider visitorProvider,
       KmVisitorProviders.KmTypeStarProjectionVisitorProvider starProjectionProvider,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     if (isStarProjection()) {
       starProjectionProvider.get();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
index dd2be72..088e7ec 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.DescriptorUtils;
 
@@ -58,7 +57,7 @@
   }
 
   String toRenamedDescriptorOrDefault(
-      AppView<AppInfoWithLiveness> appView, NamingLens namingLens, String defaultValue) {
+      AppView<?> appView, NamingLens namingLens, String defaultValue) {
     if (unknown != null) {
       return unknown;
     }
@@ -66,7 +65,8 @@
     if (!known.isClassType()) {
       return known.descriptor.toString();
     }
-    if (!appView.appInfo().isNonProgramTypeOrLiveProgramType(known)) {
+    if (appView.appInfo().hasLiveness()
+        && !appView.withLiveness().appInfo().isNonProgramTypeOrLiveProgramType(known)) {
       return defaultValue;
     }
     DexString descriptor = namingLens.lookupDescriptor(known);
@@ -77,7 +77,7 @@
   }
 
   String toRenamedBinaryNameOrDefault(
-      AppView<AppInfoWithLiveness> appView, NamingLens namingLens, String defaultValue) {
+      AppView<?> appView, NamingLens namingLens, String defaultValue) {
     if (unknown != null) {
       // Unknown values are always on the input form, so we can just return it.
       return unknown;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
index 83dd5d2..db54d35 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
@@ -69,7 +68,7 @@
 
   void rewrite(
       KmVisitorProviders.KmValueParameterVisitorProvider visitorProvider,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       NamingLens namingLens) {
     KmValueParameterVisitor kmValueParameterVisitor = visitorProvider.get(flags, name);
     type.rewrite(kmValueParameterVisitor::visitType, appView, namingLens);
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 182de8d..2ee7485 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.kotlin.KotlinMetadataRewriter;
 import com.android.tools.r8.naming.ClassNameMinifier.ClassNamingStrategy;
 import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
 import com.android.tools.r8.naming.ClassNameMinifier.PackageNamingStrategy;
@@ -89,10 +88,6 @@
     new IdentifierMinifier(appView, lens).run(executorService);
     timing.end();
 
-    timing.begin("MinifyKotlinMetadata");
-    new KotlinMetadataRewriter(appView, lens).run(executorService);
-    timing.end();
-
     return lens;
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 515d21f..57f63fa 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.kotlin.KotlinMetadataRewriter;
 import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
 import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
@@ -175,10 +174,6 @@
     new IdentifierMinifier(appView, lens).run(executorService);
     timing.end();
 
-    timing.begin("MinifyKotlinMetadata");
-    new KotlinMetadataRewriter(appView, lens).run(executorService);
-    timing.end();
-
     return lens;
   }
 
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 0edcea8..af2b520 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -35,6 +35,13 @@
 @Keep
 public class Retrace {
 
+  // This is a slight modification of the default regular expression shown for proguard retrace
+  // that allow for retracing classes in the form <class>: lorem ipsum...
+  // Seems like Proguard retrace is expecting the form "Caused by: <class>".
+  public static final String DEFAULT_REGULAR_EXPRESSION =
+      "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%s(?::%l)?\\)\\s*(?:~\\[.*\\])?)"
+          + "|(?:(?:(?:%c|.*)?[:\"]\\s+)?%c(?::.*)?)";
+
   public static final String USAGE_MESSAGE =
       StringUtils.lines(
           "Usage: retrace <proguard-map> <stacktrace-file> [--regex <regexp>, --verbose, --info]",
@@ -45,6 +52,7 @@
     Builder builder = RetraceCommand.builder(diagnosticsHandler);
     boolean hasSetProguardMap = false;
     boolean hasSetStackTrace = false;
+    boolean hasSetRegularExpression = false;
     while (context.head() != null) {
       Boolean help = OptionsParsing.tryParseBoolean(context, "--help");
       if (help != null) {
@@ -63,6 +71,7 @@
       String regex = OptionsParsing.tryParseSingle(context, "--regex", "r");
       if (regex != null && !regex.isEmpty()) {
         builder.setRegularExpression(regex);
+        hasSetRegularExpression = true;
         continue;
       }
       if (!hasSetProguardMap) {
@@ -88,6 +97,9 @@
     if (!hasSetStackTrace) {
       builder.setStackTrace(getStackTraceFromStandardInput());
     }
+    if (!hasSetRegularExpression) {
+      builder.setRegularExpression(DEFAULT_REGULAR_EXPRESSION);
+    }
     return builder;
   }
 
@@ -156,13 +168,30 @@
   }
 
   public static void run(String[] args) {
+    // To be compatible with standard retrace and remapper, we translate -arg into --arg.
+    String[] mappedArgs = new String[args.length];
+    boolean printInfo = false;
+    for (int i = 0; i < args.length; i++) {
+      String arg = args[i];
+      if (arg == null || arg.length() < 2) {
+        mappedArgs[i] = arg;
+        continue;
+      }
+      if (arg.charAt(0) == '-' && arg.charAt(1) != '-') {
+        mappedArgs[i] = "-" + arg;
+      } else {
+        mappedArgs[i] = arg;
+      }
+      if (mappedArgs[i].equals("--info")) {
+        printInfo = true;
+      }
+    }
     RetraceDiagnosticsHandler retraceDiagnosticsHandler =
-        new RetraceDiagnosticsHandler(
-            new DiagnosticsHandler() {}, Arrays.asList(args).contains("--info"));
-    Builder builder = parseArguments(args, retraceDiagnosticsHandler);
+        new RetraceDiagnosticsHandler(new DiagnosticsHandler() {}, printInfo);
+    Builder builder = parseArguments(mappedArgs, retraceDiagnosticsHandler);
     if (builder == null) {
       // --help was an argument to list
-      assert Arrays.asList(args).contains("--help");
+      assert Arrays.asList(mappedArgs).contains("--help");
       System.out.print(USAGE_MESSAGE);
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
index b561305..4973190 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
@@ -390,7 +390,8 @@
   }
 
   // TODO(b/145731185): Extend support for identifiers with strings inside back ticks.
-  private static final String javaIdentifierSegment = "[\\p{L}\\p{N}_\\p{Sc}]+";
+  private static final String javaIdentifierSegment =
+      "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
 
   private abstract static class ClassNameGroup extends RegularExpressionGroup {
 
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 76c9e4c..e432e69 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2725,7 +2725,10 @@
     Set<DexType> mainDexTypes = Sets.newIdentityHashSet();
 
     boolean isEmpty() {
-      boolean empty = syntheticInstantiations.isEmpty() && liveMethods.isEmpty();
+      boolean empty =
+          syntheticInstantiations.isEmpty()
+              && liveMethods.isEmpty()
+              && syntheticClasspathClasses.isEmpty();
       assert !empty || (liveMethodsWithKeepActions.isEmpty() && mainDexTypes.isEmpty());
       return empty;
     }
@@ -2787,6 +2790,7 @@
         enqueuer.markMethodAsTargeted(liveMethod, fakeReason);
         enqueuer.enqueueMarkMethodLiveAction(liveMethod, fakeReason);
       }
+      enqueuer.liveNonProgramTypes.addAll(syntheticClasspathClasses.values());
     }
   }
 
@@ -3099,21 +3103,8 @@
     ProgramMethodSet callbacks = desugaredLibraryWrapperAnalysis.generateCallbackMethods();
     callbacks.forEach(additions::addLiveMethod);
 
-    // Generate the wrappers.
-    List<DexProgramClass> wrappers = desugaredLibraryWrapperAnalysis.generateWrappers();
-    for (DexProgramClass wrapper : wrappers) {
-      additions.addInstantiatedClass(wrapper, null, false);
-      // Mark all methods on the wrapper as live and targeted.
-      for (DexEncodedMethod method : wrapper.methods()) {
-        additions.addLiveMethod(new ProgramMethod(wrapper, method));
-      }
-    }
-
-    // Add all vivified types as classpath classes.
-    // They will be available at runtime in the desugared library dex file.
-    desugaredLibraryWrapperAnalysis
-        .generateWrappersSuperTypeMock(wrappers)
-        .forEach(additions::addClasspathClass);
+    // Generate wrappers on classpath so types are defined.
+    desugaredLibraryWrapperAnalysis.generateWrappers(additions::addClasspathClass);
   }
 
   private void rewriteLambdaCallSites(
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 00853e0..8599b02 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -419,7 +419,8 @@
       if (outputMode == OutputMode.DexIndexed) {
         DexIndexedConsumer.ArchiveConsumer.writeResources(
             archive, getDexProgramResourcesForTesting(), getDataEntryResourcesForTesting());
-      } else if (outputMode == OutputMode.DexFilePerClassFile) {
+      } else if (outputMode == OutputMode.DexFilePerClassFile
+          || outputMode == OutputMode.DexFilePerClass) {
         List<ProgramResource> resources = getDexProgramResourcesForTesting();
         DexFilePerClassFileConsumer.ArchiveConsumer.writeResources(
             archive, resources, programResourcesMainDescriptor);
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
index 38cd73e..936395f 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import static com.android.tools.r8.utils.FileUtils.isArchive;
-
 import com.android.tools.r8.CompatProguardCommandBuilder;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
@@ -51,6 +49,14 @@
   private static final List<String> VALID_OPTIONS_WITH_TWO_OPERANDS =
       Arrays.asList("--feature-jar");
 
+  private static boolean FileUtils_isArchive(Path path) {
+    String name = path.getFileName().toString().toLowerCase();
+    return name.endsWith(".apk")
+        || name.endsWith(".jar")
+        || name.endsWith(".zip")
+        || name.endsWith(".aar");
+  }
+
   public static void main(String[] args) throws CompilationFailedException {
     boolean isCompatMode = false;
     OutputMode outputMode = OutputMode.DexIndexed;
@@ -134,7 +140,7 @@
             {
               Path featureIn = Paths.get(firstOperand);
               Path featureOut = Paths.get(secondOperand);
-              if (!isArchive(featureIn)) {
+              if (!FileUtils_isArchive(featureIn)) {
                 throw new IllegalArgumentException(
                     "Expected an archive, got `" + featureIn.toString() + "`.");
               }
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index e1572f5..367092b 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -308,8 +308,7 @@
           // methods, we either did not rename them, we renamed them according to a supplied map or
           // they may be bridges for interface methods with covariant return types.
           sortMethods(methods);
-          // TODO(b/149360203): Reenable assert.
-          assert true || verifyMethodsAreKeptDirectlyOrIndirectly(appView, methods);
+          assert verifyMethodsAreKeptDirectlyOrIndirectly(appView, methods);
         }
 
         boolean identityMapping =
@@ -450,12 +449,15 @@
       return true;
     }
     RootSet rootSet = appView.rootSet();
+    boolean allSeenAreInstanceInitializers = true;
     DexString originalName = null;
     for (DexEncodedMethod method : methods) {
       // We cannot rename instance initializers.
       if (method.isInstanceInitializer()) {
+        assert allSeenAreInstanceInitializers;
         continue;
       }
+      allSeenAreInstanceInitializers = false;
       // If the method is pinned, we cannot minify it.
       if (rootSet.mayNotBeMinified(method.method, appView)) {
         continue;
@@ -474,7 +476,8 @@
       }
       String errorString = method.method.qualifiedName() + " is not kept but is overloaded";
       assert lookupResult.getHolder().isInterface() : errorString;
-      assert originalName == null || originalName.equals(method.method.name) : errorString;
+      // TODO(b/159113601): Reenable assert.
+      assert true || originalName == null || originalName.equals(method.method.name) : errorString;
       originalName = method.method.name;
     }
     return true;
diff --git a/src/main/keep.txt b/src/main/keep.txt
index 7791d4e..84423da 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -10,6 +10,7 @@
 -keep public class com.android.tools.r8.ExtractMarker { public static void main(java.lang.String[]); }
 -keep public class com.android.tools.r8.dexsplitter.DexSplitter { public static void main(java.lang.String[]); }
 
+-keep public class com.android.tools.r8.Version { public static final java.lang.String LABEL; }
 -keep public class com.android.tools.r8.Version { public static java.lang.String getVersionString(); }
 -keep public class com.android.tools.r8.Version { public static int getMajorVersion(); }
 -keep public class com.android.tools.r8.Version { public static int getMinorVersion(); }
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
index 8f57473..febd5e6 100644
--- a/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
@@ -25,6 +25,8 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -409,8 +411,9 @@
     try {
       R8.run(
           R8Command.builder(handler)
+              .setDisableTreeShaking(true)
               .setMinApiLevel(minApiLevel)
-              .setProgramConsumer(new R8ApiUsageSample.EnsureOutputConsumer())
+              .setProgramConsumer(new EnsureOutputConsumer())
               .addLibraryFiles(libraries)
               .addProgramFiles(inputs)
               .addAssertionsConfiguration(b -> b.setScopeAll().setEnable().build())
@@ -558,6 +561,26 @@
   }
 
   private static void checkVersionApi() {
+    String labelValue;
+    int labelAccess;
+    try {
+      Field field = Version.class.getDeclaredField("LABEL");
+      labelAccess = field.getModifiers();
+      labelValue = (String) field.get(Version.class);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+    if (!Modifier.isPublic(labelAccess)
+        || !Modifier.isStatic(labelAccess)
+        || !Modifier.isFinal(labelAccess)) {
+      throw new RuntimeException("Expected public static final LABEL");
+    }
+    if (labelValue.isEmpty()) {
+      throw new RuntimeException("Expected LABEL constant");
+    }
+    if (Version.LABEL.isEmpty()) {
+      throw new RuntimeException("Expected LABEL constant");
+    }
     if (Version.getVersionString() == null) {
       throw new RuntimeException("Expected getVersionString API");
     }
diff --git a/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
index c5e44a3..846bc6b 100644
--- a/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
+++ b/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
@@ -4,7 +4,9 @@
 package com.android.tools.r8;
 
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
@@ -16,18 +18,26 @@
 import java.util.List;
 import java.util.stream.Collectors;
 import org.junit.Assert;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
-public class D8ApiBinaryCompatibilityTests {
+@RunWith(Parameterized.class)
+public class D8ApiBinaryCompatibilityTests extends TestBase {
 
-  @Rule
-  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public D8ApiBinaryCompatibilityTests(TestParameters parameters) {
+    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+  }
 
   @Test
   public void testCompatibility() throws IOException {
-    Path jar = Paths.get("tests", "d8_api_usage_sample.jar");
+    Path jar = ToolHelper.API_SAMPLE_JAR;
     String main = "com.android.tools.apiusagesample.D8ApiUsageSample";
     int minApiLevel = AndroidApiLevel.K.getLevel();
 
@@ -79,7 +89,7 @@
 
     ProcessBuilder builder = new ProcessBuilder(command);
     ProcessResult result = ToolHelper.runProcess(builder);
-    Assert.assertEquals(result.stderr + "\n" + result.stdout, 0, result.exitCode);
+    assertEquals(result.stderr + "\n" + result.stdout, 0, result.exitCode);
     Assert.assertTrue(result.stdout, result.stdout.isEmpty());
     Assert.assertTrue(result.stderr, result.stderr.isEmpty());
   }
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsMatcher.java b/src/test/java/com/android/tools/r8/DiagnosticsMatcher.java
index 70518d7..a800801 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsMatcher.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsMatcher.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
+import org.hamcrest.CoreMatchers;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
@@ -71,15 +72,20 @@
   }
 
   public static Matcher<Diagnostic> diagnosticPosition(Position position) {
+    return diagnosticPosition(CoreMatchers.equalTo(position));
+  }
+
+  public static Matcher<Diagnostic> diagnosticPosition(Matcher<Position> positionMatcher) {
     return new DiagnosticsMatcher() {
       @Override
       protected boolean eval(Diagnostic diagnostic) {
-        return diagnostic.getPosition().equals(position);
+        return positionMatcher.matches(diagnostic.getPosition());
       }
 
       @Override
       protected void explain(Description description) {
-        description.appendText("position ").appendText(position.getDescription());
+        description.appendText("position ");
+        positionMatcher.describeTo(description);
       }
     };
   }
diff --git a/src/test/java/com/android/tools/r8/PositionMatcher.java b/src/test/java/com/android/tools/r8/PositionMatcher.java
new file mode 100644
index 0000000..4fca1c6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/PositionMatcher.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.position.TextPosition;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public abstract class PositionMatcher extends TypeSafeMatcher<Position> {
+
+  public static Matcher<Position> positionLine(int line) {
+    return new PositionMatcher() {
+      @Override
+      protected boolean matchesSafely(Position position) {
+        return position instanceof TextPosition && ((TextPosition) position).getLine() == line;
+      }
+
+      @Override
+      public void describeTo(Description description) {
+        description.appendText("with line " + line);
+      }
+    };
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
index 79017bc..954d773 100644
--- a/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
+++ b/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
@@ -6,6 +6,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
@@ -16,17 +17,26 @@
 import java.nio.file.Paths;
 import java.util.List;
 import java.util.stream.Collectors;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
-public class R8ApiBinaryCompatibilityTests {
+@RunWith(Parameterized.class)
+public class R8ApiBinaryCompatibilityTests extends TestBase {
 
-  static final Path JAR = Paths.get("tests", "r8_api_usage_sample.jar");
+  static final Path JAR = ToolHelper.API_SAMPLE_JAR;
   static final String MAIN = "com.android.tools.apiusagesample.R8ApiUsageSample";
   static final AndroidApiLevel MIN_API = AndroidApiLevel.K;
 
-  @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public R8ApiBinaryCompatibilityTests(TestParameters parameters) {
+    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+  }
 
   @Test
   public void testCompatibility() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 3d00718..9ad7a70 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -120,6 +120,8 @@
   public static final String JAVA_CLASSES_DIR = BUILD_DIR + "classes/java/";
   public static final String JDK_11_TESTS_CLASSES_DIR = JAVA_CLASSES_DIR + "jdk11Tests/";
 
+  public static final Path API_SAMPLE_JAR = Paths.get("tests", "r8_api_usage_sample.jar");
+
   public static final String LINE_SEPARATOR = StringUtils.LINE_SEPARATOR;
   public static final String CLASSPATH_SEPARATOR = File.pathSeparator;
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BackwardsCompatibleSpecificationTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BackwardsCompatibleSpecificationTest.java
new file mode 100644
index 0000000..54f3acb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BackwardsCompatibleSpecificationTest.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.NoneRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class BackwardsCompatibleSpecificationTest extends TestBase {
+
+  private static final List<String> RELEASES = ImmutableList.of("2.0.74");
+
+  @Parameterized.Parameters(name = "{1}")
+  public static List<Object[]> data() {
+    return buildParameters(getTestParameters().withNoneRuntime().build(), RELEASES);
+  }
+
+  private final Path desugaredLib = ToolHelper.getDesugarJDKLibs();
+  private final Path desugaredSpec = ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING;
+  private final String release;
+
+  public BackwardsCompatibleSpecificationTest(TestParameters parameters, String release) {
+    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+    this.release = release;
+  }
+
+  private Path getReleaseJar() {
+    return Paths.get(ToolHelper.THIRD_PARTY_DIR, "r8-releases", release, "r8lib.jar");
+  }
+
+  @Test
+  public void test() throws Exception {
+    ProcessResult result =
+        ToolHelper.runJava(
+            getReleaseJar(),
+            "com.android.tools.r8.L8",
+            "--desugared-lib",
+            desugaredSpec.toString(),
+            desugaredLib.toString());
+    assertEquals(result.toString(), 0, result.exitCode);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibaryChecksumsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibaryChecksumsTest.java
new file mode 100644
index 0000000..3e819e2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibaryChecksumsTest.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.L8;
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.NoneRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugaredLibaryChecksumsTest extends TestBase {
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public DesugaredLibaryChecksumsTest(TestParameters parameters) {
+    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+  }
+
+  @Test
+  public void test() throws Exception {
+    Path out = temp.newFolder().toPath().resolve("out.jar");
+    L8.run(
+        L8Command.builder()
+            .setIncludeClassesChecksum(true)
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+            .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+            .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+            .setMode(CompilationMode.DEBUG)
+            .addDesugaredLibraryConfiguration(
+                StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+            .setMinApiLevel(AndroidApiLevel.B.getLevel())
+            .setOutput(out, OutputMode.DexIndexed)
+            .build());
+
+    try {
+      CodeInspector inspector = new CodeInspector(out);
+      for (FoundClassSubject clazz : inspector.allClasses()) {
+        assertTrue(clazz.getDexProgramClass().getChecksum() > 0);
+      }
+    } catch (CompilationError e) {
+      // TODO(b/158746302): Desugared library should support checksums.
+      //  also, the failure should have occured in the L8.run above!
+      assertThat(e.getMessage(), containsString("has no checksum"));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
new file mode 100644
index 0000000..92fc7be
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
@@ -0,0 +1,313 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.NoneRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AbortException;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Reporter;
+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.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugaredLibraryConfigurationParsingTest extends TestBase {
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public DesugaredLibraryConfigurationParsingTest(TestParameters parameters) {
+    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+  }
+
+  final AndroidApiLevel minApi = AndroidApiLevel.B;
+  final boolean libraryCompilation = true;
+
+  final DexItemFactory factory = new DexItemFactory();
+  final Origin origin =
+      new Origin(Origin.root()) {
+        @Override
+        public String part() {
+          return "Test Origin";
+        }
+      };
+
+  final Map<String, Object> TEMPLATE =
+      ImmutableMap.<String, Object>builder()
+          .put(
+              "configuration_format_version",
+              DesugaredLibraryConfigurationParser.MAX_SUPPORTED_VERSION)
+          .put("group_id", "com.tools.android")
+          .put("artifact_id", "desugar_jdk_libs")
+          .put("version", "0.0.0")
+          .put("required_compilation_api_level", 1)
+          .put("synthesized_library_classes_package_prefix", "j$.")
+          .put("common_flags", Collections.emptyList())
+          .put("program_flags", Collections.emptyList())
+          .put("library_flags", Collections.emptyList())
+          .build();
+
+  private LinkedHashMap<String, Object> template() {
+    return new LinkedHashMap<>(TEMPLATE);
+  }
+
+  private DesugaredLibraryConfigurationParser parser(DiagnosticsHandler handler) {
+    return new DesugaredLibraryConfigurationParser(
+        factory, new Reporter(handler), libraryCompilation, minApi.getLevel());
+  }
+
+  private DesugaredLibraryConfiguration runPassing(String resource) {
+    return runPassing(StringResource.fromString(resource, origin));
+  }
+
+  private DesugaredLibraryConfiguration runPassing(StringResource resource) {
+    TestDiagnosticMessagesImpl handler = new TestDiagnosticMessagesImpl();
+    DesugaredLibraryConfiguration config = parser(handler).parse(resource);
+    handler.assertNoMessages();
+    return config;
+  }
+
+  private void runFailing(String json, Consumer<TestDiagnosticMessages> checker) {
+    TestDiagnosticMessagesImpl handler = new TestDiagnosticMessagesImpl();
+    try {
+      parser(handler).parse(StringResource.fromString(json, origin));
+      fail("Expected failure");
+    } catch (AbortException e) {
+      checker.accept(handler);
+    }
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    // Just test that the reference file parses without issues.
+    DesugaredLibraryConfiguration config =
+        runPassing(StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING));
+    assertEquals(libraryCompilation, config.isLibraryCompilation());
+  }
+
+  @Test
+  public void testEmpty() {
+    runFailing(
+        "",
+        diagnostics -> {
+          diagnostics.assertErrorsMatch(
+              allOf(
+                  diagnosticMessage(containsString("Not a JSON Object")),
+                  diagnosticOrigin(origin)));
+        });
+  }
+
+  @Test
+  public void testRequiredKeys() {
+    ImmutableList<String> requiredKeys =
+        ImmutableList.of(
+            "configuration_format_version",
+            "group_id",
+            "artifact_id",
+            "version",
+            "required_compilation_api_level",
+            "synthesized_library_classes_package_prefix",
+            "program_flags",
+            "library_flags");
+    for (String key : requiredKeys) {
+      Map<String, Object> data = template();
+      data.remove(key);
+      runFailing(
+          toJson(data),
+          diagnostics ->
+              diagnostics.assertErrorsMatch(
+                  allOf(
+                      diagnosticMessage(containsString("Invalid desugared library configuration")),
+                      diagnosticMessage(containsString("Expected required key '" + key + "'")),
+                      diagnosticOrigin(origin))));
+    }
+  }
+
+  @Test
+  public void testUnsupportedFormatMissingFlags() {
+    LinkedHashMap<String, Object> data = template();
+    data.remove("common_flags");
+    runFailing(
+        toJson(data),
+        diagnostics ->
+            diagnostics.assertErrorsMatch(
+                allOf(
+                    diagnosticMessage(containsString("upgrade the desugared library")),
+                    diagnosticOrigin(origin))));
+  }
+
+  @Test
+  public void testUnsupportedAbove() {
+    LinkedHashMap<String, Object> data = template();
+    data.put("configuration_format_version", 100000);
+    runFailing(
+        toJson(data),
+        diagnostics ->
+            diagnostics.assertErrorsMatch(
+                allOf(
+                    diagnosticMessage(containsString("upgrade the D8/R8 compiler")),
+                    diagnosticOrigin(origin))));
+  }
+
+  @Test
+  public void testCustomAndWrapperOverlap() {
+    LinkedHashMap<String, Object> data = template();
+    data.put(
+        "common_flags",
+        ImmutableList.of(
+            ImmutableMap.of(
+                "api_level_below_or_equal",
+                100000,
+                "custom_conversion",
+                ImmutableMap.of("java.util.Foo", "j$.util.FooConv"),
+                "wrapper_conversion",
+                ImmutableList.of("java.util.Foo"))));
+    runFailing(
+        toJson(data),
+        diagnostics ->
+            diagnostics.assertErrorsMatch(
+                allOf(
+                    diagnosticMessage(containsString("Duplicate types")),
+                    diagnosticMessage(containsString("java.util.Foo")),
+                    diagnosticOrigin(origin))));
+  }
+
+  @Test
+  public void testRedefinition() {
+    LinkedHashMap<String, Object> data = template();
+    data.put(
+        "common_flags",
+        ImmutableList.of(
+            ImmutableMap.of(
+                "api_level_below_or_equal",
+                100000,
+                "custom_conversion",
+                ImmutableMap.of("java.util.Foo", "j$.util.FooConv1"))));
+    data.put(
+        "library_flags",
+        ImmutableList.of(
+            ImmutableMap.of(
+                "api_level_below_or_equal",
+                100000,
+                "custom_conversion",
+                ImmutableMap.of("java.util.Foo", "j$.util.FooConv2"))));
+    runFailing(
+        toJson(data),
+        diagnostics ->
+            diagnostics.assertErrorsMatch(
+                allOf(
+                    diagnosticMessage(containsString("Duplicate assignment of key")),
+                    diagnosticMessage(containsString("java.util.Foo")),
+                    diagnosticMessage(containsString("custom_conversion")),
+                    diagnosticOrigin(origin))));
+  }
+
+  @Test
+  public void testDuplicate() {
+    LinkedHashMap<String, Object> data = template();
+    data.put(
+        "common_flags",
+        ImmutableList.of(
+            ImmutableMap.of(
+                "api_level_below_or_equal",
+                100000,
+                "custom_conversion",
+                ImmutableMap.of(
+                    "java.util.Foo", "j$.util.FooConv1",
+                    "java.util.Foo2", "j$.util.FooConv2"))));
+    // The gson parser will overwrite the key in order during parsing, thus hiding potential issues.
+    DesugaredLibraryConfiguration config = runPassing(toJson(data).replace("Foo2", "Foo"));
+    assertEquals(
+        Collections.singletonList("java.util.Foo"),
+        config.getCustomConversions().keySet().stream()
+            .map(DexType::toString)
+            .collect(Collectors.toList()));
+    assertEquals(
+        Collections.singletonList("j$.util.FooConv2"),
+        config.getCustomConversions().values().stream()
+            .map(DexType::toString)
+            .collect(Collectors.toList()));
+  }
+
+  // JSON building helpers.
+  // This does not use gson to make the text input construction independent of gson.
+
+  private static String toJson(Map<String, Object> data) {
+    StringBuilder builder = new StringBuilder();
+    toJsonObject(data, builder);
+    return builder.toString();
+  }
+
+  private static void toJsonObject(Map<String, Object> data, StringBuilder builder) {
+    StringUtils.append(
+        builder,
+        ListUtils.map(
+            data.entrySet(),
+            entry -> "\n  " + quote(entry.getKey()) + ": " + toJsonElement(entry.getValue())),
+        ", ",
+        BraceType.TUBORG);
+  }
+
+  private static String toJsonElement(Object element) {
+    StringBuilder builder = new StringBuilder();
+    toJsonElement(element, builder);
+    return builder.toString();
+  }
+
+  private static void toJsonElement(Object element, StringBuilder builder) {
+    if (element instanceof String) {
+      builder.append(quote((String) element));
+    } else if (element instanceof Integer) {
+      builder.append(element);
+    } else if (element instanceof List) {
+      toJsonList((List<Object>) element, builder);
+    } else if (element instanceof Map) {
+      toJsonObject((Map<String, Object>) element, builder);
+    } else {
+      throw new IllegalStateException("Unexpected object type: " + element.getClass());
+    }
+  }
+
+  private static void toJsonList(List<Object> element, StringBuilder builder) {
+    StringUtils.append(
+        builder, ListUtils.map(element, o -> "\n  " + toJsonElement(o)), ", ", BraceType.SQUARE);
+  }
+
+  private static String quote(String str) {
+    return "\"" + str + "\"";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java
new file mode 100644
index 0000000..c1c4ce3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.time.LocalDate;
+import java.util.List;
+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 DesugaredLocalDateReflectedTypePassedToStaticType extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+  private static final String EXPECTED = StringUtils.lines("1992");
+
+  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(), BooleanUtils.values());
+  }
+
+  public DesugaredLocalDateReflectedTypePassedToStaticType(
+      TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.parameters = parameters;
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    D8TestRunResult runResult =
+        testForD8()
+            .addInnerClasses(DesugaredLocalDateReflectedTypePassedToStaticType.class)
+            .setMinApi(parameters.getApiLevel())
+            .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+            .setIncludeClassesChecksum(true)
+            .compile()
+            .addDesugaredCoreLibraryRunClassPath(
+                this::buildDesugaredLibrary,
+                parameters.getApiLevel(),
+                keepRuleConsumer.get(),
+                shrinkDesugaredLibrary)
+            .run(parameters.getRuntime(), Main.class);
+    if (shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.O)) {
+      runResult.assertFailureWithErrorThatMatches(
+          containsString("java.lang.NoSuchMethodException"));
+    } else {
+      runResult.assertSuccessWithOutput(EXPECTED);
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    R8TestRunResult runResult =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(DesugaredLocalDateReflectedTypePassedToStaticType.class)
+            .addKeepMainRule(Main.class)
+            .setMinApi(parameters.getApiLevel())
+            .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+            .compile()
+            .addDesugaredCoreLibraryRunClassPath(
+                this::buildDesugaredLibrary,
+                parameters.getApiLevel(),
+                keepRuleConsumer.get(),
+                shrinkDesugaredLibrary)
+            .run(parameters.getRuntime(), Main.class);
+    if (shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.O)) {
+      runResult.assertFailureWithErrorThatMatches(
+          containsString("java.lang.NoSuchMethodException"));
+    } else {
+      runResult.assertSuccessWithOutput(EXPECTED);
+    }
+  }
+
+  public static class Main {
+
+    public static void printYear(LocalDate date) {
+      System.out.println(date.getYear());
+    }
+
+    public static void main(String[] args) throws Exception {
+      LocalDate date =
+          (LocalDate)
+              LocalDate.class
+                  .getMethod("of", int.class, int.class, int.class)
+                  .invoke(null, 1992, 1, 1);
+      printYear(date);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectionTest.java
new file mode 100644
index 0000000..980c9ff
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectionTest.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+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 DesugaredLocalDateReflectionTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+  private static final String EXPECTED = StringUtils.lines("1992");
+
+  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(), BooleanUtils.values());
+  }
+
+  public DesugaredLocalDateReflectionTest(
+      TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.parameters = parameters;
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    D8TestRunResult runResult =
+        testForD8()
+            .addInnerClasses(DesugaredLocalDateReflectionTest.class)
+            .setMinApi(parameters.getApiLevel())
+            .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+            .setIncludeClassesChecksum(true)
+            .compile()
+            .addDesugaredCoreLibraryRunClassPath(
+                this::buildDesugaredLibrary,
+                parameters.getApiLevel(),
+                keepRuleConsumer.get(),
+                shrinkDesugaredLibrary)
+            .run(parameters.getRuntime(), Main.class);
+    if (parameters.getRuntime().asDex().getVm().isNewerThan(DexVm.ART_7_0_0_HOST)) {
+      runResult.assertSuccessWithOutput(EXPECTED);
+    } else {
+      runResult.assertFailureWithErrorThatMatches(
+          containsString("java.lang.ClassNotFoundException: java.time.LocalDate"));
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    R8TestRunResult runResult =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(DesugaredLocalDateReflectionTest.class)
+            .addKeepMainRule(Main.class)
+            .setMinApi(parameters.getApiLevel())
+            .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+            .compile()
+            .addDesugaredCoreLibraryRunClassPath(
+                this::buildDesugaredLibrary,
+                parameters.getApiLevel(),
+                keepRuleConsumer.get(),
+                shrinkDesugaredLibrary)
+            .run(parameters.getRuntime(), Main.class);
+    if (parameters.getRuntime().asDex().getVm().isNewerThan(DexVm.ART_7_0_0_HOST)) {
+      runResult.assertSuccessWithOutput(EXPECTED);
+    } else {
+      runResult.assertFailureWithErrorThatMatches(
+          containsString("java.lang.ClassNotFoundException: java.time.LocalDate"));
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) throws Exception {
+      Class<?> localDateClass =
+          Class.forName(System.nanoTime() > 0 ? "java.time.LocalDate" : "java.lang.Object");
+      Object localDate =
+          localDateClass.getMethod("of", int.class, int.class, int.class).invoke(null, 1992, 1, 1);
+      System.out.println(localDateClass.getMethod("getYear").invoke(localDate));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java
new file mode 100644
index 0000000..f56829c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.time.LocalDate;
+import java.util.List;
+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 DesugaredReflectedDesugaredTypePassedToStaticTypeTest
+    extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+  private static final String EXPECTED = StringUtils.lines("1992", "1992");
+
+  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(), BooleanUtils.values());
+  }
+
+  public DesugaredReflectedDesugaredTypePassedToStaticTypeTest(
+      TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.parameters = parameters;
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    D8TestRunResult runResult =
+        testForD8()
+            .addInnerClasses(DesugaredReflectedDesugaredTypePassedToStaticTypeTest.class)
+            .setMinApi(parameters.getApiLevel())
+            .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+            .setIncludeClassesChecksum(true)
+            .compile()
+            .addDesugaredCoreLibraryRunClassPath(
+                this::buildDesugaredLibrary,
+                parameters.getApiLevel(),
+                keepRuleConsumer.get(),
+                shrinkDesugaredLibrary)
+            .run(parameters.getRuntime(), Main.class);
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)) {
+      runResult.assertFailureWithErrorThatMatches(
+          containsString("java.lang.ClassNotFoundException: j$.time.LocalDate"));
+    } else {
+      runResult.assertSuccessWithOutput(EXPECTED);
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    R8TestRunResult runResult =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(DesugaredReflectedDesugaredTypePassedToStaticTypeTest.class)
+            .addKeepMainRule(Main.class)
+            .setMinApi(parameters.getApiLevel())
+            .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+            .compile()
+            .addDesugaredCoreLibraryRunClassPath(
+                this::buildDesugaredLibrary,
+                parameters.getApiLevel(),
+                keepRuleConsumer.get(),
+                shrinkDesugaredLibrary)
+            .run(parameters.getRuntime(), Main.class);
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)) {
+      runResult.assertFailureWithErrorThatMatches(
+          containsString("java.lang.ClassNotFoundException: j$.time.LocalDate"));
+    } else {
+      runResult.assertSuccessWithOutput(EXPECTED);
+    }
+  }
+
+  public static class Main {
+
+    public static void printYear(LocalDate date) {
+      System.out.println(date.getYear());
+    }
+
+    public static void main(String[] args) throws Exception {
+      printYear(LocalDate.of(1992, 1, 1));
+      printYear(
+          (LocalDate)
+              Class.forName("j$.time.LocalDate")
+                  .getMethod("of", int.class, int.class, int.class)
+                  .invoke(null, 1992, 1, 1));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
new file mode 100644
index 0000000..846b65d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -0,0 +1,304 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.GenerateLintFiles;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.NoneRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ExtractWrapperTypesTest extends TestBase {
+
+  // Filter on types that do not need to be considered for wrapping.
+  private static boolean doesNotNeedWrapper(String type, Set<String> customConversions) {
+    return excludePackage(type)
+        || NOT_NEEDED_NOT_IN_DOCS.contains(type)
+        || FINAL_CLASSES.contains(type)
+        || customConversions.contains(type);
+  }
+
+  private static boolean excludePackage(String type) {
+    return type.startsWith("java.lang.")
+        || type.startsWith("java.nio.")
+        || type.startsWith("java.security.")
+        || type.startsWith("java.net.")
+        || type.startsWith("java.awt.")
+        || type.startsWith("java.util.concurrent.");
+  }
+
+  // Types not picked up by the android.jar scan but for which wrappers are needed.
+  private static final Set<String> ADDITIONAL_WRAPPERS = ImmutableSet.of();
+
+  // Types not in API docs, referenced in android.jar and must be wrapped.
+  private static final Set<String> NEEDED_BUT_NOT_IN_DOCS = ImmutableSet.of();
+
+  // Types not in API docs, referenced in android.jar but need not be wrapped.
+  private static final Set<String> NOT_NEEDED_NOT_IN_DOCS =
+      ImmutableSet.of(
+          "java.util.Base64$Decoder",
+          "java.util.Base64$Encoder",
+          "java.util.Calendar$Builder",
+          "java.util.Locale$Builder",
+          "java.util.Locale$Category",
+          "java.util.Locale$FilteringMode",
+          "java.util.SplittableRandom");
+
+  // List of referenced final classes (cannot be wrapper converted) with no custom conversions.
+  private static final Set<String> FINAL_CLASSES =
+      ImmutableSet.of(
+          // TODO(b/159304624): Does this need custom conversion?
+          "java.time.Period");
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  // TODO: parameterize to check both api<=23 as well as 23<api<26 for which the spec differs.
+  private final AndroidApiLevel minApi = AndroidApiLevel.B;
+  private final AndroidApiLevel targetApi = AndroidApiLevel.Q;
+
+  public ExtractWrapperTypesTest(TestParameters parameters) {
+    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+  }
+
+  @Test
+  public void checkConsistency() {
+    List<Set<String>> sets =
+        ImmutableList.of(
+            ADDITIONAL_WRAPPERS, NEEDED_BUT_NOT_IN_DOCS, NOT_NEEDED_NOT_IN_DOCS, FINAL_CLASSES);
+    for (Set<String> set1 : sets) {
+      for (Set<String> set2 : sets) {
+        if (set1 != set2) {
+          assertEquals(Collections.emptySet(), Sets.intersection(set1, set2));
+        }
+      }
+    }
+    for (Set<String> set : sets) {
+      for (String type : set) {
+        assertFalse(excludePackage(type));
+      }
+    }
+  }
+
+  @Test
+  public void test() throws Exception {
+    CodeInspector desugaredApiJar = getDesugaredApiJar();
+    Set<ClassReference> preDesugarTypes = getPreDesugarTypes();
+
+    DesugaredLibraryConfiguration conf = getDesugaredLibraryConfiguration();
+    Set<String> wrappersInSpec =
+        conf.getWrapperConversions().stream().map(DexType::toString).collect(Collectors.toSet());
+    Set<String> customConversionsInSpec =
+        conf.getCustomConversions().keySet().stream()
+            .map(DexType::toString)
+            .collect(Collectors.toSet());
+    assertEquals(
+        Collections.emptySet(), Sets.intersection(wrappersInSpec, customConversionsInSpec));
+    assertEquals(Collections.emptySet(), Sets.intersection(FINAL_CLASSES, customConversionsInSpec));
+
+    CodeInspector nonDesugaredJar = new CodeInspector(ToolHelper.getAndroidJar(targetApi));
+    Map<ClassReference, Set<MethodReference>> directWrappers =
+        getDirectlyReferencedWrapperTypes(
+            desugaredApiJar, preDesugarTypes, nonDesugaredJar, customConversionsInSpec);
+    Map<ClassReference, Set<ClassReference>> indirectWrappers =
+        getIndirectlyReferencedWrapperTypes(
+            directWrappers, preDesugarTypes, nonDesugaredJar, customConversionsInSpec);
+
+    {
+      Set<String> missingWrappers = getMissingWrappers(directWrappers, wrappersInSpec);
+      assertTrue(
+          "Missing direct wrappers:\n" + String.join("\n", missingWrappers),
+          missingWrappers.isEmpty());
+    }
+
+    {
+      Set<String> missingWrappers = getMissingWrappers(indirectWrappers, wrappersInSpec);
+      assertTrue(
+          "Missing indirect wrappers:\n" + String.join("\n", missingWrappers),
+          missingWrappers.isEmpty());
+    }
+
+    Set<String> additionalWrappers = new TreeSet<>();
+    for (String wrapper : wrappersInSpec) {
+      ClassReference item = Reference.classFromTypeName(wrapper);
+      if (!directWrappers.containsKey(item)
+          && !indirectWrappers.containsKey(item)
+          && !ADDITIONAL_WRAPPERS.contains(wrapper)) {
+        additionalWrappers.add(wrapper);
+      }
+    }
+    assertTrue(
+        "Additional wrapper:\n" + String.join("\n", additionalWrappers),
+        additionalWrappers.isEmpty());
+
+    assertEquals(
+        directWrappers.size() + indirectWrappers.size() + ADDITIONAL_WRAPPERS.size(),
+        wrappersInSpec.size());
+  }
+
+  private static <T> Set<String> getMissingWrappers(
+      Map<ClassReference, Set<T>> expected, Set<String> wrappersInSpec) {
+    Set<String> missingWrappers = new TreeSet<>();
+    for (ClassReference addition : expected.keySet()) {
+      String item = descriptorToJavaType(addition.getDescriptor());
+      if (!wrappersInSpec.contains(item)) {
+        missingWrappers.add(item + " referenced from: " + expected.get(addition));
+      }
+    }
+    return missingWrappers;
+  }
+
+  private DesugaredLibraryConfiguration getDesugaredLibraryConfiguration() {
+    DesugaredLibraryConfigurationParser parser =
+        new DesugaredLibraryConfigurationParser(
+            new DexItemFactory(), null, true, minApi.getLevel());
+    return parser.parse(StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING));
+  }
+
+  private Map<ClassReference, Set<MethodReference>> getDirectlyReferencedWrapperTypes(
+      CodeInspector desugaredApiJar,
+      Set<ClassReference> preDesugarTypes,
+      CodeInspector nonDesugaredJar,
+      Set<String> customConversions) {
+    Map<ClassReference, Set<MethodReference>> directWrappers = new HashMap<>();
+    nonDesugaredJar.forAllClasses(
+        clazz -> {
+          clazz.forAllMethods(
+              method -> {
+                if (!method.isPublic() && !method.isProtected()) {
+                  return;
+                }
+                if (desugaredApiJar.method(method.asMethodReference()).isPresent()) {
+                  return;
+                }
+                Consumer<ClassReference> adder =
+                    t ->
+                        directWrappers
+                            .computeIfAbsent(t, k -> new HashSet<>())
+                            .add(method.asMethodReference());
+                MethodSignature signature = method.getFinalSignature().asMethodSignature();
+                addType(adder, signature.type, preDesugarTypes, customConversions);
+                for (String parameter : signature.parameters) {
+                  addType(adder, parameter, preDesugarTypes, customConversions);
+                }
+              });
+        });
+    return directWrappers;
+  }
+
+  private Map<ClassReference, Set<ClassReference>> getIndirectlyReferencedWrapperTypes(
+      Map<ClassReference, Set<MethodReference>> directWrappers,
+      Set<ClassReference> existing,
+      CodeInspector latest,
+      Set<String> customConversions) {
+    Map<ClassReference, Set<ClassReference>> indirectWrappers = new HashMap<>();
+    WorkList<ClassReference> worklist = WorkList.newEqualityWorkList(directWrappers.keySet());
+    while (worklist.hasNext()) {
+      ClassReference reference = worklist.next();
+      ClassSubject clazz = latest.clazz(reference);
+      clazz.forAllVirtualMethods(
+          method -> {
+            assertTrue(method.toString(), method.isPublic() || method.isProtected());
+            MethodSignature signature = method.getFinalSignature().asMethodSignature();
+            Consumer<ClassReference> adder =
+                t -> {
+                  if (worklist.addIfNotSeen(t)) {
+                    indirectWrappers.computeIfAbsent(t, k -> new HashSet<>()).add(reference);
+                  }
+                };
+            addType(adder, signature.type, existing, customConversions);
+            for (String parameter : signature.parameters) {
+              addType(adder, parameter, existing, customConversions);
+            }
+          });
+    }
+    return indirectWrappers;
+  }
+
+  private Set<ClassReference> getPreDesugarTypes() throws IOException {
+    Set<ClassReference> existing = new HashSet<>();
+    Path androidJar = ToolHelper.getAndroidJar(minApi);
+    new CodeInspector(androidJar)
+        .forAllClasses(
+            clazz -> {
+              if (clazz.getFinalName().startsWith("java.")) {
+                existing.add(Reference.classFromTypeName(clazz.getFinalName()));
+              }
+            });
+    return existing;
+  }
+
+  private CodeInspector getDesugaredApiJar() throws Exception {
+    Path out = temp.newFolder().toPath();
+    GenerateLintFiles desugaredApi =
+        new GenerateLintFiles(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(), out.toString());
+    desugaredApi.run(targetApi.getLevel());
+    return new CodeInspector(
+        out.resolve("compile_api_level_" + targetApi.getLevel())
+            .resolve("desugared_apis_" + targetApi.getLevel() + "_" + minApi.getLevel() + ".jar"));
+  }
+
+  private void addType(
+      Consumer<ClassReference> additions,
+      String type,
+      Set<ClassReference> preDesugarTypes,
+      Set<String> customConversions) {
+    if (type.equals("void")) {
+      return;
+    }
+    TypeReference typeReference = Reference.typeFromTypeName(type);
+    if (typeReference.isArray()) {
+      typeReference = typeReference.asArray().getBaseType();
+    }
+    if (typeReference.isClass()) {
+      ClassReference clazz = typeReference.asClass();
+      String clazzType = descriptorToJavaType(clazz.getDescriptor());
+      if (clazzType.startsWith("java.")
+          && !doesNotNeedWrapper(clazzType, customConversions)
+          && !preDesugarTypes.contains(clazz)) {
+        additions.accept(clazz);
+      }
+    }
+  }
+}
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 3cb6218..23d3658 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
@@ -6,18 +6,26 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CheckCastInstructionSubject;
 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.InvokeInstructionSubject;
-import java.util.Iterator;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TryCatchSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import com.google.common.collect.ImmutableSet;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -28,7 +36,8 @@
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
-  private static final String expectedOutput = StringUtils.lines("Hello, world");
+  private static final String expectedOutput =
+      StringUtils.lines("Caught java.time.format.DateTimeParseException", "true", "Hello, world");
 
   @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
   public static List<Object[]> data() {
@@ -47,15 +56,52 @@
   }
 
   private void checkRewrittenInvokes(CodeInspector inspector) {
+    Set<String> expectedInvokeHolders;
+    Set<String> expectedCatchGuards;
+    Set<String> expectedCheckCastType;
+    String expectedInstanceOfTypes;
     if (parameters.getApiLevel().getLevel() >= 26) {
-      return;
+      expectedInvokeHolders =
+          ImmutableSet.of("java.time.Clock", "java.time.LocalDate", "java.time.ZoneOffset");
+      expectedCatchGuards = ImmutableSet.of("java.time.format.DateTimeParseException");
+      expectedCheckCastType = ImmutableSet.of("java.time.ZoneId");
+      expectedInstanceOfTypes = "java.time.ZoneOffset";
+    } else {
+      expectedInvokeHolders =
+          ImmutableSet.of("j$.time.Clock", "j$.time.LocalDate", "j$.time.ZoneOffset");
+      expectedCatchGuards = ImmutableSet.of("j$.time.format.DateTimeParseException");
+      expectedCheckCastType = ImmutableSet.of("j$.time.ZoneId");
+      expectedInstanceOfTypes = "j$.time.ZoneOffset";
     }
     ClassSubject classSubject = inspector.clazz(TestClass.class);
     assertThat(classSubject, isPresent());
-    Iterator<InvokeInstructionSubject> iterator =
-        classSubject.uniqueMethodWithName("main").iterateInstructions(InstructionSubject::isInvoke);
-    InvokeInstructionSubject invoke = iterator.next();
-    assertTrue(invoke.holder().is("j$.time.Clock"));
+    MethodSubject main = classSubject.uniqueMethodWithName("main");
+    Set<String> foundInvokeHolders =
+        main.streamInstructions()
+            .filter(InstructionSubject::isInvoke)
+            .map(
+                instructionSubject ->
+                    ((InvokeInstructionSubject) instructionSubject).holder().toString())
+            .filter(holder -> holder.startsWith("j$.time.") || holder.startsWith("java.time."))
+            .collect(Collectors.toSet());
+    assertEquals(expectedInvokeHolders, foundInvokeHolders);
+    main.streamInstructions()
+        .filter(InstructionSubject::isCheckCast)
+        .map(InstructionSubject::asCheckCast)
+        .map(CheckCastInstructionSubject::getType)
+        .map(DexType::toSourceString)
+        .collect(Collectors.toSet())
+        .equals(expectedCheckCastType);
+    assertEquals(
+        1,
+        main.streamInstructions().filter(io -> io.isInstanceOf(expectedInstanceOfTypes)).count());
+
+    Set<String> foundCatchGuards =
+        main.streamTryCatches()
+            .flatMap(TryCatchSubject::streamGuards)
+            .map(TypeSubject::toString)
+            .collect(Collectors.toSet());
+    assertEquals(expectedCatchGuards, foundCatchGuards);
   }
 
   @Test
@@ -84,6 +130,7 @@
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .enableInliningAnnotations()
         .compile()
         .inspect(this::checkRewrittenInvokes)
         .addDesugaredCoreLibraryRunClassPath(
@@ -97,8 +144,28 @@
 
   static class TestClass {
 
+    @NeverInline
+    public static Object newObjectInstance() {
+      return System.currentTimeMillis() > 0 ? new Object() : null;
+    }
+
+    @NeverInline
+    public static Object nullReference() {
+      return System.currentTimeMillis() > 0 ? null : new Object();
+    }
+
     public static void main(String[] args) {
       java.time.Clock.systemDefaultZone();
+      try {
+        java.time.LocalDate.parse("");
+      } catch (java.time.format.DateTimeParseException e) {
+        System.out.println("Caught java.time.format.DateTimeParseException");
+      }
+      java.time.ZoneId id = (java.time.ZoneId) nullReference();
+      if (newObjectInstance() instanceof java.time.ZoneOffset) {
+        System.out.println("NOT!");
+      }
+      System.out.println(java.time.ZoneOffset.getAvailableZoneIds().size() > 0);
       System.out.println("Hello, world");
     }
   }
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 579b6f6..ad8e74c 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
@@ -31,7 +31,7 @@
   private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
   private static final String EXPECTED_RESULT =
       StringUtils.lines(
-          "[5, 6, 7]", "$r8$wrapper$java$util$stream$IntStream$-V-WRP", "IntSummaryStatistics");
+          "[5, 6, 7]", "j$.$r8$wrapper$java$util$stream$IntStream$-V-WRP", "IntSummaryStatistics");
 
   @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
   public static List<Object[]> data() {
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 4581211..382f55d 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
@@ -57,6 +57,7 @@
         .addLibraryClasses(CustomLibClass.class)
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
+        .assertNoMessages()
         .addDesugaredCoreLibraryRunClassPath(
             this::buildDesugaredLibrary,
             parameters.getApiLevel(),
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 3d1b999..e8aa581 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
@@ -6,7 +6,6 @@
 
 import com.android.tools.r8.TestParameters;
 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.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -21,8 +20,7 @@
 import java.util.function.IntSupplier;
 import java.util.function.LongConsumer;
 import java.util.function.LongSupplier;
-import org.junit.Assert;
-import org.junit.Assume;
+import java.util.function.Supplier;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,6 +68,7 @@
         .addLibraryClasses(CustomLibClass.class)
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
+        .assertNoMessages()
         .addDesugaredCoreLibraryRunClassPath(
             this::buildDesugaredLibrary,
             parameters.getApiLevel(),
@@ -101,43 +100,6 @@
         .assertSuccessWithOutput(EXPECTED_RESULT);
   }
 
-  @Test
-  public void testWrapperWithChecksum() throws Exception {
-    Assume.assumeTrue(
-        shrinkDesugaredLibrary && parameters.getApiLevel().getLevel() <= MIN_SUPPORTED.getLevel());
-    testForD8()
-        .addProgramClasses(
-            Executor.class, Executor.Object1.class, Executor.Object2.class, Executor.Object3.class)
-        .addLibraryClasses(CustomLibClass.class)
-        .setMinApi(parameters.getApiLevel())
-        .enableCoreLibraryDesugaring(parameters.getApiLevel())
-        .setIncludeClassesChecksum(true) // Compilation fails if some classes are missing checksum.
-        .compile()
-        .inspect(
-            inspector -> {
-              Assert.assertEquals(
-                  9,
-                  inspector.allClasses().stream()
-                      .filter(
-                          clazz ->
-                              clazz
-                                  .getFinalName()
-                                  .contains(DesugaredLibraryWrapperSynthesizer.TYPE_WRAPPER_SUFFIX))
-                      .count());
-              Assert.assertEquals(
-                  9,
-                  inspector.allClasses().stream()
-                      .filter(
-                          clazz ->
-                              clazz
-                                  .getFinalName()
-                                  .contains(
-                                      DesugaredLibraryWrapperSynthesizer
-                                          .VIVIFIED_TYPE_WRAPPER_SUFFIX))
-                      .count());
-            });
-  }
-
   static class Executor {
 
     public static void main(String[] args) {
@@ -146,16 +108,16 @@
       BiFunction<String, String, Character> biFunction =
           CustomLibClass.mixBiFunctions((String i, String j) -> i + j, (String s) -> s.charAt(1));
       System.out.println(biFunction.apply("1", "2"));
-      BooleanSupplier booleanSupplier = CustomLibClass.mixBoolSuppliers(() -> true, () -> false);
+      BooleanSupplier booleanSupplier =
+          () -> CustomLibClass.mixBoolSuppliers(() -> true, () -> false).get();
       System.out.println(booleanSupplier.getAsBoolean());
       LongConsumer longConsumer = CustomLibClass.mixLong(() -> 1L, System.out::println);
       longConsumer.accept(2L);
       DoublePredicate doublePredicate =
           CustomLibClass.mixPredicate(d -> d > 1.0, d -> d == 2.0, d -> d < 3.0);
       System.out.println(doublePredicate.test(2.0));
-      // Reverse wrapper should not exist.
       System.out.println(CustomLibClass.extractInt(() -> 5));
-      System.out.println(CustomLibClass.getDoubleSupplier().getAsDouble());
+      System.out.println(CustomLibClass.getDoubleSupplier().get());
     }
 
     static class Object1 {}
@@ -203,13 +165,18 @@
       return operator.andThen(function);
     }
 
-    public static BooleanSupplier mixBoolSuppliers(
-        BooleanSupplier supplier1, BooleanSupplier supplier2) {
-      return () -> supplier1.getAsBoolean() && supplier2.getAsBoolean();
+    // BooleanSupplier is not a wrapped type, so it can't be placed on the boundary.
+    public static Supplier<Boolean> mixBoolSuppliers(
+        Supplier<Boolean> supplier1, Supplier<Boolean> supplier2) {
+      BooleanSupplier wrap1 = supplier1::get;
+      BooleanSupplier wrap2 = supplier2::get;
+      return () -> wrap1.getAsBoolean() && wrap2.getAsBoolean();
     }
 
-    public static LongConsumer mixLong(LongSupplier supplier, LongConsumer consumer) {
-      return l -> consumer.accept(l + supplier.getAsLong());
+    // LongSupplier is not a wrapped type, so it can't be placed on the boundary.
+    public static LongConsumer mixLong(Supplier<Long> supplier, LongConsumer consumer) {
+      LongSupplier wrap = supplier::get;
+      return l -> consumer.accept(l + wrap.getAsLong());
     }
 
     public static DoublePredicate mixPredicate(
@@ -217,12 +184,16 @@
       return predicate1.and(predicate2).and(predicate3);
     }
 
-    public static int extractInt(IntSupplier supplier) {
-      return supplier.getAsInt();
+    // IntSupplier is not a wrapped type, so it can't be placed on the boundary.
+    public static int extractInt(Supplier<Integer> supplier) {
+      IntSupplier wrap = supplier::get;
+      return wrap.getAsInt();
     }
 
-    public static DoubleSupplier getDoubleSupplier() {
-      return () -> 42.0;
+    // DoubleSupplier is not a wrapped type, so it can't be placed on the boundary.
+    public static Supplier<Double> getDoubleSupplier() {
+      DoubleSupplier supplier = () -> 42.0;
+      return supplier::getAsDouble;
     }
   }
 }
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/WrapperPlacementTest.java
similarity index 72%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperPlacementTest.java
index 9a29671..cbab10c 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/WrapperPlacementTest.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.TestParameters;
@@ -17,19 +17,17 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
-import com.google.common.collect.Sets;
-import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
-import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Arrays;
-import java.util.Set;
+import java.util.stream.Stream;
 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 WrapperMergeTest extends DesugaredLibraryTestBase {
+public class WrapperPlacementTest extends DesugaredLibraryTestBase {
 
   private static final String EXPECTED = StringUtils.lines("[1, 2, 3]", "[2, 3, 4]");
 
@@ -40,7 +38,7 @@
 
   private final TestParameters parameters;
 
-  public WrapperMergeTest(TestParameters parameters) {
+  public WrapperPlacementTest(TestParameters parameters) {
     this.parameters = parameters;
   }
 
@@ -57,21 +55,28 @@
   }
 
   @Test
-  public void testWrapperMerge() throws Exception {
+  public void testNoWrappers() throws Exception {
     assumeTrue(parameters.isDexRuntime());
-    // Multiple wrapper classes have to be merged here.
+    // No wrappers are made during program compilation.
     Path path1 = compileWithCoreLibraryDesugaring(MyArrays1.class);
     Path path2 = compileWithCoreLibraryDesugaring(MyArrays2.class);
     testForD8()
-        .addProgramFiles(path1, path2)
         .addProgramClasses(TestClass.class)
         .addAndroidBuildVersion()
         .enableCoreLibraryDesugaring(parameters.getApiLevel())
         .setMinApi(parameters.getApiLevel())
         .compile()
-        .inspect(this::assertWrappers)
-        .inspect(this::assertNoDuplicates)
-        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
+        .inspect(this::assertNoWrappers)
+        .apply(
+            b -> {
+              if (!hasNativeIntUnaryOperator()) {
+                Path coreLib = buildDesugaredLibrary(parameters.getApiLevel());
+                assertCoreLibContainsWrappers(coreLib);
+                b.addRunClasspathFiles(coreLib);
+              }
+            })
+        // The previous compilations are appended to the classpath (no merge).
+        .addRunClasspathFiles(path1, path2)
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(EXPECTED);
   }
@@ -82,34 +87,29 @@
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel())
         .compile()
-        .inspect(this::assertWrappers)
+        .inspect(this::assertNoWrappers)
         .writeToZip();
   }
 
-  private void assertNoDuplicates(CodeInspector inspector) {
-    Object2ReferenceMap<String, Set<FoundClassSubject>> map = new Object2ReferenceOpenHashMap<>();
-    for (FoundClassSubject clazz : inspector.allClasses()) {
-      map.computeIfAbsent(clazz.getFinalName(), k -> Sets.newIdentityHashSet()).add(clazz);
-    }
-    for (Set<FoundClassSubject> duplicates : map.values()) {
-      if (duplicates.size() > 1) {
-        fail("Unexpected duplicates: " + duplicates);
-      }
-    }
-  }
-
   private boolean hasNativeIntUnaryOperator() {
     return parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
   }
 
-  private void assertWrappers(CodeInspector inspector) {
-    assertEquals(
-        hasNativeIntUnaryOperator() ? 0 : 2,
-        inspector.allClasses().stream()
-            .filter(
-                c ->
-                    c.getOriginalName().contains(DesugaredLibraryWrapperSynthesizer.WRAPPER_PREFIX))
-            .count());
+  private void assertCoreLibContainsWrappers(Path coreLib) throws IOException {
+    CodeInspector inspector = new CodeInspector(coreLib);
+    Stream<FoundClassSubject> wrappers = getWrappers(inspector);
+    assertNotEquals(0, wrappers.count());
+  }
+
+  private void assertNoWrappers(CodeInspector inspector) {
+    Stream<FoundClassSubject> wrappers = getWrappers(inspector);
+    assertEquals(0, wrappers.count());
+  }
+
+  private Stream<FoundClassSubject> getWrappers(CodeInspector inspector) {
+    return inspector.allClasses().stream()
+        .filter(
+            c -> c.getOriginalName().contains(DesugaredLibraryWrapperSynthesizer.WRAPPER_PREFIX));
   }
 
   static class MyArrays1 {
diff --git a/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
new file mode 100644
index 0000000..6fea76f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.internal.retrace;
+
+import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.internal.retrace.stacktraces.CronetStackTrace;
+import com.android.tools.r8.internal.retrace.stacktraces.FinskyStackTrace;
+import com.android.tools.r8.internal.retrace.stacktraces.VelvetStackTrace;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceCommand;
+import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
+import java.util.List;
+import java.util.function.BiConsumer;
+import junit.framework.TestCase;
+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 RetraceTests extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public RetraceTests(TestParameters parameters) {}
+
+  @Test
+  public void testCronetStackTrace() {
+    runRetraceTest(new CronetStackTrace());
+  }
+
+  @Test
+  public void testFinskyStackTrace() {
+    runRetraceTest(new FinskyStackTrace(), "(?:.*Finsky\\s+:\\s+\\[\\d+\\]\\s+%c\\.%m\\(%l\\):.*)");
+  }
+
+  @Test
+  public void testVelvetStackTrace() {
+    runRetraceTest(new VelvetStackTrace());
+  }
+
+  private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
+    return runRetraceTest(stackTraceForTest, DEFAULT_REGULAR_EXPRESSION);
+  }
+
+  private TestDiagnosticMessagesImpl runRetraceTest(
+      StackTraceForTest stackTraceForTest, String regularExpression) {
+    return runRetraceTest(stackTraceForTest, regularExpression, TestCase::assertEquals);
+  }
+
+  private TestDiagnosticMessagesImpl runRetraceTest(
+      StackTraceForTest stackTraceForTest,
+      String regularExpression,
+      BiConsumer<List<String>, List<String>> matcher) {
+    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+    RetraceCommand retraceCommand =
+        RetraceCommand.builder(diagnosticsHandler)
+            .setProguardMapProducer(stackTraceForTest::mapping)
+            .setStackTrace(stackTraceForTest.obfuscatedStackTrace())
+            .setRegularExpression(regularExpression)
+            .setRetracedStackTraceConsumer(
+                retraced -> matcher.accept(stackTraceForTest.retracedStackTrace(), retraced))
+            .build();
+    Retrace.run(retraceCommand);
+    return diagnosticsHandler;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/CronetStackTrace.java b/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/CronetStackTrace.java
new file mode 100644
index 0000000..aa24fbc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/CronetStackTrace.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.internal.retrace.stacktraces;
+
+public class CronetStackTrace extends RetraceInternalStackTraceForTest {
+
+  public CronetStackTrace() {
+    super("cronet_obfuscated.txt", "cronet_deobfuscated.txt");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/FinskyStackTrace.java b/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/FinskyStackTrace.java
new file mode 100644
index 0000000..6fde480
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/FinskyStackTrace.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.internal.retrace.stacktraces;
+
+public class FinskyStackTrace extends RetraceInternalStackTraceForTest {
+
+  public FinskyStackTrace() {
+    super("finsky_obfuscated.txt", "finsky_deobfuscated.txt");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/RetraceInternalStackTraceForTest.java b/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/RetraceInternalStackTraceForTest.java
new file mode 100644
index 0000000..e4005de
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/RetraceInternalStackTraceForTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.internal.retrace.stacktraces;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+public class RetraceInternalStackTraceForTest implements StackTraceForTest {
+
+  private static final Path retraceInternal =
+      Paths.get(ToolHelper.THIRD_PARTY_DIR).resolve("retrace_internal");
+  private final Path obfuscatedPath;
+  private final Path deobfuscatedPath;
+
+  public RetraceInternalStackTraceForTest(String obfuscatedFile, String deobfuscatedFile) {
+    this.obfuscatedPath = retraceInternal.resolve(obfuscatedFile);
+    this.deobfuscatedPath = retraceInternal.resolve(deobfuscatedFile);
+  }
+
+  @Override
+  public List<String> obfuscatedStackTrace() {
+    try {
+      return FileUtils.readAllLines(obfuscatedPath);
+    } catch (IOException e) {
+      e.printStackTrace();
+      throw new RuntimeException("Could not read file " + obfuscatedPath.toString());
+    }
+  }
+
+  @Override
+  public String mapping() {
+    Path mappingPath = retraceInternal.resolve("mapping.txt");
+    try {
+      return new String(Files.readAllBytes(mappingPath));
+    } catch (IOException e) {
+      e.printStackTrace();
+      throw new RuntimeException("Could not read mapping file " + mappingPath.toString());
+    }
+  }
+
+  @Override
+  public List<String> retracedStackTrace() {
+    try {
+      return FileUtils.readAllLines(deobfuscatedPath);
+    } catch (IOException e) {
+      e.printStackTrace();
+      throw new RuntimeException("Could not read file " + deobfuscatedPath.toString());
+    }
+  }
+
+  @Override
+  public int expectedWarnings() {
+    return 0;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/VelvetStackTrace.java b/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/VelvetStackTrace.java
new file mode 100644
index 0000000..a2f468e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/retrace/stacktraces/VelvetStackTrace.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.internal.retrace.stacktraces;
+
+public class VelvetStackTrace extends RetraceInternalStackTraceForTest {
+
+  public VelvetStackTrace() {
+    super("velvet_obfuscated.txt", "velvet_deobfuscated.txt");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/HostWithStaticMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/HostWithStaticMethodTest.java
new file mode 100644
index 0000000..0fcb3ee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/HostWithStaticMethodTest.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.staticizer;
+
+import com.android.tools.r8.CompilationFailedException;
+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.ir.optimize.staticizer.HostWithStaticMethodTest.Outer.SingletonHolder;
+import java.io.IOException;
+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)
+// This is a reproduction of b/158018192.
+public class HostWithStaticMethodTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public HostWithStaticMethodTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Outer.class, SingletonHolder.class, Main.class)
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .enableNeverClassInliningAnnotations()
+        .enableInliningAnnotations()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("foo", "bar", "foo");
+  }
+
+  @NeverClassInline
+  public static class Outer {
+
+    public static class SingletonHolder {
+
+      public static final Outer outer = new Outer();
+
+      @NeverInline
+      // This method should not be in conflict with any methods in Outer.
+      public static void foo2() {
+        foo();
+      }
+    }
+
+    @NeverInline
+    public static void foo() {
+      System.out.println("foo");
+    }
+
+    @NeverInline
+    public void bar() {
+      System.out.println("bar");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      Outer.foo();
+      SingletonHolder.outer.bar();
+      SingletonHolder.foo2();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InstanceInsideCompanionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InstanceInsideCompanionTest.java
index f28235b..5dabfaf 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InstanceInsideCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InstanceInsideCompanionTest.java
@@ -46,8 +46,9 @@
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN)
-        .assertSuccessWithOutputLines("Candidate#foo(false)")
-        .inspect(this::inspect);
+        .assertSuccessWithOutputLines("Candidate#foo(false)");
+    // TODO(b/159174309): Disable inspection until fixed.
+    // .inspect(this::inspect);
   }
 
   private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
index e552581..4e3f691 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
@@ -59,11 +59,13 @@
     assertThat(instance, not(isPresent()));
 
     ClassSubject companion = inspector.clazz(Host.Companion.class);
-    assertThat(companion, not(isPresent()));
+    // TODO(b/158018192): This should not be present.
+    assertThat(companion, isPresent());
 
     // Check if the candidate methods are staticized (if necessary) and migrated.
     for (String name : ImmutableList.of("boo", "foo")) {
-      MethodSubject oo = host.uniqueMethodWithName(name);
+      // TODO(b/158018192): This should be host and not companion.
+      MethodSubject oo = companion.uniqueMethodWithName(name);
       assertThat(oo, isPresent());
       assertTrue(oo.isStatic());
       assertTrue(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
new file mode 100644
index 0000000..549f834
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
@@ -0,0 +1,156 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.StringContains.containsString;
+
+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.utils.DescriptorUtils;
+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.MethodSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteAllowAccessModificationTest extends KotlinMetadataTestBase {
+
+  private static final String PKG_LIB = PKG + ".allow_access_modification_lib";
+  private static final String PKG_APP = PKG + ".allow_access_modification_app";
+  private final String EXPECTED =
+      StringUtils.lines(
+          "4",
+          "2",
+          "42",
+          "3",
+          "1",
+          "42",
+          "5",
+          "6",
+          "7",
+          "funPrivate",
+          "funInternal",
+          "funProtected",
+          "extensionPrivate",
+          "extensionInternal",
+          "companionPrivate",
+          "companionInternal",
+          "staticPrivate",
+          "staticInternal");
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteAllowAccessModificationTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>();
+  private static Map<KotlinTargetVersion, Path> libReferenceJars = new HashMap<>();
+  private final TestParameters parameters;
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      libJars.put(
+          targetVersion,
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(
+                  getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"))
+              .compile());
+      libReferenceJars.put(
+          targetVersion,
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(
+                  getKotlinFileInTest(
+                      DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib_reference"))
+              .compile());
+    }
+  }
+
+  @Test
+  public void smokeTest() throws Exception {
+    Path libJar = libReferenceJars.get(targetVersion);
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(
+                getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testMetadataForLib() throws Exception {
+    // We compile libjar with -allowaccesmodification such that all visibility modifiers are updated
+    // to be public. We also compile it with a mapping file to rename Lib to LibReference. After
+    // running with R8, the output should be binary compatible with libReference.
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(libJars.get(targetVersion))
+            .addKeepRules("-keepclassmembers,allowaccessmodification class **.Lib { *; }")
+            .addKeepRules("-keep,allowaccessmodification,allowobfuscation class **.Lib { *; }")
+            .addKeepRules("-keepclassmembers,allowaccessmodification class **.Lib$Comp { *; }")
+            .addKeepRules("-keep,allowaccessmodification,allowobfuscation class **.Lib$Comp { *; }")
+            .addKeepRules("-keep,allowaccessmodification,allowobfuscation class **.LibKt { *; }")
+            .addKeepRules("-allowaccessmodification")
+            .addApplyMapping(
+                StringUtils.lines(
+                    PKG_LIB + ".Lib -> " + PKG_LIB + ".LibReference:",
+                    PKG_LIB + ".Lib$Comp -> " + PKG_LIB + ".LibReference$Comp:",
+                    PKG_LIB + ".LibKt -> " + PKG_LIB + ".LibReferenceKt:",
+                    "  void extensionPrivate(" + PKG_LIB + ".Lib) -> extensionPrivate",
+                    "  void extensionInternal(" + PKG_LIB + ".Lib) -> extensionInternal",
+                    "  void staticPrivate() -> staticPrivateReference",
+                    "  void staticInternal() -> staticInternalReference"))
+            .addKeepRuntimeVisibleAnnotations()
+            .compile()
+            .inspect(this::inspect)
+            .writeToZip();
+    ProcessResult mainResult =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(
+                getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compileRaw();
+    assertEquals(1, mainResult.exitCode);
+    assertThat(mainResult.stderr, containsString("cannot access 'LibReference'"));
+  }
+
+  private void inspect(CodeInspector inspector) throws Exception {
+    // TODO(b/154348683): Assert equality between LibReference and Lib.
+    // assertEqualMetadata(new CodeInspector(libReferenceJars.get(targetVersion)), inspector);
+    ClassSubject lib = inspector.clazz(PKG_LIB + ".Lib");
+    MethodSubject funInline = lib.uniqueMethodWithName("funInline$main");
+    assertThat(funInline, isPresent());
+    // TODO(b/154348683): Keep the inline method package private.
+    // assertTrue(funInline.isPackagePrivate());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
index b2e0b8a..2c14c8d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -58,7 +58,8 @@
           "42",
           "42",
           "1",
-          "Hello World!");
+          "Hello World!",
+          "class com.android.tools.r8.kotlin.metadata.typealias_lib.Super");
 
   private final TestParameters parameters;
 
@@ -102,7 +103,8 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addRunClasspathFiles(
+            ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), PKG + ".typealias_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
@@ -110,9 +112,11 @@
 
   @Test
   public void testMetadataInTypeAlias_renamed() throws Exception {
+    String superTypeName = "com.android.tools.r8.kotlin.metadata.typealias_lib.Super";
+    String renamedSuperTypeName = "com.android.tools.r8.kotlin.metadata.typealias_lib.FooBar";
     Path libJar =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(ToolHelper.getKotlinStdlibJar())
+            .addClasspathFiles(ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar())
             .addProgramFiles(typeAliasLibJarMap.get(targetVersion))
             // Keep non-private members of Impl
             .addKeepRules("-keep class **.Impl { !private *; }")
@@ -128,6 +132,8 @@
             // Keep the library test methods
             .addKeepRules("-keep class " + PKG + ".typealias_lib.*Tester { *; }")
             .addKeepRules("-keep class " + PKG + ".typealias_lib.*Tester$Companion { *; }")
+            .addKeepRules("-keep class " + PKG + ".typealias_lib.SubTypeOfAlias { *; }")
+            .addApplyMapping(superTypeName + " -> " + renamedSuperTypeName + ":")
             .addKeepAttributes(
                 ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
                 ProguardKeepAttributes.SIGNATURE,
@@ -144,10 +150,11 @@
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addRunClasspathFiles(
+            ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar)
         .addClasspath(appJar)
         .run(parameters.getRuntime(), PKG + ".typealias_app.MainKt")
-        .assertSuccessWithOutput(EXPECTED);
+        .assertSuccessWithOutput(EXPECTED.replace(superTypeName, renamedSuperTypeName));
   }
 
   private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index 39e9d12..2a33484 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -6,15 +6,13 @@
 
 import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.containsString;
+import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 
 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.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -85,24 +83,23 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(libJars.get(targetVersion))
-            .addKeepRules("-keep class " + PKG_LIB + ".Sub { *** kept(); }")
+            .addKeepRules("-keep class " + PKG_LIB + ".Sub { <init>(); *** kept(); }")
             .addKeepRuntimeVisibleAnnotations()
             .noMinification()
             .compile()
             .inspect(this::checkPruned)
             .writeToZip();
-    // TODO(b/158766557): This should work.
-    ProcessResult mainResult =
+    Path output =
         kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
             .addClasspathFiles(libJar)
             .addSourceFiles(
                 getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compileRaw();
-    assertEquals(1, mainResult.exitCode);
-    assertThat(
-        mainResult.stderr,
-        containsString("cannot access 'com.android.tools.r8.kotlin.metadata.pruned_lib.Base'"));
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addProgramFiles(output)
+        .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+        .assertSuccessWithOutput(EXPECTED);
   }
 
   private void checkPruned(CodeInspector inspector) {
@@ -112,6 +109,7 @@
     assertThat(sub, isPresent());
     KmClassSubject kmClass = sub.getKmClass();
     assertThat(kmClass, isPresent());
-    // TODO(b/158766557): Assert that things are indeed removed.
+    assertEquals(0, kmClass.getSuperTypes().size());
+    assertThat(kmClass.kmFunctionWithUniqueName("notKept"), not(isPresent()));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/allow_access_modification_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/allow_access_modification_app/main.kt
new file mode 100644
index 0000000..fc2e518
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/allow_access_modification_app/main.kt
@@ -0,0 +1,40 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.allow_access_modification_app
+
+import com.android.tools.r8.kotlin.metadata.allow_access_modification_lib.LibReference
+import com.android.tools.r8.kotlin.metadata.allow_access_modification_lib.extensionInternal
+import com.android.tools.r8.kotlin.metadata.allow_access_modification_lib.extensionPrivate
+import com.android.tools.r8.kotlin.metadata.allow_access_modification_lib.staticInternalReference
+import com.android.tools.r8.kotlin.metadata.allow_access_modification_lib.staticPrivateReference
+
+fun main() {
+  val libReference = LibReference(1, 2, 3, 4)
+  println(libReference.propPrimaryPrivate)
+  println(libReference.propPrimaryInternal)
+  println(libReference.propPrivate)
+  libReference.propPrimaryPrivate = 5
+  libReference.propPrimaryInternal = 6
+  libReference.propPrivate = 7
+  println(libReference.readOnlyPropPrimaryPrivate)
+  println(libReference.readOnlyPropPrimaryInternal)
+  println(libReference.readOnlyPropInternal)
+  println(libReference.propPrimaryPrivate)
+  println(libReference.propPrimaryInternal)
+  println(libReference.propPrivate)
+
+  libReference.funPrivate()
+  libReference.funInternal()
+  libReference.funProtected()
+
+  libReference.extensionPrivate()
+  libReference.extensionInternal()
+
+  LibReference.companionPrivate()
+  LibReference.companionInternal()
+
+  staticPrivateReference()
+  staticInternalReference()
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/allow_access_modification_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/allow_access_modification_lib/lib.kt
new file mode 100644
index 0000000..f77d4f8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/allow_access_modification_lib/lib.kt
@@ -0,0 +1,59 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.allow_access_modification_lib
+
+internal class Lib internal constructor(
+  internal val readOnlyPropPrimaryInternal: Int,
+  internal var propPrimaryInternal : Int,
+  private val readOnlyPropPrimaryPrivate: Int,
+  internal var propPrimaryPrivate : Int) {
+
+  internal val propInternal: Int = 42
+  private var propPrivate: Int = 0
+
+  private fun funPrivate() {
+    println("funPrivate")
+  }
+
+  internal fun funInternal() {
+    println("funInternal")
+  }
+
+  protected fun funProtected() {
+    println("funProtected")
+  }
+
+  // Keep this internal to ensure we do not modify inline functions.
+  internal inline fun funInline() {
+    println("funInline")
+  }
+
+  internal companion object Comp {
+
+    private fun companionPrivate() {
+      println("companionPrivate")
+    }
+
+    internal fun companionInternal() {
+      println("companionInternal")
+    }
+  }
+}
+
+private fun Lib.extensionPrivate() {
+  println("extensionPrivate")
+}
+
+internal fun Lib.extensionInternal() {
+  println("extensionInternal")
+}
+
+private fun staticPrivate() {
+  println("staticPrivate")
+}
+
+internal fun staticInternal() {
+  println("staticInternal")
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/allow_access_modification_lib/lib_reference.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/allow_access_modification_lib/lib_reference.kt
new file mode 100644
index 0000000..a26d8d2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/allow_access_modification_lib/lib_reference.kt
@@ -0,0 +1,59 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.allow_access_modification_lib
+
+class LibReference(
+  val readOnlyPropPrimaryInternal: Int,
+  var propPrimaryInternal : Int,
+  val readOnlyPropPrimaryPrivate: Int,
+  var propPrimaryPrivate : Int) {
+
+  val readOnlyPropInternal: Int = 42
+  var propPrivate: Int = 42
+
+  fun funPrivate() {
+    println("funPrivate")
+  }
+
+  fun funInternal() {
+    println("funInternal")
+  }
+
+  fun funProtected() {
+    println("funProtected")
+  }
+
+  // Keep this internal to ensure we do not modify inline functions.
+  internal inline fun funInline() {
+    println("funInline")
+  }
+
+  companion object Factory {
+
+    fun companionPrivate() {
+      println("companionPrivate")
+    }
+
+    fun companionInternal() {
+      println("companionInternal")
+    }
+  }
+}
+
+fun LibReference.extensionPrivate() {
+  println("extensionPrivate")
+}
+
+fun LibReference.extensionInternal() {
+  println("extensionInternal")
+}
+
+fun staticPrivateReference() {
+  println("staticPrivate")
+}
+
+fun staticInternalReference() {
+  println("staticInternal")
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_app/main.kt
index f836216..71dc2aa 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_app/main.kt
@@ -23,6 +23,7 @@
 import com.android.tools.r8.kotlin.metadata.typealias_lib.OuterTester
 import com.android.tools.r8.kotlin.metadata.typealias_lib.SimpleClassTester
 import com.android.tools.r8.kotlin.metadata.typealias_lib.StillCWithConstructor
+import com.android.tools.r8.kotlin.metadata.typealias_lib.SubTypeOfAlias
 import com.android.tools.r8.kotlin.metadata.typealias_lib.UnderlyingTypeTester
 import com.android.tools.r8.kotlin.metadata.typealias_lib.UnusedTypeArgument
 import com.android.tools.r8.kotlin.metadata.typealias_lib.VerticalClassMergingTester
@@ -107,6 +108,10 @@
   VerticalClassMergingTester.passThrough(apiImpl).foo()
 }
 
+fun testSuperType() {
+  println(SubTypeOfAlias::class.supertypes[0].classifier)
+}
+
 fun main() {
   val instance = ProgramClass()
   val l = seq(instance)
@@ -122,6 +127,7 @@
   testNestedClasses()
   testCompanion()
   testConstructor()
-  testUnderlyingType();
-  testVerticalClassMerging();
+  testUnderlyingType()
+  testVerticalClassMerging()
+  testSuperType()
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_lib/lib_ext.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_lib/lib_ext.kt
index 60d5bda..481adfc 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_lib/lib_ext.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_lib/lib_ext.kt
@@ -420,6 +420,11 @@
     fun i(a : MyAdvancedMap) : MutableMap<OuterNested, OuterNestedInner> {
       return a;
     }
-
   }
 }
+
+open class Super
+
+typealias TypeAliasForSuper = Super
+
+class SubTypeOfAlias : TypeAliasForSuper()
diff --git a/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java b/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java
new file mode 100644
index 0000000..acf084b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.regress.b158429654;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.regress.b158429654.innerpackage.InnerClass;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InliningNonAccessible extends TestBase {
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public InliningNonAccessible(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testCompileToInvalidFileD8() throws Exception {
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters.getApiLevel())
+        .addProgramClasses(OuterAbstract.class, OuterImpl.class, InnerClass.class)
+        .addProgramClasses(Main.class)
+        .addKeepMainRule(Main.class)
+        .noMinification()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("42");
+  }
+
+  static class Main {
+    public static void main(String[] args) {
+      OuterImpl.register(args);
+      InnerClass inner = new InnerClass();
+      inner.foobar();
+      System.out.println("42");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b158429654/OuterAbstract.java b/src/test/java/com/android/tools/r8/regress/b158429654/OuterAbstract.java
new file mode 100644
index 0000000..0c5a078
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b158429654/OuterAbstract.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.regress.b158429654;
+
+public abstract class OuterAbstract {
+  private static OuterAbstract sInstance;
+
+  public static OuterAbstract getInstance() {
+    return sInstance;
+  }
+
+  public static void setsInstance(OuterAbstract sInstance) {
+    OuterAbstract.sInstance = sInstance;
+  }
+
+  public abstract void theMethod();
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b158429654/OuterImpl.java b/src/test/java/com/android/tools/r8/regress/b158429654/OuterImpl.java
new file mode 100644
index 0000000..edca5c1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b158429654/OuterImpl.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.regress.b158429654;
+
+class OuterImpl extends OuterAbstract {
+
+  public static void register(String[] args) {
+    OuterAbstract.setsInstance(new OuterImpl());
+  }
+
+  private OuterImpl() {}
+
+  @Override
+  public void theMethod() {}
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b158429654/innerpackage/InnerClass.java b/src/test/java/com/android/tools/r8/regress/b158429654/innerpackage/InnerClass.java
new file mode 100644
index 0000000..b2f8582
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b158429654/innerpackage/InnerClass.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.regress.b158429654.innerpackage;
+
+import com.android.tools.r8.regress.b158429654.OuterAbstract;
+
+public class InnerClass {
+
+  public void foobar() {
+    OuterAbstract.getInstance().theMethod();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
index 102f7f0..e8494d6 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -4,10 +4,10 @@
 
 package com.android.tools.r8.retrace;
 
-import static com.android.tools.r8.retrace.RetraceTests.DEFAULT_REGULAR_EXPRESSION;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.StringContains.containsString;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -61,7 +61,8 @@
   @Test
   public void testVerbose() throws IOException {
     FoundMethodVerboseStackTrace stackTrace = new FoundMethodVerboseStackTrace();
-    runTest(
+    // TODO(b/159562137): Add proper support for -verbose when using regexp.
+    runTestNotEquals(
         stackTrace.mapping(),
         StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
         false,
@@ -70,14 +71,35 @@
   }
 
   @Test
+  public void testVerboseSingleHyphen() throws IOException {
+    FoundMethodVerboseStackTrace stackTrace = new FoundMethodVerboseStackTrace();
+    // TODO(b/159562137): Add proper support for -verbose when using regexp.
+    runTestNotEquals(
+        stackTrace.mapping(),
+        StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
+        false,
+        StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR,
+        "-verbose");
+  }
+
+  @Test
   public void testRegularExpression() throws IOException {
     ActualRetraceBotStackTrace stackTrace = new ActualRetraceBotStackTrace();
     runTest(
         stackTrace.mapping(),
         StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
         false,
-        StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR,
-        "--regex=" + DEFAULT_REGULAR_EXPRESSION);
+        StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR);
+  }
+
+  @Test
+  public void testRegularExpressionSingleHyphen() throws IOException {
+    ActualRetraceBotStackTrace stackTrace = new ActualRetraceBotStackTrace();
+    runTest(
+        stackTrace.mapping(),
+        StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
+        false,
+        StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR);
   }
 
   @Test
@@ -88,7 +110,6 @@
         StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
         false,
         StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR,
-        "--regex=" + DEFAULT_REGULAR_EXPRESSION,
         "--info");
   }
 
@@ -123,6 +144,14 @@
     assertEquals(expected, result.stdout);
   }
 
+  private void runTestNotEquals(
+      String mapping, String stackTrace, boolean stacktraceStdIn, String expected, String... args)
+      throws IOException {
+    ProcessResult result = runRetrace(mapping, stackTrace, stacktraceStdIn, args);
+    assertEquals(0, result.exitCode);
+    assertNotEquals(expected, result.stdout);
+  }
+
   private void runAbortTest(Matcher<String> errorMatch, String... args) throws IOException {
     ProcessResult result = runRetraceCommandLine(null, Arrays.asList(args));
     assertEquals(1, result.exitCode);
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 47dc5c3..9af9e28 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.retrace;
 
+import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -45,12 +46,6 @@
 @RunWith(Parameterized.class)
 public class RetraceTests extends TestBase {
 
-  // This is a slight modification of the default regular expression shown for proguard retrace
-  // that allow for retracing classes in the form <class>: lorem ipsum...
-  // Seems like Proguard retrace is expecting the form "Caused by: <class>".
-  public static final String DEFAULT_REGULAR_EXPRESSION =
-      "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%s(?::%l)?\\)\\s*(?:~\\[.*\\])?)|(?:(?:(?:%c|.*)?[:\"]\\s+)?%c(?::.*)?)";
-
   @Parameters(name = "{0}, use regular expression: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(getTestParameters().withNoneRuntime().build(), BooleanUtils.values());
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
index 3e47f3b..d6bac64 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
@@ -11,12 +11,17 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
+import java.util.Collection;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 class CfTryCatchSubject implements TryCatchSubject {
+  private final CodeInspector inspector;
   private final CfCode cfCode;
   private final CfTryCatch tryCatch;
 
-  CfTryCatchSubject(CfCode cfCode, CfTryCatch tryCatch) {
+  CfTryCatchSubject(CodeInspector inspector, CfCode cfCode, CfTryCatch tryCatch) {
+    this.inspector = inspector;
     this.cfCode = cfCode;
     this.tryCatch = tryCatch;
   }
@@ -62,6 +67,16 @@
   }
 
   @Override
+  public Stream<TypeSubject> streamGuards() {
+    return tryCatch.guards.stream().map(type -> new TypeSubject(inspector, type));
+  }
+
+  @Override
+  public Collection<TypeSubject> guards() {
+    return streamGuards().collect(Collectors.toList());
+  }
+
+  @Override
   public int getNumberOfHandlers() {
     return tryCatch.guards.size();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 2a4c7c5..f43c01c 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -386,11 +386,11 @@
   }
 
   TryCatchSubject createTryCatchSubject(DexCode code, Try tryElement, TryHandler tryHandler) {
-    return new DexTryCatchSubject(code, tryElement, tryHandler);
+    return new DexTryCatchSubject(this, code, tryElement, tryHandler);
   }
 
   TryCatchSubject createTryCatchSubject(CfCode code, CfTryCatch tryCatch) {
-    return new CfTryCatchSubject(code, tryCatch);
+    return new CfTryCatchSubject(this, code, tryCatch);
   }
 
   TryCatchIterator createTryCatchIterator(MethodSubject method) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
index 4a96ff1..4624c33 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
@@ -9,13 +9,20 @@
 import com.android.tools.r8.graph.DexCode.Try;
 import com.android.tools.r8.graph.DexCode.TryHandler;
 import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 class DexTryCatchSubject implements TryCatchSubject {
+  private final CodeInspector inspector;
   private final DexCode dexCode;
   private final Try tryElement;
   private final TryHandler tryHandler;
 
-  DexTryCatchSubject(DexCode dexCode, Try tryElement, TryHandler tryHandler) {
+  DexTryCatchSubject(
+      CodeInspector inspector, DexCode dexCode, Try tryElement, TryHandler tryHandler) {
+    this.inspector = inspector;
     this.dexCode = dexCode;
     this.tryElement = tryElement;
     this.tryHandler = tryHandler;
@@ -45,6 +52,18 @@
   }
 
   @Override
+  public Stream<TypeSubject> streamGuards() {
+    return Arrays.stream(tryHandler.pairs)
+        .map(pair -> pair.type)
+        .map(type -> new TypeSubject(inspector, type));
+  }
+
+  @Override
+  public Collection<TypeSubject> guards() {
+    return streamGuards().collect(Collectors.toList());
+  }
+
+  @Override
   public int getNumberOfHandlers() {
     if (tryHandler.catchAllAddr != NO_HANDLER) {
       return tryHandler.pairs.length + 1;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index b158404..72b71b0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -89,6 +89,10 @@
     return Streams.stream(iterateInstructions());
   }
 
+  public Stream<TryCatchSubject> streamTryCatches() {
+    return Streams.stream(iterateTryCatches());
+  }
+
   public void getLineNumberForInstruction(InstructionSubject subject) {
     assert hasLineNumberTable();
     getLineNumberTable().getLineForInstruction(subject);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java
index 73e9ef1..d34f4e9 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java
@@ -3,10 +3,17 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils.codeinspector;
 
+import java.util.Collection;
+import java.util.stream.Stream;
+
 public interface TryCatchSubject {
   RangeSubject getRange();
   boolean isCatching(String exceptionType);
   boolean hasCatchAll();
 
+  Stream<TypeSubject> streamGuards();
+
+  Collection<TypeSubject> guards();
+
   int getNumberOfHandlers();
 }
diff --git a/tests/d8_api_usage_sample.jar b/tests/d8_api_usage_sample.jar
deleted file mode 100644
index da01fdb..0000000
--- a/tests/d8_api_usage_sample.jar
+++ /dev/null
Binary files differ
diff --git a/tests/r8_api_usage_sample.jar b/tests/r8_api_usage_sample.jar
index da01fdb..c50e110 100644
--- a/tests/r8_api_usage_sample.jar
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/third_party/r8-releases/2.0.74.tar.gz.sha1 b/third_party/r8-releases/2.0.74.tar.gz.sha1
new file mode 100644
index 0000000..7359e6d
--- /dev/null
+++ b/third_party/r8-releases/2.0.74.tar.gz.sha1
@@ -0,0 +1 @@
+6903a4fb98ef23da4d40e1b08998a390578dd0ef
\ No newline at end of file
diff --git a/third_party/retrace_internal.tar.gz.sha1 b/third_party/retrace_internal.tar.gz.sha1
new file mode 100644
index 0000000..b840e19
--- /dev/null
+++ b/third_party/retrace_internal.tar.gz.sha1
@@ -0,0 +1 @@
+f00aa79849d9eb8a16accaca566ac275f4711d8e
\ No newline at end of file
diff --git a/tools/r8_release.py b/tools/r8_release.py
index 357b659..1224b1d 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -21,7 +21,7 @@
 R8_VERSION_FILE = os.path.join(
     'src', 'main', 'java', 'com', 'android', 'tools', 'r8', 'Version.java')
 THIS_FILE_RELATIVE = os.path.join('tools', 'r8_release.py')
-ADMRT = '/google/data/ro/teams/android-devtools-infra/tools/admrt'
+GMAVEN_PUBLISHER = '/google/bin/releases/android-devtools/gmaven/publisher/gmaven-publisher'
 
 DESUGAR_JDK_LIBS = 'desugar_jdk_libs'
 DESUGAR_JDK_LIBS_CONFIGURATION = DESUGAR_JDK_LIBS + '_configuration'
@@ -223,6 +223,31 @@
   return release_aosp
 
 
+def prepare_maven(args):
+  assert args.version
+
+  def release_maven(options):
+    gfile = '/bigstore/r8-releases/raw/%s/r8lib.zip' % args.version
+    release_id = gmaven_publisher_stage(options, [gfile])
+
+    print "Staged Release ID " + release_id + ".\n"
+    gmaven_publisher_stage_redir_test_info(
+        release_id, "com.android.tools:r8:%s" % args.version, "r8lib.jar")
+
+    print
+    input = raw_input("Continue with publishing [y/N]:")
+
+    if input != 'y':
+      print 'Aborting release to Google maven'
+      sys.exit(1)
+
+    gmaven_publisher_publish(args, release_id)
+
+    print
+    print "Published. Use the email workflow for approval."
+
+  return release_maven
+
 def git_message_dev(version):
   return """Update D8 R8 master to %s
 
@@ -297,6 +322,14 @@
       ('https://storage.googleapis.com/r8-releases/raw/%s/%s' % (version, file)),
       dst)
 
+def download_gfile(gfile, dst):
+  if not gfile.startswith('/bigstore/r8-releases'):
+    print 'Unexpected gfile prefix for %s' % gfile
+    sys.exit(1)
+
+  urllib.urlretrieve(
+      'https://storage.googleapis.com/%s' % gfile[len('/bigstore/'):],
+      dst)
 
 def blaze_run(target):
   return subprocess.check_output(
@@ -356,46 +389,52 @@
 
   def make_release(args):
     library_version = args.desugar_library[0]
-    configuration_hash = args.desugar_library[1]
+    configuration_version = args.desugar_library[1]
 
     library_archive = DESUGAR_JDK_LIBS + '.zip'
+    library_jar = DESUGAR_JDK_LIBS + '.jar'
     library_artifact_id = \
         '%s:%s:%s' % (ANDROID_TOOLS_PACKAGE, DESUGAR_JDK_LIBS, library_version)
 
+    configuration_archive = DESUGAR_JDK_LIBS_CONFIGURATION + '.zip'
+
     with utils.TempDir() as temp:
       with utils.ChangedWorkingDirectory(temp):
-        download_file(
-          '%s/%s' % (DESUGAR_JDK_LIBS, library_version),
-          library_archive,
-          library_archive)
-        configuration_archive = DESUGAR_JDK_LIBS_CONFIGURATION + '.zip'
-        configuration_artifact_id = \
-            download_configuration(configuration_hash, configuration_archive)
+        library_gfile = ('/bigstore/r8-releases/raw/%s/%s/%s'
+              % (DESUGAR_JDK_LIBS, library_version, library_archive))
+        configuration_gfile = ('/bigstore/r8-releases/raw/master/%s/%s'
+              % (configuration_version, configuration_archive))
 
-        print 'Preparing maven release of:'
-        print '  %s' % library_artifact_id
-        print '  %s' % configuration_artifact_id
+        download_gfile(library_gfile, library_archive)
+        download_gfile(configuration_gfile, configuration_archive)
+        check_configuration(configuration_archive)
+
+        release_id = gmaven_publisher_stage(
+            args, [library_gfile, configuration_gfile])
+
+        print "Staged Release ID " + release_id + ".\n"
+        gmaven_publisher_stage_redir_test_info(
+            release_id,
+            "com.android.tools:%s:%s" % (DESUGAR_JDK_LIBS, library_version),
+            library_jar)
+
         print
+        input = raw_input("Continue with publishing [y/N]:")
 
-        admrt_stage(
-          [library_archive, configuration_archive],
-          [library_artifact_id, configuration_artifact_id],
-          args)
+        if input != 'y':
+          print 'Aborting release to Google maven'
+          sys.exit(1)
 
-        admrt_lorry(
-          [library_archive, configuration_archive],
-          [library_artifact_id, configuration_artifact_id],
-          args)
+        gmaven_publisher_publish(args, release_id)
+
+        print
+        print "Published. Use the email workflow for approval."
 
   return make_release
 
 
-def download_configuration(hash, archive):
-  print
-  print 'Downloading %s from GCS' % archive
-  print
-  download_file('master/' + hash, archive, archive)
-  zip = zipfile.ZipFile(archive)
+def check_configuration(configuration_archive):
+  zip = zipfile.ZipFile(configuration_archive)
   zip.extractall()
   dirs = os.listdir(
     os.path.join('com', 'android', 'tools', DESUGAR_JDK_LIBS_CONFIGURATION))
@@ -415,9 +454,6 @@
   if version != version_from_pom:
     print 'Version mismatch, %s != %s' % (version, version_from_pom)
     sys.exit(1)
-  return '%s:%s:%s' % \
-      (ANDROID_TOOLS_PACKAGE, DESUGAR_JDK_LIBS_CONFIGURATION, version)
-
 
 def check_no_google3_client(args, client_name):
   if not args.use_existing_work_branch:
@@ -437,68 +473,78 @@
     return tree.getroot().find("{%s}version" % ns).text
 
 
-def admrt_stage(archives, artifact_ids, args):
+GMAVEN_PUBLISH_STAGE_RELEASE_ID_PATTERN = re.compile('Release ID = ([0-9a-f\-]+)')
+
+
+def gmaven_publisher_stage(args, gfiles):
   if args.dry_run:
-    print 'Dry-run, just copying archives to %s' % args.dry_run_output
-    for archive in archives:
-      print 'Copying: %s' % archive
-      shutil.copyfile(archive, os.path.join(args.dry_run_output, archive))
-    return
+    print 'Dry-run, would have staged %s' % gfiles
+    return 'dry-run-release-id'
 
-  admrt(archives, 'stage')
-
-  jdk9_home = os.path.join(
-      utils.REPO_ROOT, 'third_party', 'openjdk', 'openjdk-9.0.4', 'linux')
-  print
-  print "Use the following commands to test with 'redir':"
-  print
-  print 'export BUCKET_PATH=/studio_staging/maven2/<user>/<id>'
-  print '/google/data/ro/teams/android-devtools-infra/tools/redir \\'
-  print '  --alsologtostderr \\'
-  print '  --gcs_bucket_path=$BUCKET_PATH \\'
-  print '  --port=1480'
-  print
-  print 'The path for BUCKET_PATH has to be taken from the admrt info line:'
-  print '  INFO: Stage Available at: ...'
-  print '(without the /bigstore prefix).'
-  print
-  print "When the 'redir' server is running use the following commands"
-  print 'to retreive the artifact:'
-  print
-  print 'rm -rf /tmp/maven_repo_local'
-  print ('JAVA_HOME=%s ' % jdk9_home
-      + 'mvn org.apache.maven.plugins:maven-dependency-plugin:2.4:get \\')
-  print '  -Dmaven.repo.local=/tmp/maven_repo_local \\'
-  print '  -DremoteRepositories=http://localhost:1480 \\'
-  print '  -Dartifact=%s \\' % artifact_ids[0]
-  print '  -Ddest=%s' % archives[0]
+  print "Staging: %s" % ', '.join(gfiles)
   print
 
+  cmd = [GMAVEN_PUBLISHER, 'stage', '--gfile', ','.join(gfiles)]
+  output = subprocess.check_output(cmd)
 
-def admrt_lorry(archives, artifact_ids, args):
-  if args.dry_run:
-    print 'Dry run - no lorry action'
-    return
+  # Expect output to contain:
+  # [INFO] 06/19/2020 09:35:12 CEST: >>>>>>>>>> Staged
+  # [INFO] 06/19/2020 09:35:12 CEST: Release ID = 9171d015-18f6-4a90-9984-1c362589dc1b
+  # [INFO] 06/19/2020 09:35:12 CEST: Stage Path = /bigstore/studio_staging/maven2/sgjesse/9171d015-18f6-4a90-9984-1c362589dc1b
 
-  print
-  print 'Continue with running in lorry mode for release of:'
-  for artifact_id in artifact_ids:
-    print '  %s' % artifact_id
-  input = raw_input('[y/N]:')
-
-  if input != 'y':
-    print 'Aborting release to Google maven'
+  matches = GMAVEN_PUBLISH_STAGE_RELEASE_ID_PATTERN.findall(output)
+  if matches == None or len(matches) > 1:
+    print ("Could not determine the release ID from the gmaven_publisher " +
+           "output. Expected a line with 'Release ID = <release id>'.")
+    print "Output was:"
+    print output
     sys.exit(1)
 
-  admrt(archives, 'lorry')
+  print output
+
+  release_id = matches[0]
+  return release_id
 
 
-def admrt(archives, action):
-  cmd = [ADMRT, '--archives']
-  cmd.append(','.join(archives))
-  cmd.extend(['--action', action])
-  subprocess.check_call(cmd)
+def gmaven_publisher_stage_redir_test_info(release_id, artifact, dst):
 
+  redir_command = ("/google/data/ro/teams/android-devtools-infra/tools/redir "
+                 + "--alsologtostderr "
+                 + "--gcs_bucket_path=/studio_staging/maven2/${USER}/%s "
+                 + "--port=1480") % release_id
+
+  get_command = ("mvn org.apache.maven.plugins:maven-dependency-plugin:2.4:get "
+                + "-Dmaven.repo.local=/tmp/maven_repo_local "
+                + "-DremoteRepositories=http://localhost:1480 "
+                + "-Dartifact=%s "
+                + "-Ddest=%s") % (artifact, dst)
+
+  print """To test the staged content with 'redir' run:
+
+%s
+
+Add the following repository to gradle.build for using 'redir':
+
+repositories {
+  maven {
+    url 'http://localhost:1480'
+  }
+}
+
+Use this commands to get artifact from 'redir':
+
+rm -rf /tmp/maven_repo_local
+%s
+""" % (redir_command, get_command)
+
+
+def gmaven_publisher_publish(args, release_id):
+  if args.dry_run:
+    print 'Dry-run, would have published %s' % release_id
+    return
+
+  cmd = [GMAVEN_PUBLISHER, 'publish', release_id]
+  output = subprocess.check_output(cmd)
 
 def branch_change_diff(diff, old_version, new_version):
   invalid_line = None
@@ -658,6 +704,10 @@
                       metavar=('<path>'),
                       help='Release for aosp by setting the path to the '
                            'checkout')
+  result.add_argument('--maven',
+                      default=False,
+                      action='store_true',
+                      help='Release to Google Maven')
   result.add_argument('--google3',
                       default=False,
                       action='store_true',
@@ -683,8 +733,12 @@
                       metavar=('<path>'),
                       help='Location for dry run output.')
   args = result.parse_args()
-  if args.version and not 'dev' in args.version and args.bug == []:
-    print "When releasing a release version add the list of bugs by using '--bug'"
+  if (args.studio
+      and args.version
+      and not 'dev' in args.version
+      and args.bug == []):
+    print ("When releasing a release version to Android Studio add the "
+           + "list of bugs by using '--bug'")
     sys.exit(1)
 
   if args.version and not 'dev' in args.version and args.google3:
@@ -711,6 +765,7 @@
     targets_to_run.append(prepare_release(args))
 
   if (args.google3
+      or args.maven
       or (args.studio and not args.no_sync)
       or (args.desugar_library and not args.dry_run)):
     utils.check_prodacces()
@@ -721,6 +776,8 @@
     targets_to_run.append(prepare_studio(args))
   if args.aosp:
     targets_to_run.append(prepare_aosp(args))
+  if args.maven:
+    targets_to_run.append(prepare_maven(args))
 
   if args.desugar_library:
     targets_to_run.append(prepare_desugar_library(args))
diff --git a/tools/test.py b/tools/test.py
index 95766ca..ad99485 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -162,7 +162,7 @@
 
   gradle_args = ['--stacktrace']
   if utils.is_bot():
-    # Bots don't like dangling processes
+    # Bots don't like dangling processes.
     gradle_args.append('--no-daemon')
 
   # Set all necessary Gradle properties and options first.