Merge commit '10ab5636bdbb856529f8e00ecb63da7cc6c5685b' into dev-release
diff --git a/build.gradle b/build.gradle
index 1077080..433b39a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -43,6 +43,8 @@
     gsonVersion = '2.7'
     junitVersion = '4.13-beta-2'
     mockitoVersion = '2.10.0'
+    // The kotlin version is only here to specify the kotlin language level,
+    // all kotlin compilations are done in tests.
     kotlinVersion = '1.3.72'
     kotlinExtMetadataJVMVersion = '0.1.0'
     smaliVersion = '2.2b4'
@@ -148,12 +150,6 @@
             srcDirs = ['src/test/testngrunner']
         }
     }
-    examplesKotlin {
-        java {
-            srcDirs = ['src/test/examplesKotlin']
-        }
-        output.resourcesDir = 'build/classes/examplesKotlin'
-    }
     examplesAndroidN {
         java {
             srcDirs = ['src/test/examplesAndroidN']
@@ -274,7 +270,6 @@
     supportLibs "com.android.support.test.espresso:espresso-core:$espressoVersion"
     apiUsageSampleCompile sourceSets.main.output
     apiUsageSampleCompile "com.google.guava:guava:$guavaVersion"
-    examplesKotlinCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     kotlinR8TestResourcesCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     errorprone("com.google.errorprone:error_prone_core:$errorproneVersion")
     testImplementation "org.jetbrains.kotlin:kotlin-reflect:1.3.31"
@@ -1229,18 +1224,6 @@
     targetCompatibility = JavaVersion.VERSION_1_8
 }
 
-task buildExampleKotlinJars {
-    def kotlinSrcDir = file("src/test/examplesKotlin")
-    kotlinSrcDir.eachDir { dir ->
-        def name = dir.getName();
-        dependsOn "compile_example_kotlin_${name}"
-        task "compile_example_kotlin_${name}"(type: kotlin.Kotlinc) {
-            source = fileTree(dir: file("src/test/examplesKotlin/${name}"), include: '**/*.kt')
-            destination = file("build/test/examplesKotlin/${name}.jar")
-        }
-    }
-}
-
 task buildProtoGeneratedSources {
     def examplesProtoDir = file("src/test/examplesProto")
     examplesProtoDir.eachDir { dir ->
@@ -1622,52 +1605,15 @@
     }
 }
 
-task buildExamplesKotlin {
-    if (OperatingSystem.current().isMacOsX() || OperatingSystem.current().isWindows()) {
-        logger.lifecycle("WARNING: Testing (including building kotlin examples) is only partially" +
-                " supported on your platform (" + OperatingSystem.current().getName() + ").")
-    } else if (!OperatingSystem.current().isLinux()) {
-        logger.lifecycle("WARNING: Testing (including building kotlin examples) is not supported " +
-                "on your platform. It is fully supported on Linux and partially supported on " +
-                "Mac OS and Windows")
-        return;
-    }
-    def examplesDir = file("src/test/examplesKotlin")
-    examplesDir.eachDir { dir ->
-        def name = dir.getName();
-        dependsOn "dex_example_kotlin_${name}"
-        def exampleOutputDir = file("build/test/examplesKotlin/" + name);
-        def dexPath = file("${exampleOutputDir}")
-        task "dex_example_kotlin_${name}"(type: DxTask,
-                dependsOn: "compile_example_kotlin_${name}") {
-            doFirst {
-                if (!dexPath.exists()) {
-                    dexPath.mkdirs()
-                }
-            }
-            source = files(tasks.getByPath("compile_example_kotlin_${name}")).asFileTree
-            destination = dexPath
-            debug = false
-        }
-    }
-}
-
 task buildKotlinR8TestResources {
     def examplesDir = file("src/test/kotlinR8TestResources")
     examplesDir.eachDir { dir ->
         kotlin.Kotlinc.KotlinTargetVersion.values().each { kotlinTargetVersion ->
             def name = dir.getName()
             def taskName = "jar_kotlinR8TestResources_${name}_${kotlinTargetVersion}"
-            def outputFile = "build/test/kotlinR8TestResources/${kotlinTargetVersion}/${name}.jar"
             def javaOutput = "build/test/kotlinR8TestResources/${kotlinTargetVersion}/${name}/java"
             def javaOutputJarName = "${name}.java.jar"
             def javaOutputJarDir = "build/test/kotlinR8TestResources/${kotlinTargetVersion}"
-            task "${taskName}Kotlin"(type: kotlin.Kotlinc) {
-                source = fileTree(dir: file("${examplesDir}/${name}"),
-                        include: ['**/*.kt', '**/*.java'])
-                destination = file(outputFile)
-                targetVersion = kotlinTargetVersion
-            }
             task "${taskName}Java"(type: JavaCompile) {
                 source = fileTree(dir: file("${examplesDir}/${name}"), include: '**/*.java')
                 destinationDir = file(javaOutput)
@@ -1682,7 +1628,7 @@
                 from javaOutput
                 include "**/*.class"
             }
-            dependsOn "${taskName}Kotlin", "${taskName}JavaJar"
+            dependsOn "${taskName}JavaJar"
         }
     }
 }
@@ -2101,7 +2047,6 @@
         }
         dependsOn downloadDeps
         dependsOn buildExamples
-        dependsOn buildExamplesKotlin
         dependsOn buildKotlinR8TestResources
         dependsOn buildSmali
         dependsOn jctfCommonJar
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 3ebf03f..5960a22 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -140,6 +140,15 @@
       }
     }
     builders {
+      name: "desugared_library_head"
+      mixins: "linux"
+      mixins: "normal"
+      priority: 26
+      recipe {
+        properties_j: "test_options:[\"--desugared-library\", \"HEAD\"]"
+      }
+    }
+    builders {
       name: "linux"
       mixins: "linux"
       mixins: "normal"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 7400340..2cf08a6 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -96,6 +96,11 @@
     short_name: "sdk_desugar"
   }
   builders {
+    name: "buildbucket/luci.r8.ci/desugared_library_head"
+    category: "archive_desugar"
+    short_name: "head_desugar"
+  }
+  builders {
     name: "buildbucket/luci.r8.ci/archive_release"
     category: "release archive"
     short_name: "archive"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index af1ff68..5e0355e 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -42,6 +42,7 @@
   triggers: "linux-jctf"
   triggers: "r8cf-linux-jctf"
   triggers: "windows"
+  triggers: "desugared_library_head"
 }
 
 trigger {
@@ -145,6 +146,20 @@
 }
 
 job {
+  id: "desugared_library_head"
+  acl_sets: "default"
+  triggering_policy: {
+    max_concurrent_invocations: 1
+    max_batch_size: 1
+  }
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.r8.ci"
+    builder: "desugared_library_head"
+  }
+}
+
+job {
   id: "kotlin-builder"
   acl_sets: "default"
   triggering_policy: {
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index ec1f516..30093b5 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.ArchiveBuilder;
+import com.android.tools.r8.utils.DexUtils;
 import com.android.tools.r8.utils.DirectoryBuilder;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.FileUtils;
@@ -88,16 +89,6 @@
       this.consumer = consumer;
     }
 
-    protected static String getDefaultDexFileName(int fileIndex) {
-      return fileIndex == 0
-          ? "classes" + FileUtils.DEX_EXTENSION
-          : ("classes" + (fileIndex + 1) + FileUtils.DEX_EXTENSION);
-    }
-
-    protected String getDexFileName(int fileIndex) {
-      return getDefaultDexFileName(fileIndex);
-    }
-
     @Override
     public DataResourceConsumer getDataResourceConsumer() {
       return consumer != null ? consumer.getDataResourceConsumer() : null;
@@ -162,7 +153,8 @@
     public void accept(
         int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
       super.accept(fileIndex, data, descriptors, handler);
-      outputBuilder.addIndexedClassFile(fileIndex, getDexFileName(fileIndex), data, handler);
+      outputBuilder.addIndexedClassFile(
+          fileIndex, DexUtils.getDefaultDexFileName(fileIndex), data, handler);
     }
 
     @Override
@@ -192,7 +184,7 @@
                 new BufferedOutputStream(Files.newOutputStream(archive, options)))) {
           for (int i = 0; i < resources.size(); i++) {
             ProgramResource resource = resources.get(i);
-            String entryName = getDefaultDexFileName(i);
+            String entryName = DexUtils.getDefaultDexFileName(i);
             byte[] bytes = ByteStreams.toByteArray(closer.register(resource.getByteStream()));
             ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.STORED);
           }
@@ -253,7 +245,7 @@
       } catch (IOException e) {
         handler.error(new ExceptionDiagnostic(e, new PathOrigin(directory)));
       }
-      outputBuilder.addFile(getDexFileName(fileIndex), data, handler);
+      outputBuilder.addFile(DexUtils.getDefaultDexFileName(fileIndex), data, handler);
     }
 
     @Override
@@ -303,7 +295,7 @@
     }
 
     private static Path getTargetDexFile(Path directory, int fileIndex) {
-      return directory.resolve(ForwardingConsumer.getDefaultDexFileName(fileIndex));
+      return directory.resolve(DexUtils.getDefaultDexFileName(fileIndex));
     }
 
     private static void writeFile(byte[] contents, Path target) throws IOException {
diff --git a/src/main/java/com/android/tools/r8/StringConsumer.java b/src/main/java/com/android/tools/r8/StringConsumer.java
index 1cd6718..843d145 100644
--- a/src/main/java/com/android/tools/r8/StringConsumer.java
+++ b/src/main/java/com/android/tools/r8/StringConsumer.java
@@ -64,7 +64,8 @@
   }
 
   /** Forwarding consumer to delegate to an optional existing consumer. */
-  class ForwardingConsumer implements StringConsumer {
+  @Keep
+  public class ForwardingConsumer implements StringConsumer {
 
     private final StringConsumer consumer;
 
diff --git a/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java b/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java
deleted file mode 100644
index b46a2ba..0000000
--- a/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.CLASS)
-@Target(ElementType.TYPE)
-public @interface SynthesizedClassMap {
-  Class<?>[] value() default {};
-}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
index 259ad12..7ce2440 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.utils.MapUtils;
 import com.android.tools.r8.utils.collections.ImmutableDeque;
 import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
@@ -37,6 +38,7 @@
   private final BiPredicate<DexType, DexType> isJavaAssignable;
   private final DexItemFactory factory;
   private final List<CfTryCatch> tryCatchRanges;
+  private final GraphLens graphLens;
 
   private final Deque<CfTryCatch> currentCatchRanges = new ArrayDeque<>();
   private final Set<CfLabel> tryCatchRangeLabels;
@@ -46,12 +48,14 @@
       Map<CfLabel, CfFrame> stateMap,
       List<CfTryCatch> tryCatchRanges,
       BiPredicate<DexType, DexType> isJavaAssignable,
-      DexItemFactory factory) {
+      DexItemFactory factory,
+      GraphLens graphLens) {
     this.context = context;
     this.stateMap = stateMap;
     this.tryCatchRanges = tryCatchRanges;
     this.isJavaAssignable = isJavaAssignable;
     this.factory = factory;
+    this.graphLens = graphLens;
     throwStack = ImmutableDeque.of(FrameType.initialized(factory.throwableType));
     // Compute all labels that marks a start or end to catch ranges.
     tryCatchRangeLabels = Sets.newIdentityHashSet();
@@ -230,10 +234,11 @@
 
   public void verifyIsAssignable(FrameType source, DexType target) {
     if (!source.isInitialized()) {
-      if (source.isUninitializedThis() && target == context) {
+      DexType rewrittenTarget = graphLens.lookupClassType(target);
+      if (source.isUninitializedThis() && rewrittenTarget == context) {
         return;
       }
-      if (target == factory.objectType) {
+      if (rewrittenTarget == factory.objectType) {
         return;
       }
       throw CfCodeStackMapValidatingException.error(
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 614ac4d..135fcb4 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -401,9 +401,7 @@
       VirtualFile mainDexFile = virtualFiles.get(0);
       mainDexInfo.forEach(
           type -> {
-            // TODO(b/178577273): We should ensure only live types in main dex.
-            DexProgramClass clazz =
-                asProgramClassOrNull(appView.appInfo().definitionForWithoutExistenceAssert(type));
+            DexProgramClass clazz = asProgramClassOrNull(appView.appInfo().definitionFor(type));
             if (clazz != null) {
               mainDexFile.addClass(clazz);
               classes.remove(clazz);
diff --git a/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionInfo.java b/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionInfo.java
index c2905ce..ec486de 100644
--- a/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionInfo.java
+++ b/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionInfo.java
@@ -64,6 +64,9 @@
     return null;
   }
 
+  /** User friendly description of the missing definition. */
+  String getDiagnosticMessage();
+
   /** The contexts from which this missing definition was referenced. */
   Collection<MissingDefinitionContext> getReferencedFromContexts();
 }
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoBase.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoBase.java
index 0f9e655..b3d2872 100644
--- a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoBase.java
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoBase.java
@@ -18,6 +18,13 @@
   }
 
   @Override
+  public String getDiagnosticMessage() {
+    StringBuilder builder = new StringBuilder();
+    MissingDefinitionInfoUtils.writeDiagnosticMessage(builder, this);
+    return builder.toString();
+  }
+
+  @Override
   public final Collection<MissingDefinitionContext> getReferencedFromContexts() {
     return referencedFromContexts;
   }
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
index b94dbf1..34f2f2c 100644
--- a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
@@ -4,13 +4,19 @@
 
 package com.android.tools.r8.diagnostic.internal;
 
+import static com.android.tools.r8.utils.ClassReferenceUtils.getClassReferenceComparator;
+import static com.android.tools.r8.utils.FieldReferenceUtils.getFieldReferenceComparator;
+import static com.android.tools.r8.utils.MethodReferenceUtils.getMethodReferenceComparator;
+
 import com.android.tools.r8.diagnostic.MissingClassInfo;
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
 import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
 import com.android.tools.r8.diagnostic.MissingFieldInfo;
 import com.android.tools.r8.diagnostic.MissingMethodInfo;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.ClassReferenceUtils;
 import com.android.tools.r8.utils.FieldReferenceUtils;
 import com.android.tools.r8.utils.MethodReferenceUtils;
@@ -77,4 +83,65 @@
   public static Comparator<MissingDefinitionInfo> getComparator() {
     return COMPARATOR;
   }
+
+  public static void writeDiagnosticMessage(
+      StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo) {
+    builder.append("Missing class ");
+    MissingDefinitionInfoUtils.accept(
+        missingDefinitionInfo,
+        missingClassInfo -> builder.append(missingClassInfo.getClassReference().getTypeName()),
+        missingFieldInfo ->
+            builder.append(
+                FieldReferenceUtils.toSourceString(missingFieldInfo.getFieldReference())),
+        missingMethodInfo ->
+            builder.append(
+                MethodReferenceUtils.toSourceString(missingMethodInfo.getMethodReference())));
+    writeReferencedFromSuffix(builder, missingDefinitionInfo);
+  }
+
+  private static void writeReferencedFromSuffix(
+      StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo) {
+    Box<ClassReference> classContext = new Box<>();
+    Box<FieldReference> fieldContext = new Box<>();
+    Box<MethodReference> methodContext = new Box<>();
+    for (MissingDefinitionContext missingDefinitionContext :
+        missingDefinitionInfo.getReferencedFromContexts()) {
+      MissingDefinitionContextUtils.accept(
+          missingDefinitionContext,
+          missingDefinitionClassContext ->
+              classContext.setMin(
+                  missingDefinitionClassContext.getClassReference(), getClassReferenceComparator()),
+          missingDefinitionFieldContext ->
+              fieldContext.setMin(
+                  missingDefinitionFieldContext.getFieldReference(), getFieldReferenceComparator()),
+          missingDefinitionMethodContext ->
+              methodContext.setMin(
+                  missingDefinitionMethodContext.getMethodReference(),
+                  getMethodReferenceComparator()));
+    }
+    assert classContext.isSet() || fieldContext.isSet() || methodContext.isSet();
+    if (fieldContext.isSet()) {
+      writeReferencedFromSuffix(
+          builder, missingDefinitionInfo, FieldReferenceUtils.toSourceString(fieldContext.get()));
+    } else if (methodContext.isSet()) {
+      writeReferencedFromSuffix(
+          builder, missingDefinitionInfo, MethodReferenceUtils.toSourceString(methodContext.get()));
+    } else {
+      writeReferencedFromSuffix(builder, missingDefinitionInfo, classContext.get().getTypeName());
+    }
+  }
+
+  private static void writeReferencedFromSuffix(
+      StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo, String referencedFrom) {
+    int numberOfOtherContexts = missingDefinitionInfo.getReferencedFromContexts().size() - 1;
+    assert numberOfOtherContexts >= 0;
+    builder.append(" (referenced from: ").append(referencedFrom);
+    if (numberOfOtherContexts >= 1) {
+      builder.append(" and ").append(numberOfOtherContexts).append(" other context");
+      if (numberOfOtherContexts >= 2) {
+        builder.append("s");
+      }
+    }
+    builder.append(")");
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
index 7be87b6..a180ed2 100644
--- a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
@@ -4,21 +4,10 @@
 
 package com.android.tools.r8.diagnostic.internal;
 
-import static com.android.tools.r8.utils.ClassReferenceUtils.getClassReferenceComparator;
-import static com.android.tools.r8.utils.FieldReferenceUtils.getFieldReferenceComparator;
-import static com.android.tools.r8.utils.MethodReferenceUtils.getMethodReferenceComparator;
-
-import com.android.tools.r8.diagnostic.MissingDefinitionContext;
 import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
 import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.FieldReference;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.FieldReferenceUtils;
-import com.android.tools.r8.utils.MethodReferenceUtils;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -40,12 +29,12 @@
 
   @Override
   public Collection<MissingDefinitionInfo> getMissingDefinitions() {
-    return missingDefinitions;
+    return getMissingDefinitionsWithDeterministicOrder();
   }
 
   private Collection<MissingDefinitionInfo> getMissingDefinitionsWithDeterministicOrder() {
     List<MissingDefinitionInfo> missingDefinitionsWithDeterministicOrder =
-        new ArrayList<>(getMissingDefinitions());
+        new ArrayList<>(missingDefinitions);
     missingDefinitionsWithDeterministicOrder.sort(MissingDefinitionInfoUtils.getComparator());
     return missingDefinitionsWithDeterministicOrder;
   }
@@ -72,78 +61,17 @@
     assert missingDefinitionsIterator.hasNext();
 
     // Write first line.
-    writeMissingDefinition(builder.append("Missing class "), missingDefinitionsIterator.next());
+    MissingDefinitionInfoUtils.writeDiagnosticMessage(builder, missingDefinitionsIterator.next());
 
     // Write remaining lines with line separator before.
     missingDefinitionsIterator.forEachRemaining(
         missingDefinition ->
-            writeMissingDefinition(
-                builder.append(System.lineSeparator()).append("Missing class "),
-                missingDefinition));
+            MissingDefinitionInfoUtils.writeDiagnosticMessage(
+                builder.append(System.lineSeparator()), missingDefinition));
 
     return builder.toString();
   }
 
-  private static void writeMissingDefinition(
-      StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo) {
-    MissingDefinitionInfoUtils.accept(
-        missingDefinitionInfo,
-        missingClassInfo -> builder.append(missingClassInfo.getClassReference().getTypeName()),
-        missingFieldInfo ->
-            builder.append(
-                FieldReferenceUtils.toSourceString(missingFieldInfo.getFieldReference())),
-        missingMethodInfo ->
-            builder.append(
-                MethodReferenceUtils.toSourceString(missingMethodInfo.getMethodReference())));
-    writeReferencedFromSuffix(builder, missingDefinitionInfo);
-  }
-
-  private static void writeReferencedFromSuffix(
-      StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo) {
-    Box<ClassReference> classContext = new Box<>();
-    Box<FieldReference> fieldContext = new Box<>();
-    Box<MethodReference> methodContext = new Box<>();
-    for (MissingDefinitionContext missingDefinitionContext :
-        missingDefinitionInfo.getReferencedFromContexts()) {
-      MissingDefinitionContextUtils.accept(
-          missingDefinitionContext,
-          missingDefinitionClassContext ->
-              classContext.setMin(
-                  missingDefinitionClassContext.getClassReference(), getClassReferenceComparator()),
-          missingDefinitionFieldContext ->
-              fieldContext.setMin(
-                  missingDefinitionFieldContext.getFieldReference(), getFieldReferenceComparator()),
-          missingDefinitionMethodContext ->
-              methodContext.setMin(
-                  missingDefinitionMethodContext.getMethodReference(),
-                  getMethodReferenceComparator()));
-    }
-    assert classContext.isSet() || fieldContext.isSet() || methodContext.isSet();
-    if (fieldContext.isSet()) {
-      writeReferencedFromSuffix(
-          builder, missingDefinitionInfo, FieldReferenceUtils.toSourceString(fieldContext.get()));
-    } else if (methodContext.isSet()) {
-      writeReferencedFromSuffix(
-          builder, missingDefinitionInfo, MethodReferenceUtils.toSourceString(methodContext.get()));
-    } else {
-      writeReferencedFromSuffix(builder, missingDefinitionInfo, classContext.get().getTypeName());
-    }
-  }
-
-  private static void writeReferencedFromSuffix(
-      StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo, String referencedFrom) {
-    int numberOfOtherContexts = missingDefinitionInfo.getReferencedFromContexts().size() - 1;
-    assert numberOfOtherContexts >= 0;
-    builder.append(" (referenced from: ").append(referencedFrom);
-    if (numberOfOtherContexts >= 1) {
-      builder.append(", and ").append(numberOfOtherContexts).append(" other context");
-      if (numberOfOtherContexts >= 2) {
-        builder.append("s");
-      }
-    }
-    builder.append(")");
-  }
-
   public static class Builder {
 
     private ImmutableList.Builder<MissingDefinitionInfo> missingDefinitionsBuilder =
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 337ec1d..ed43ca9 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -305,9 +305,7 @@
       LensCodeRewriterUtils rewriter,
       MethodVisitor visitor) {
     GraphLens graphLens = appView.graphLens();
-    // TODO(b/170073151): Handle unapplied code rewritings.
-    assert graphLens.hasCodeRewritings()
-        || verifyFrames(method.getDefinition(), appView, null, false);
+    assert verifyFrames(method.getDefinition(), appView, null, false);
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     InitClassLens initClassLens = appView.initClassLens();
     InternalOptions options = appView.options();
@@ -791,7 +789,8 @@
             stateMap,
             tryCatchRanges,
             isAssignablePredicate(appView),
-            appView.dexItemFactory());
+            appView.dexItemFactory(),
+            appView.graphLens());
     if (stateMap.containsKey(null)) {
       assert !shouldComputeInitialFrame();
       builder.verifyFrameAndSet(stateMap.get(null));
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 3483fbb..f2959bb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
 import com.android.tools.r8.graph.DexValue.DexValueArray;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
@@ -22,10 +21,7 @@
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
-import java.util.TreeSet;
 import java.util.function.Function;
 
 public class DexAnnotation extends DexItem implements StructuralItem<DexAnnotation> {
@@ -96,8 +92,7 @@
     }
     if (annotation == options.itemFactory.dalvikFastNativeAnnotation
         || annotation == options.itemFactory.dalvikCriticalNativeAnnotation
-        || annotation == options.itemFactory.annotationSynthesizedClass
-        || annotation == options.itemFactory.annotationSynthesizedClassMap) {
+        || annotation == options.itemFactory.annotationSynthesizedClass) {
       return true;
     }
     if (options.processCovariantReturnTypeAnnotations) {
@@ -363,43 +358,6 @@
     return new DexValueString(factory.createString(string));
   }
 
-  public static Collection<DexType> readAnnotationSynthesizedClassMap(
-      DexProgramClass clazz, DexItemFactory dexItemFactory) {
-    DexAnnotation foundAnnotation =
-        clazz.annotations().getFirstMatching(dexItemFactory.annotationSynthesizedClassMap);
-    if (foundAnnotation != null) {
-      if (foundAnnotation.annotation.elements.length != 1) {
-        throw new CompilationError(getInvalidSynthesizedClassMapMessage(clazz, foundAnnotation));
-      }
-      DexAnnotationElement value = foundAnnotation.annotation.elements[0];
-      if (value.name != dexItemFactory.valueString) {
-        throw new CompilationError(getInvalidSynthesizedClassMapMessage(clazz, foundAnnotation));
-      }
-      DexValueArray existingList = value.value.asDexValueArray();
-      if (existingList == null) {
-        throw new CompilationError(getInvalidSynthesizedClassMapMessage(clazz, foundAnnotation));
-      }
-      Collection<DexType> synthesized = new ArrayList<>(existingList.values.length);
-      for (DexValue element : existingList.getValues()) {
-        if (!element.isDexValueType()) {
-          throw new CompilationError(getInvalidSynthesizedClassMapMessage(clazz, foundAnnotation));
-        }
-        synthesized.add(element.asDexValueType().value);
-      }
-      return synthesized;
-    }
-    return Collections.emptyList();
-  }
-
-  private static String getInvalidSynthesizedClassMapMessage(
-      DexProgramClass annotatedClass,
-      DexAnnotation invalidAnnotation) {
-    return annotatedClass.toSourceString()
-        + " is annotated with invalid "
-        + invalidAnnotation.annotation.type.toString()
-        + ": " + invalidAnnotation.toString();
-  }
-
   public static DexAnnotation createAnnotationSynthesizedClass(
       SyntheticKind kind, DexType synthesizingContext, DexItemFactory dexItemFactory) {
     DexAnnotationElement kindElement =
@@ -456,26 +414,6 @@
     return new Pair<>(kind, valueElement.value.asDexValueType().getValue());
   }
 
-  public static DexAnnotation createAnnotationSynthesizedClassMap(
-      TreeSet<DexType> synthesized,
-      DexItemFactory dexItemFactory) {
-    DexValueType[] values = synthesized.stream()
-        .map(DexValueType::new)
-        .toArray(DexValueType[]::new);
-    DexValueArray array = new DexValueArray(values);
-    DexAnnotationElement pair =
-        new DexAnnotationElement(dexItemFactory.createString("value"), array);
-    return new DexAnnotation(
-        VISIBILITY_BUILD,
-        new DexEncodedAnnotation(
-            dexItemFactory.annotationSynthesizedClassMap, new DexAnnotationElement[]{pair}));
-  }
-
-  public static boolean isSynthesizedClassMapAnnotation(DexAnnotation annotation,
-      DexItemFactory factory) {
-    return annotation.annotation.type == factory.annotationSynthesizedClassMap;
-  }
-
   public DexAnnotation rewrite(Function<DexEncodedAnnotation, DexEncodedAnnotation> rewriter) {
     DexEncodedAnnotation rewritten = rewriter.apply(annotation);
     if (rewritten == annotation) {
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 afc04c7..5c9fc0f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1184,6 +1184,9 @@
       return this;
     }
     Builder builder = builder(this);
+    if (isNonPrivateVirtualMethod() && isLibraryMethodOverride() != OptionalBool.unknown()) {
+      builder.setIsLibraryMethodOverride(isLibraryMethodOverride());
+    }
     builder.setMethod(method);
     // TODO(b/112847660): Fix type fixers that use this method: Class staticizer
     // TODO(b/112847660): Fix type fixers that use this method: Uninstantiated type optimization
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 0752d20..e76409d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -605,8 +605,6 @@
   public final DexType annotationThrows = createStaticallyKnownType("Ldalvik/annotation/Throws;");
   public final DexType annotationSynthesizedClass =
       createStaticallyKnownType("Lcom/android/tools/r8/annotations/SynthesizedClass;");
-  public final DexType annotationSynthesizedClassMap =
-      createStaticallyKnownType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
   public final DexType annotationCovariantReturnType =
       createStaticallyKnownType("Ldalvik/annotation/codegen/CovariantReturnType;");
   public final DexType annotationCovariantReturnTypes =
@@ -638,6 +636,7 @@
   public final DexType comparableType = createStaticallyKnownType("Ljava/lang/Comparable;");
   public final DexType stringConcatFactoryType =
       createStaticallyKnownType("Ljava/lang/invoke/StringConcatFactory;");
+  public final DexType unsafeType = createStaticallyKnownType("Lsun/misc/Unsafe;");
 
   public final ServiceLoaderMethods serviceLoaderMethods = new ServiceLoaderMethods();
   public final StringConcatFactoryMembers stringConcatFactoryMembers =
@@ -2330,8 +2329,17 @@
       MethodHandleType type,
       DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod,
       boolean isInterface) {
+    return createMethodHandle(type, fieldOrMethod, isInterface, null);
+  }
+
+  public DexMethodHandle createMethodHandle(
+      MethodHandleType type,
+      DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod,
+      boolean isInterface,
+      DexMethod rewrittenTarget) {
     assert !sorted;
-    DexMethodHandle methodHandle = new DexMethodHandle(type, fieldOrMethod, isInterface);
+    DexMethodHandle methodHandle =
+        new DexMethodHandle(type, fieldOrMethod, isInterface, rewrittenTarget);
     return canonicalize(methodHandles, methodHandle);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index 0bdb208..71bc104 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -207,13 +207,6 @@
   public DexMethodHandle(
       MethodHandleType type,
       DexMember<? extends DexItem, ? extends DexMember<?, ?>> member,
-      boolean isInterface) {
-    this(type, member, isInterface, null);
-  }
-
-  public DexMethodHandle(
-      MethodHandleType type,
-      DexMember<? extends DexItem, ? extends DexMember<?, ?>> member,
       boolean isInterface,
       DexMethod rewrittenTarget) {
     this.type = type;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java
index 673411a..68b456b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java
@@ -12,6 +12,11 @@
 
   public abstract StateType join(StateType state);
 
+  public boolean isGreaterThanOrEquals(StateType state) {
+    StateType leastUpperBound = join(state);
+    return equals(leastUpperBound);
+  }
+
   @Override
   public abstract boolean equals(Object other);
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
index 68a8a24..701df38 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.analysis.framework.intraprocedural;
 
 import com.android.tools.r8.ir.code.BasicBlock;
+import java.util.Map;
 
 /**
  * The result returned by {@link IntraproceduralDataflowAnalysis#run(BasicBlock)}.
@@ -23,16 +24,34 @@
     return false;
   }
 
+  public <StateType extends AbstractState<StateType>>
+      SuccessfulDataflowAnalysisResult<StateType> asSuccessfulAnalysisResult() {
+    return null;
+  }
+
   public boolean isFailedAnalysisResult() {
     return false;
   }
 
-  public static class SuccessfulDataflowAnalysisResult extends DataflowAnalysisResult {
+  public static class SuccessfulDataflowAnalysisResult<StateType extends AbstractState<StateType>>
+      extends DataflowAnalysisResult {
+
+    private final Map<BasicBlock, StateType> blockExitStates;
+
+    public SuccessfulDataflowAnalysisResult(Map<BasicBlock, StateType> blockExitStates) {
+      this.blockExitStates = blockExitStates;
+    }
 
     @Override
     public boolean isSuccessfulAnalysisResult() {
       return true;
     }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public SuccessfulDataflowAnalysisResult<StateType> asSuccessfulAnalysisResult() {
+      return this;
+    }
   }
 
   public static class FailedDataflowAnalysisResult extends DataflowAnalysisResult {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java
index a8482bb..c990bdb 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java
@@ -45,37 +45,50 @@
   private DataflowAnalysisResult run(WorkList<BasicBlock> worklist) {
     while (worklist.hasNext()) {
       BasicBlock block = worklist.next();
+      BasicBlock end = null;
       // Compute the abstract state upon entry to the basic block, by joining all the predecessor
       // exit states.
       StateType state = computeBlockEntryState(block);
-      for (Instruction instruction : block.getInstructions()) {
-        TransferFunctionResult<StateType> transferResult = transfer.apply(instruction, state);
-        if (transferResult.isFailedTransferResult()) {
-          return new FailedDataflowAnalysisResult();
+      do {
+        for (Instruction instruction : block.getInstructions()) {
+          TransferFunctionResult<StateType> transferResult = transfer.apply(instruction, state);
+          if (transferResult.isFailedTransferResult()) {
+            return new FailedDataflowAnalysisResult();
+          }
+          assert transferResult.isAbstractState();
+          state = transferResult.asAbstractState();
         }
-        assert transferResult.isAbstractState();
-        state = transferResult.asAbstractState();
-      }
+        if (block.hasUniqueSuccessorWithUniquePredecessor()) {
+          block = block.getUniqueSuccessor();
+        } else {
+          end = block;
+          block = null;
+        }
+      } while (block != null);
       // Update the block exit state, and re-enqueue all successor blocks if the abstract state
       // changed.
-      if (setBlockExitState(block, state)) {
-        worklist.addAllIgnoringSeenSet(block.getSuccessors());
+      if (setBlockExitState(end, state)) {
+        worklist.addAllIgnoringSeenSet(end.getSuccessors());
       }
     }
-    return new SuccessfulDataflowAnalysisResult();
+    return new SuccessfulDataflowAnalysisResult<>(blockExitStates);
   }
 
   private StateType computeBlockEntryState(BasicBlock block) {
     StateType result = bottom;
     for (BasicBlock predecessor : block.getPredecessors()) {
-      StateType predecessorState = blockExitStates.getOrDefault(predecessor, bottom);
-      result = result.join(predecessorState);
+      StateType edgeState =
+          transfer.computeBlockEntryState(
+              block, predecessor, blockExitStates.getOrDefault(predecessor, bottom));
+      result = result.join(edgeState);
     }
     return result;
   }
 
   private boolean setBlockExitState(BasicBlock block, StateType state) {
+    assert !block.hasUniqueSuccessorWithUniquePredecessor();
     StateType previous = blockExitStates.put(block, state);
+    assert previous == null || state.isGreaterThanOrEquals(previous);
     return !state.equals(previous);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/TransferFunction.java
index 98ede7c..73c9470 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/TransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/TransferFunction.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.analysis.framework.intraprocedural;
 
+import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.Instruction;
 
 /**
@@ -13,4 +14,9 @@
 public interface TransferFunction<StateType extends AbstractState<StateType>> {
 
   TransferFunctionResult<StateType> apply(Instruction instruction, StateType state);
+
+  default StateType computeBlockEntryState(
+      BasicBlock block, BasicBlock predecessor, StateType predecessorExitState) {
+    return predecessorExitState;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index e81d765..7dbc0ad 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -190,6 +190,10 @@
     return successors.size() == 1;
   }
 
+  public boolean hasUniqueSuccessorWithUniquePredecessor() {
+    return hasUniqueSuccessor() && getUniqueSuccessor().getPredecessors().size() == 1;
+  }
+
   public boolean hasUniqueNormalSuccessor() {
     return numberOfNormalSuccessors() == 1;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index 4bc8714..b8a70f9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -137,17 +137,21 @@
       if (newType != oldType || actualTarget != invokedMethod || rewrittenTarget != actualTarget) {
         DexClass holder = definitions.definitionFor(actualTarget.holder, context);
         boolean isInterface = holder != null ? holder.isInterface() : methodHandle.isInterface;
-        return new DexMethodHandle(
-            newType,
-            actualTarget,
-            isInterface,
-            rewrittenTarget != actualTarget ? rewrittenTarget : null);
+        return definitions
+            .dexItemFactory()
+            .createMethodHandle(
+                newType,
+                actualTarget,
+                isInterface,
+                rewrittenTarget != actualTarget ? rewrittenTarget : null);
       }
     } else {
       DexField field = methodHandle.asField();
       DexField actualField = graphLens().lookupField(field);
       if (actualField != field) {
-        return new DexMethodHandle(methodHandle.type, actualField, methodHandle.isInterface);
+        return definitions
+            .dexItemFactory()
+            .createMethodHandle(methodHandle.type, actualField, methodHandle.isInterface);
       }
     }
     return methodHandle;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index b1b14fb..0cdf967 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
@@ -60,6 +61,9 @@
 
   void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo);
 
+  void setClassInlinerMethodConstraint(
+      ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint);
+
   void setClassInlinerEligibility(DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility);
 
   void setInstanceInitializerInfoCollection(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 0d5cd1c..bd5c0b3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -174,6 +174,9 @@
       if (options.minApiLevel < AndroidApiLevel.R.getLevel()) {
         initializeAndroidRMethodProviders(factory);
       }
+      if (options.minApiLevel < AndroidApiLevel.S.getLevel()) {
+        initializeAndroidSMethodProviders(factory);
+      }
 
       // The following providers are currently not implemented at any API level in Android.
       // They however require the Optional/Stream class to be present, either through desugared
@@ -191,7 +194,6 @@
       }
 
       // These are currently not implemented at any API level in Android.
-      initializeJava9MethodProviders(factory);
       initializeJava10MethodProviders(factory);
       initializeJava11MethodProviders(factory);
     }
@@ -962,15 +964,49 @@
       addProvider(new MethodGenerator(method, BackportedMethods::CollectionMethods_mapEntry));
     }
 
-    private void initializeJava9MethodProviders(DexItemFactory factory) {
+    private void initializeAndroidSMethodProviders(DexItemFactory factory) {
+      DexType type;
+      DexString name;
+      DexProto proto;
+      DexMethod method;
+
+      // Set
+      type = factory.setType;
+
+      // Set Set.copyOf(Collection)
+      name = factory.createString("copyOf");
+      proto = factory.createProto(factory.setType, factory.collectionType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(
+          new MethodGenerator(
+              method, BackportedMethods::CollectionsMethods_copyOfSet, "copyOfSet"));
+
+      // Byte
+      type = factory.boxedByteType;
+
+      // int Byte.compareUnsigned(byte, byte)
+      name = factory.createString("compareUnsigned");
+      proto = factory.createProto(factory.intType, factory.byteType, factory.byteType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(new MethodGenerator(method, BackportedMethods::ByteMethods_compareUnsigned));
+
+      // Short
+      type = factory.boxedShortType;
+
+      // int Short.compareUnsigned(short, short)
+      name = factory.createString("compareUnsigned");
+      proto = factory.createProto(factory.intType, factory.shortType, factory.shortType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(new MethodGenerator(method, BackportedMethods::ShortMethods_compareUnsigned));
+
       // Math & StrictMath, which have some symmetric, binary-compatible APIs
       DexType[] mathTypes = {factory.mathType, factory.strictMathType};
       for (DexType mathType : mathTypes) {
 
         // long {Math,StrictMath}.multiplyExact(long, int)
-        DexString name = factory.createString("multiplyExact");
-        DexProto proto = factory.createProto(factory.longType, factory.longType, factory.intType);
-        DexMethod method = factory.createMethod(mathType, proto, name);
+        name = factory.createString("multiplyExact");
+        proto = factory.createProto(factory.longType, factory.longType, factory.intType);
+        method = factory.createMethod(mathType, proto, name);
         addProvider(
             new MethodGenerator(
                 method,
@@ -1005,24 +1041,6 @@
             new MethodGenerator(
                 method, BackportedMethods::MathMethods_floorModLongInt, "floorModLongInt"));
       }
-
-      // Byte
-      DexType type = factory.boxedByteType;
-
-      // int Byte.compareUnsigned(byte, byte)
-      DexString name = factory.createString("compareUnsigned");
-      DexProto proto = factory.createProto(factory.intType, factory.byteType, factory.byteType);
-      DexMethod method = factory.createMethod(type, proto, name);
-      addProvider(new MethodGenerator(method, BackportedMethods::ByteMethods_compareUnsigned));
-
-      // Short
-      type = factory.boxedShortType;
-
-      // int Short.compareUnsigned(short, short)
-      name = factory.createString("compareUnsigned");
-      proto = factory.createProto(factory.intType, factory.shortType, factory.shortType);
-      method = factory.createMethod(type, proto, name);
-      addProvider(new MethodGenerator(method, BackportedMethods::ShortMethods_compareUnsigned));
     }
 
     private void initializeJava10MethodProviders(DexItemFactory factory) {
@@ -1037,18 +1055,7 @@
           new MethodGenerator(
               method, BackportedMethods::CollectionsMethods_copyOfList, "copyOfList"));
 
-      // Set
-      type = factory.setType;
-
-      // Set Set.copyOf(Collection)
-      name = factory.createString("copyOf");
-      proto = factory.createProto(factory.setType, factory.collectionType);
-      method = factory.createMethod(type, proto, name);
-      addProvider(
-          new MethodGenerator(
-              method, BackportedMethods::CollectionsMethods_copyOfSet, "copyOfSet"));
-
-      // Set
+      // Map
       type = factory.mapType;
 
       // Map Map.copyOf(Map)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SwitchCaseEliminator.java b/src/main/java/com/android/tools/r8/ir/optimize/SwitchCaseEliminator.java
index 733db77..6c9bfbb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SwitchCaseEliminator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SwitchCaseEliminator.java
@@ -83,20 +83,20 @@
   boolean optimize() {
     if (canBeOptimized()) {
       int originalNumberOfSuccessors = block.getSuccessors().size();
-      unlinkDeadSuccessors();
+      IntList removedSuccessorIndices = unlinkDeadSuccessors();
       if (hasAlwaysHitCase() || allSwitchCasesMarkedForRemoval()) {
         // Replace switch with a simple goto.
         replaceSwitchByGoto();
       } else {
         // Replace switch by a new switch where the dead switch cases have been removed.
-        replaceSwitchByOptimizedSwitch(originalNumberOfSuccessors);
+        replaceSwitchByOptimizedSwitch(originalNumberOfSuccessors, removedSuccessorIndices);
       }
       return true;
     }
     return false;
   }
 
-  private void unlinkDeadSuccessors() {
+  private IntList unlinkDeadSuccessors() {
     IntPredicate successorHasBecomeDeadPredicate = computeSuccessorHasBecomeDeadPredicate();
     IntList successorIndicesToBeRemoved = new IntArrayList();
     for (int i = 0; i < block.getSuccessors().size(); i++) {
@@ -111,6 +111,7 @@
     }
     successorIndicesToBeRemoved.sort(Comparator.naturalOrder());
     block.removeSuccessorsByIndex(successorIndicesToBeRemoved);
+    return successorIndicesToBeRemoved;
   }
 
   private IntPredicate computeSuccessorHasBecomeDeadPredicate() {
@@ -136,16 +137,12 @@
     iterator.replaceCurrentInstruction(new Goto(target));
   }
 
-  private void replaceSwitchByOptimizedSwitch(int originalNumberOfSuccessors) {
+  private void replaceSwitchByOptimizedSwitch(
+      int originalNumberOfSuccessors, IntList removedSuccessorIndices) {
     int[] targetBlockIndexOffset = new int[originalNumberOfSuccessors];
-    for (int i : switchCasesToBeRemoved) {
-      int targetBlockIndex = theSwitch.getTargetBlockIndex(i);
-      // Add 1 because we are interested in the number of targets removed before a given index.
-      if (targetBlockIndex + 1 < targetBlockIndexOffset.length) {
-        targetBlockIndexOffset[targetBlockIndex + 1] = 1;
-      }
+    for (int removedSuccessorIndex : removedSuccessorIndices) {
+      targetBlockIndexOffset[removedSuccessorIndex] = 1;
     }
-
     for (int i = 1; i < targetBlockIndexOffset.length; i++) {
       targetBlockIndexOffset[i] += targetBlockIndexOffset[i - 1];
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
index 35eaaf5..78e756d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
@@ -21,8 +21,8 @@
    */
   final OptionalBool returnsReceiver;
 
-  final boolean hasMonitorOnReceiver;
-  final boolean modifiesInstanceFields;
+  public final boolean hasMonitorOnReceiver;
+  public final boolean modifiesInstanceFields;
 
   public ClassInlinerEligibilityInfo(
       List<Pair<Invoke.Type, DexMethod>> callsReceiver,
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 371b79a..32e25f1 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
@@ -53,6 +53,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.ir.optimize.InliningOracle;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInliner.EligibilityStatus;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
@@ -1096,33 +1097,22 @@
     }
 
     MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo();
-    ClassInlinerEligibilityInfo eligibility = optimizationInfo.getClassInlinerEligibility();
-    if (eligibility == null) {
-      return false;
-    }
-
-    if (root.isStaticGet()) {
-      // If we are class inlining a singleton instance from a static-get, then we don't know the
-      // value of the fields.
-      ParameterUsage receiverUsage = optimizationInfo.getParameterUsages(0);
-      if (receiverUsage == null || receiverUsage.hasFieldRead) {
+    ClassInlinerMethodConstraint classInlinerMethodConstraint =
+        optimizationInfo.getClassInlinerMethodConstraint();
+    if (root.isNewInstance()) {
+      if (!classInlinerMethodConstraint.isEligibleForNewInstanceClassInlining(singleTarget)) {
         return false;
       }
-      if (eligibility.hasMonitorOnReceiver) {
-        // We will not be able to remove the monitor instruction afterwards.
-        return false;
-      }
-      if (eligibility.modifiesInstanceFields) {
-        // The static instance could be accessed from elsewhere. Therefore, we cannot
-        // allow side-effects to be removed and therefore cannot class inline method
-        // calls that modifies the instance.
+    } else {
+      assert root.isStaticGet();
+      if (!classInlinerMethodConstraint.isEligibleForStaticGetClassInlining(singleTarget)) {
         return false;
       }
     }
 
     // If the method returns receiver and the return value is actually
     // used in the code we need to make some additional checks.
-    if (!eligibilityAcceptanceCheck.test(eligibility)) {
+    if (!eligibilityAcceptanceCheck.test(optimizationInfo.getClassInlinerEligibility())) {
       return false;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..c441134
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, 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.classinliner.constraint;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public class AlwaysFalseClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
+
+  private static final AlwaysFalseClassInlinerMethodConstraint INSTANCE =
+      new AlwaysFalseClassInlinerMethodConstraint();
+
+  private AlwaysFalseClassInlinerMethodConstraint() {}
+
+  public static AlwaysFalseClassInlinerMethodConstraint getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method) {
+    return false;
+  }
+
+  @Override
+  public boolean isEligibleForStaticGetClassInlining(ProgramMethod method) {
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..3488111
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, 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.classinliner.constraint;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public class AlwaysTrueClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
+
+  private static final AlwaysTrueClassInlinerMethodConstraint INSTANCE =
+      new AlwaysTrueClassInlinerMethodConstraint();
+
+  private AlwaysTrueClassInlinerMethodConstraint() {}
+
+  public static AlwaysTrueClassInlinerMethodConstraint getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method) {
+    return true;
+  }
+
+  @Override
+  public boolean isEligibleForStaticGetClassInlining(ProgramMethod method) {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..e8ab3ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2021, 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.classinliner.constraint;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface ClassInlinerMethodConstraint {
+
+  boolean isEligibleForNewInstanceClassInlining(ProgramMethod method);
+
+  boolean isEligibleForStaticGetClassInlining(ProgramMethod method);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraintAnalysis.java
new file mode 100644
index 0000000..fea67a4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraintAnalysis.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2021, 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.classinliner.constraint;
+
+import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo;
+import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
+
+public class ClassInlinerMethodConstraintAnalysis {
+
+  public static ClassInlinerMethodConstraint analyze(
+      ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
+      ParameterUsagesInfo parameterUsagesInfo) {
+    boolean isEligibleForNewInstanceClassInlining =
+        isEligibleForNewInstanceClassInlining(classInlinerEligibilityInfo);
+    boolean isEligibleForStaticGetClassInlining =
+        isEligibleForStaticGetClassInlining(classInlinerEligibilityInfo, parameterUsagesInfo);
+    if (isEligibleForNewInstanceClassInlining) {
+      if (isEligibleForStaticGetClassInlining) {
+        return alwaysTrue();
+      }
+      return onlyNewInstanceClassInlining();
+    }
+    assert !isEligibleForStaticGetClassInlining;
+    return alwaysFalse();
+  }
+
+  private static boolean isEligibleForNewInstanceClassInlining(
+      ClassInlinerEligibilityInfo classInlinerEligibilityInfo) {
+    return classInlinerEligibilityInfo != null;
+  }
+
+  private static boolean isEligibleForStaticGetClassInlining(
+      ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
+      ParameterUsagesInfo parameterUsagesInfo) {
+    if (classInlinerEligibilityInfo == null || parameterUsagesInfo == null) {
+      return false;
+    }
+    if (classInlinerEligibilityInfo.hasMonitorOnReceiver) {
+      // We will not be able to remove the monitor instruction afterwards.
+      return false;
+    }
+    if (classInlinerEligibilityInfo.modifiesInstanceFields) {
+      // The static instance could be accessed from elsewhere. Therefore, we cannot allow
+      // side-effects to be removed and therefore cannot class inline method calls that modifies the
+      // instance.
+      return false;
+    }
+    ParameterUsage receiverUsage = parameterUsagesInfo.getParameterUsage(0);
+    if (receiverUsage == null) {
+      return false;
+    }
+    if (receiverUsage.hasFieldRead) {
+      // We don't know the value of the field.
+      return false;
+    }
+    return true;
+  }
+
+  private static AlwaysFalseClassInlinerMethodConstraint alwaysFalse() {
+    return AlwaysFalseClassInlinerMethodConstraint.getInstance();
+  }
+
+  private static AlwaysTrueClassInlinerMethodConstraint alwaysTrue() {
+    return AlwaysTrueClassInlinerMethodConstraint.getInstance();
+  }
+
+  private static OnlyNewInstanceClassInlinerMethodConstraint onlyNewInstanceClassInlining() {
+    return OnlyNewInstanceClassInlinerMethodConstraint.getInstance();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/OnlyNewInstanceClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/OnlyNewInstanceClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..d3f35d4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/OnlyNewInstanceClassInlinerMethodConstraint.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, 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.classinliner.constraint;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public class OnlyNewInstanceClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
+
+  private static final OnlyNewInstanceClassInlinerMethodConstraint INSTANCE =
+      new OnlyNewInstanceClassInlinerMethodConstraint();
+
+  private OnlyNewInstanceClassInlinerMethodConstraint() {}
+
+  public static OnlyNewInstanceClassInlinerMethodConstraint getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method) {
+    return true;
+  }
+
+  @Override
+  public boolean isEligibleForStaticGetClassInlining(ProgramMethod method) {
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index a9854ee..45764df 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -13,6 +13,8 @@
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.AlwaysFalseClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
@@ -74,6 +76,11 @@
   }
 
   @Override
+  public ClassInlinerMethodConstraint getClassInlinerMethodConstraint() {
+    return AlwaysFalseClassInlinerMethodConstraint.getInstance();
+  }
+
+  @Override
   public TypeElement getDynamicUpperBoundType() {
     return UNKNOWN_TYPE;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 93a330e..7cced9d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -35,6 +36,8 @@
 
   public abstract boolean classInitializerMayBePostponed();
 
+  public abstract ClassInlinerMethodConstraint getClassInlinerMethodConstraint();
+
   public abstract TypeElement getDynamicUpperBoundType();
 
   public final TypeElement getDynamicUpperBoundTypeOrElse(TypeElement orElse) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 7432e43..804b6b3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -90,6 +90,8 @@
 import com.android.tools.r8.ir.optimize.DynamicTypeOptimization;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerReceiverAnalysis;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraintAnalysis;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsageBuilder;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeAnalyzer;
@@ -143,12 +145,16 @@
       Timing timing) {
     DexEncodedMethod definition = method.getDefinition();
     identifyBridgeInfo(definition, code, feedback, timing);
-    identifyClassInlinerEligibility(code, feedback, timing);
-    identifyParameterUsages(definition, code, feedback, timing);
+    ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
+        identifyClassInlinerEligibility(code, feedback, timing);
+    ParameterUsagesInfo parameterUsagesInfo =
+        identifyParameterUsages(definition, code, feedback, timing);
     analyzeReturns(code, feedback, timing);
     if (options.enableInlining) {
       identifyInvokeSemanticsForInlining(definition, code, feedback, timing);
     }
+    computeClassInlinerMethodConstraint(
+        method, code, feedback, classInlinerEligibilityInfo, parameterUsagesInfo, timing);
     computeSimpleInliningConstraint(method, code, feedback, timing);
     computeDynamicReturnType(dynamicTypeOptimization, feedback, definition, code, timing);
     computeInitializedClassesOnNormalExit(feedback, definition, code, timing);
@@ -167,14 +173,17 @@
     timing.end();
   }
 
-  private void identifyClassInlinerEligibility(
+  private ClassInlinerEligibilityInfo identifyClassInlinerEligibility(
       IRCode code, OptimizationFeedback feedback, Timing timing) {
     timing.begin("Identify class inliner eligibility");
-    identifyClassInlinerEligibility(code, feedback);
+    ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
+        identifyClassInlinerEligibility(code, feedback);
     timing.end();
+    return classInlinerEligibilityInfo;
   }
 
-  private void identifyClassInlinerEligibility(IRCode code, OptimizationFeedback feedback) {
+  private ClassInlinerEligibilityInfo identifyClassInlinerEligibility(
+      IRCode code, OptimizationFeedback feedback) {
     // Method eligibility is calculated in similar way for regular method
     // and for the constructor. To be eligible method should only be using its
     // receiver in the following ways:
@@ -193,14 +202,14 @@
     boolean instanceInitializer = definition.isInstanceInitializer();
     if (definition.isNative()
         || (!definition.isNonAbstractVirtualMethod() && !instanceInitializer)) {
-      return;
+      return null;
     }
 
     feedback.setClassInlinerEligibility(definition, null); // To allow returns below.
 
     Value receiver = code.getThis();
     if (receiver.numberOfPhiUsers() > 0) {
-      return;
+      return null;
     }
 
     List<Pair<Invoke.Type, DexMethod>> callsReceiver = new ArrayList<>();
@@ -229,17 +238,17 @@
               InstancePut instancePutInstruction = insn.asInstancePut();
               // Only allow field writes to the receiver.
               if (!isReceiverAlias.test(instancePutInstruction.object())) {
-                return;
+                return null;
               }
               // Do not allow the receiver to escape via a field write.
               if (isReceiverAlias.test(instancePutInstruction.value())) {
-                return;
+                return null;
               }
               modifiesInstanceFields = true;
             }
             DexField field = insn.asFieldInstruction().getField();
             if (appView.appInfo().resolveField(field).isFailedOrUnknownResolution()) {
-              return;
+              return null;
             }
             break;
           }
@@ -257,7 +266,7 @@
               break;
             }
             // We don't support other direct calls yet.
-            return;
+            return null;
           }
 
         case INVOKE_STATIC:
@@ -265,27 +274,27 @@
             InvokeStatic invoke = insn.asInvokeStatic();
             DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
             if (singleTarget == null) {
-              return; // Not allowed.
+              return null; // Not allowed.
             }
             if (singleTarget.getReference() == dexItemFactory.objectsMethods.requireNonNull) {
               if (!invoke.hasOutValue() || !invoke.outValue().hasAnyUsers()) {
                 continue;
               }
             }
-            return;
+            return null;
           }
 
         case INVOKE_VIRTUAL:
           {
             InvokeVirtual invoke = insn.asInvokeVirtual();
             if (ListUtils.lastIndexMatching(invoke.arguments(), isReceiverAlias) != 0) {
-              return; // Not allowed.
+              return null; // Not allowed.
             }
             DexMethod invokedMethod = invoke.getInvokedMethod();
             DexType returnType = invokedMethod.proto.returnType;
             if (returnType.isClassType()
                 && appView.appInfo().inSameHierarchy(returnType, context.getHolderType())) {
-              return; // Not allowed, could introduce an alias of the receiver.
+              return null; // Not allowed, could introduce an alias of the receiver.
             }
             callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod));
           }
@@ -293,34 +302,35 @@
 
         default:
           // Other receiver usages make the method not eligible.
-          return;
+          return null;
       }
     }
 
     if (instanceInitializer && !seenSuperInitCall) {
       // Call to super constructor not found?
-      return;
+      return null;
     }
 
     boolean synchronizedVirtualMethod = definition.isSynchronized() && definition.isVirtualMethod();
-
-    feedback.setClassInlinerEligibility(
-        definition,
+    ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
         new ClassInlinerEligibilityInfo(
             callsReceiver,
             new ClassInlinerReceiverAnalysis(appView, definition, code).computeReturnsReceiver(),
             seenMonitor || synchronizedVirtualMethod,
-            modifiesInstanceFields));
+            modifiesInstanceFields);
+    feedback.setClassInlinerEligibility(definition, classInlinerEligibilityInfo);
+    return classInlinerEligibilityInfo;
   }
 
-  private void identifyParameterUsages(
+  private ParameterUsagesInfo identifyParameterUsages(
       DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
     timing.begin("Identify parameter usages");
-    identifyParameterUsages(method, code, feedback);
+    ParameterUsagesInfo parameterUsagesInfo = identifyParameterUsages(method, code, feedback);
     timing.end();
+    return parameterUsagesInfo;
   }
 
-  private void identifyParameterUsages(
+  private ParameterUsagesInfo identifyParameterUsages(
       DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
     List<ParameterUsage> usages = new ArrayList<>();
     List<Value> values = code.collectArguments();
@@ -331,11 +341,10 @@
         usages.add(usage);
       }
     }
-    feedback.setParameterUsages(
-        method,
-        usages.isEmpty()
-            ? DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO
-            : new ParameterUsagesInfo(usages));
+    ParameterUsagesInfo parameterUsagesInfo =
+        !usages.isEmpty() ? new ParameterUsagesInfo(usages) : null;
+    feedback.setParameterUsages(method, parameterUsagesInfo);
+    return parameterUsagesInfo;
   }
 
   private ParameterUsage collectParameterUsages(int i, Value root) {
@@ -969,6 +978,31 @@
     return true;
   }
 
+  private void computeClassInlinerMethodConstraint(
+      ProgramMethod method,
+      IRCode code,
+      OptimizationFeedback feedback,
+      ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
+      ParameterUsagesInfo parameterUsagesInfo,
+      Timing timing) {
+    timing.begin("Compute class inlining constraint");
+    computeClassInlinerMethodConstraint(
+        method, code, feedback, classInlinerEligibilityInfo, parameterUsagesInfo);
+    timing.end();
+  }
+
+  private void computeClassInlinerMethodConstraint(
+      ProgramMethod method,
+      IRCode code,
+      OptimizationFeedback feedback,
+      ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
+      ParameterUsagesInfo parameterUsagesInfo) {
+    ClassInlinerMethodConstraint classInlinerMethodConstraint =
+        ClassInlinerMethodConstraintAnalysis.analyze(
+            classInlinerEligibilityInfo, parameterUsagesInfo);
+    feedback.setClassInlinerMethodConstraint(method, classInlinerMethodConstraint);
+  }
+
   private void computeSimpleInliningConstraint(
       ProgramMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
     if (appView.options().enableSimpleInliningConstraints) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 1236ed6..39f725d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -250,11 +251,18 @@
   }
 
   @Override
-  public void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo) {
+  public synchronized void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo) {
     getMethodOptimizationInfoForUpdating(method).setBridgeInfo(bridgeInfo);
   }
 
   @Override
+  public synchronized void setClassInlinerMethodConstraint(
+      ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint) {
+    getMethodOptimizationInfoForUpdating(method)
+        .setClassInlinerMethodConstraint(classInlinerConstraint);
+  }
+
+  @Override
   public synchronized void setClassInlinerEligibility(
       DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {
     getMethodOptimizationInfoForUpdating(method).setClassInlinerEligibility(eligibility);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 299f7ce..3b46f4e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -113,6 +114,10 @@
   public void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo) {}
 
   @Override
+  public void setClassInlinerMethodConstraint(
+      ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint) {}
+
+  @Override
   public void setClassInlinerEligibility(
       DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {}
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 945d84a..2624486 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -163,6 +164,12 @@
   }
 
   @Override
+  public void setClassInlinerMethodConstraint(
+      ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint) {
+    // Ignored.
+  }
+
+  @Override
   public void setClassInlinerEligibility(
       DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {
     // Ignored.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
index 921ae28..4389193 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
@@ -39,7 +39,7 @@
         parametersUsages.stream().map(usage -> usage.index).collect(Collectors.toSet()).size();
   }
 
-  ParameterUsage getParameterUsage(int index) {
+  public ParameterUsage getParameterUsage(int index) {
     for (ParameterUsage usage : parametersUsages) {
       if (usage.index == index) {
         return usage;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index a179561..43667c8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -16,6 +16,8 @@
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.AlwaysFalseClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -33,6 +35,8 @@
   private int returnedArgument = DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT;
   private AbstractValue abstractReturnValue =
       DefaultMethodOptimizationInfo.UNKNOWN_ABSTRACT_RETURN_VALUE;
+  private ClassInlinerMethodConstraint classInlinerConstraint =
+      AlwaysFalseClassInlinerMethodConstraint.getInstance();
   private TypeElement returnsObjectWithUpperBoundType = DefaultMethodOptimizationInfo.UNKNOWN_TYPE;
   private ClassTypeElement returnsObjectWithLowerBoundType =
       DefaultMethodOptimizationInfo.UNKNOWN_CLASS_TYPE;
@@ -239,6 +243,15 @@
   }
 
   @Override
+  public ClassInlinerMethodConstraint getClassInlinerMethodConstraint() {
+    return classInlinerConstraint;
+  }
+
+  void setClassInlinerMethodConstraint(ClassInlinerMethodConstraint classInlinerConstraint) {
+    this.classInlinerConstraint = classInlinerConstraint;
+  }
+
+  @Override
   public TypeElement getDynamicUpperBoundType() {
     return returnsObjectWithUpperBoundType;
   }
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 3a25d00..1587e8a 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.jar;
 
+import static com.android.tools.r8.repackaging.Repackaging.DefaultRepackagingConfiguration.TEMPORARY_PACKAGE_NAME;
 import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
 
 import com.android.tools.r8.ByteDataView;
@@ -185,6 +186,7 @@
     }
     String desc = namingLens.lookupDescriptor(clazz.type).toString();
     String name = namingLens.lookupInternalName(clazz.type);
+    assert !name.contains(TEMPORARY_PACKAGE_NAME);
     String signature = clazz.getClassSignature().toRenamedString(namingLens, isTypeMissing);
     String superName =
         clazz.type == options.itemFactory.objectType
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 00e689c..d9baf1f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -249,7 +249,10 @@
     }
     // Rewrite super types.
     for (KotlinTypeInfo superType : superTypes) {
-      superType.rewrite(kmClass::visitSupertype, appView, namingLens);
+      // Ensure the rewritten super type is not this type.
+      if (clazz.getType() != superType.rewriteType(appView.graphLens())) {
+        superType.rewrite(kmClass::visitSupertype, appView, namingLens);
+      }
     }
     // Rewrite nested classes.
     for (KotlinTypeReference nestedClass : nestedClasses) {
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 c80ed32..54b8054 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
@@ -7,6 +7,8 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
@@ -48,6 +50,10 @@
 
   abstract void rewrite(KmTypeVisitor visitor, AppView<?> appView, NamingLens namingLens);
 
+  public DexType rewriteType(GraphLens graphLens) {
+    return null;
+  }
+
   public static class KotlinClassClassifierInfo extends KotlinClassifierInfo {
 
     private final KotlinTypeReference type;
@@ -75,6 +81,11 @@
     public void trace(DexDefinitionSupplier definitionSupplier) {
       type.trace(definitionSupplier);
     }
+
+    @Override
+    public DexType rewriteType(GraphLens graphLens) {
+      return type.rewriteType(graphLens);
+    }
   }
 
   public static class KotlinTypeParameterClassifierInfo extends KotlinClassifierInfo {
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 fdb9472..1e3273c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.DexEncodedAnnotation;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueArray;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
@@ -79,14 +80,14 @@
     this.kotlin = factory.kotlin;
   }
 
-  private static boolean isNotKotlinMetadata(AppView<?> appView, DexAnnotation annotation) {
-    return annotation.annotation.type != appView.dexItemFactory().kotlinMetadataType;
+  private static boolean isNotKotlinMetadata(DexAnnotation annotation, DexType kotlinMetadataType) {
+    return annotation.annotation.type != kotlinMetadataType;
   }
 
   public void runForR8(ExecutorService executorService) throws ExecutionException {
-    final DexClass kotlinMetadata =
-        appView.definitionFor(appView.dexItemFactory().kotlinMetadataType);
-    final WriteMetadataFieldInfo writeMetadataFieldInfo =
+    DexType rewrittenMetadataType = appView.graphLens().lookupClassType(factory.kotlinMetadataType);
+    DexClass kotlinMetadata = appView.definitionFor(rewrittenMetadataType);
+    WriteMetadataFieldInfo writeMetadataFieldInfo =
         new WriteMetadataFieldInfo(
             kotlinMetadataFieldExists(kotlinMetadata, appView, kotlin.metadata.kind),
             kotlinMetadataFieldExists(kotlinMetadata, appView, kotlin.metadata.metadataVersion),
@@ -100,11 +101,13 @@
         appView.appInfo().classes(),
         clazz -> {
           KotlinClassLevelInfo kotlinInfo = clazz.getKotlinInfo();
-          DexAnnotation oldMeta = clazz.annotations().getFirstMatching(factory.kotlinMetadataType);
+          DexAnnotation oldMeta = clazz.annotations().getFirstMatching(rewrittenMetadataType);
           if (kotlinInfo == INVALID_KOTLIN_INFO) {
             // Maintain invalid kotlin info for classes.
             return;
           }
+          // TODO(b/181103083): Consider removing if rewrittenMetadataType
+          //  != factory.kotlinMetadataType
           if (oldMeta == null
               || kotlinInfo == NO_KOTLIN_INFO
               || (appView.appInfo().hasLiveness()
@@ -113,7 +116,9 @@
             // missing.
             if (oldMeta != null) {
               clazz.setAnnotations(
-                  clazz.annotations().keepIf(anno -> isNotKotlinMetadata(appView, anno)));
+                  clazz
+                      .annotations()
+                      .keepIf(anno -> isNotKotlinMetadata(anno, rewrittenMetadataType)));
             }
             return;
           }
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 4e74391..7f068f6 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -9,6 +9,8 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
@@ -120,4 +122,8 @@
     flexibleTypeUpperBound.trace(definitionSupplier);
     forEachApply(annotations, annotation -> annotation::trace, definitionSupplier);
   }
+
+  public DexType rewriteType(GraphLens graphLens) {
+    return classifier.rewriteType(graphLens);
+  }
 }
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 088e7ec..2e1ddf9 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
@@ -7,8 +7,8 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -35,6 +35,10 @@
     assert unknown != null;
   }
 
+  public DexType getKnown() {
+    return known;
+  }
+
   static KotlinTypeReference fromBinaryName(String binaryName, DexItemFactory factory) {
     if (DescriptorUtils.isValidBinaryName(binaryName)) {
       return fromDescriptor(
@@ -62,18 +66,11 @@
       return unknown;
     }
     assert known != null;
-    if (!known.isClassType()) {
-      return known.descriptor.toString();
-    }
-    if (appView.appInfo().hasLiveness()
-        && !appView.withLiveness().appInfo().isNonProgramTypeOrLiveProgramType(known)) {
+    DexType rewrittenType = toRewrittenTypeOrNull(appView, known);
+    if (rewrittenType == null) {
       return defaultValue;
     }
-    DexString descriptor = namingLens.lookupDescriptor(known);
-    if (descriptor != null) {
-      return descriptor.toString();
-    }
-    return defaultValue;
+    return namingLens.lookupDescriptor(rewrittenType).toString();
   }
 
   String toRenamedBinaryNameOrDefault(
@@ -93,6 +90,25 @@
     return DescriptorUtils.getBinaryNameFromDescriptor(descriptor);
   }
 
+  private static DexType toRewrittenTypeOrNull(AppView<?> appView, DexType type) {
+    if (type.isArrayType()) {
+      DexType rewrittenBaseType =
+          toRewrittenTypeOrNull(appView, type.toBaseType(appView.dexItemFactory()));
+      return rewrittenBaseType != null
+          ? type.replaceBaseType(rewrittenBaseType, appView.dexItemFactory())
+          : null;
+    }
+    if (!type.isClassType()) {
+      return type;
+    }
+    DexType rewrittenType = appView.graphLens().lookupClassType(type);
+    if (appView.appInfo().hasLiveness()
+        && !appView.withLiveness().appInfo().isNonProgramTypeOrLiveProgramType(rewrittenType)) {
+      return null;
+    }
+    return rewrittenType;
+  }
+
   @Override
   public String toString() {
     return known != null ? known.descriptor.toString() : unknown;
@@ -102,7 +118,14 @@
   public void trace(DexDefinitionSupplier definitionSupplier) {
     if (known != null && known.isClassType()) {
       // Lookup the definition, ignoring the result. This populates the sets in the Enqueuer.
-      definitionSupplier.definitionFor(known);
+      definitionSupplier.contextIndependentDefinitionFor(known);
     }
   }
+
+  public DexType rewriteType(GraphLens graphLens) {
+    if (known != null && known.isClassType()) {
+      return graphLens.lookupClassType(known);
+    }
+    return null;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index 771c336..1b91b62 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -39,8 +39,6 @@
   private final ClassNamingStrategy classNamingStrategy;
   private final PackageNamingStrategy packageNamingStrategy;
   private final Iterable<? extends DexClass> classes;
-  private final boolean isAccessModificationAllowed;
-  private final Set<String> noObfuscationPrefixes = Sets.newHashSet();
   private final Set<String> usedPackagePrefixes = Sets.newHashSet();
   private final Set<String> usedTypeNames = Sets.newHashSet();
   private final Map<DexType, DexString> renaming = Maps.newIdentityHashMap();
@@ -50,6 +48,7 @@
   private final Namespace topLevelState;
   private final boolean allowMixedCaseNaming;
   private final Predicate<String> isUsed;
+  private final ProguardPackageNameList keepPackageNames;
 
   ClassNameMinifier(
       AppView<AppInfoWithLiveness> appView,
@@ -61,8 +60,6 @@
     this.packageNamingStrategy = packageNamingStrategy;
     this.classes = classes;
     InternalOptions options = appView.options();
-    this.isAccessModificationAllowed =
-        options.getProguardConfiguration().isAccessModificationAllowed();
     this.keepInnerClassStructure = options.keepInnerClassStructure();
 
     // Initialize top-level naming state.
@@ -70,9 +67,8 @@
     String newPackageDescriptor =
         StringUtils.replaceAll(options.getProguardConfiguration().getPackagePrefix(), ".", "/");
     if (!newPackageDescriptor.isEmpty()) {
-      registerPackagePrefixesAsUsed(newPackageDescriptor, false);
+      registerPackagePrefixesAsUsed(newPackageDescriptor);
     }
-
     states.put("", topLevelState);
 
     if (options.getProguardConfiguration().hasDontUseMixedCaseClassnames()) {
@@ -82,6 +78,7 @@
       allowMixedCaseNaming = true;
       isUsed = usedTypeNames::contains;
     }
+    keepPackageNames = options.getProguardConfiguration().getKeepPackageNamesPatterns();
   }
 
   private void setUsedTypeName(String typeName) {
@@ -180,8 +177,7 @@
   private void registerClassAsUsed(DexType type, DexString descriptor) {
     renaming.put(type, descriptor);
     registerPackagePrefixesAsUsed(
-        getParentPackagePrefix(getClassBinaryNameFromDescriptor(descriptor.toSourceString())),
-        isAccessModificationAllowed);
+        getParentPackagePrefix(getClassBinaryNameFromDescriptor(descriptor.toSourceString())));
     setUsedTypeName(descriptor.toString());
     if (keepInnerClassStructure) {
       DexType outerClass = getOutClassForType(type);
@@ -197,15 +193,11 @@
   }
 
   /** Registers the given package prefix and all of parent packages as used. */
-  private void registerPackagePrefixesAsUsed(String packagePrefix, boolean isMinificationAllowed) {
-    // If -allowaccessmodification is not set, we may keep classes in their original packages,
-    // accounting for package-private accesses.
-    if (!isMinificationAllowed) {
-      noObfuscationPrefixes.add(packagePrefix);
-    }
+  private void registerPackagePrefixesAsUsed(String packagePrefix) {
     String usedPrefix = packagePrefix;
     while (usedPrefix.length() > 0) {
       usedPackagePrefixes.add(usedPrefix);
+      states.computeIfAbsent(usedPrefix, Namespace::new);
       usedPrefix = getParentPackagePrefix(usedPrefix);
     }
   }
@@ -259,9 +251,7 @@
     String packageName = getPackageBinaryNameFromJavaType(type.getPackageDescriptor());
     // Check whether the given class should be kept.
     // or check whether the given class belongs to a package that is kept for another class.
-    ProguardPackageNameList keepPackageNames =
-        appView.options().getProguardConfiguration().getKeepPackageNamesPatterns();
-    if (noObfuscationPrefixes.contains(packageName) || keepPackageNames.matches(type)) {
+    if (keepPackageNames.matches(type)) {
       return states.computeIfAbsent(packageName, Namespace::new);
     }
     return getStateForPackagePrefix(packageName);
@@ -272,15 +262,9 @@
     if (state == null) {
       // Calculate the parent package prefix, e.g., La/b/c -> La/b
       String parentPackage = getParentPackagePrefix(prefix);
-      Namespace superState;
-      if (noObfuscationPrefixes.contains(parentPackage)) {
-        // Restore a state for parent package prefix if it should be kept.
-        superState = states.computeIfAbsent(parentPackage, Namespace::new);
-      } else {
-        // Create a state for parent package prefix, if necessary, in a recursive manner.
-        // That recursion should end when the parent package hits the top-level, "".
-        superState = getStateForPackagePrefix(parentPackage);
-      }
+      // Create a state for parent package prefix, if necessary, in a recursive manner.
+      // That recursion should end when the parent package hits the top-level, "".
+      Namespace superState = getStateForPackagePrefix(parentPackage);
       // From the super state, get a renamed package prefix for the current level.
       String renamedPackagePrefix = superState.nextPackagePrefix();
       // Create a new state, which corresponds to a new name space, for the current level.
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index eba0a87..cdfa649 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -9,6 +9,7 @@
 import static com.android.tools.r8.utils.DescriptorUtils.INNER_CLASS_SEPARATOR;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -26,6 +27,7 @@
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.synthesis.CommittedItems;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
@@ -291,26 +293,46 @@
 
   public static class DefaultRepackagingConfiguration implements RepackagingConfiguration {
 
+    private final AppView<?> appView;
     private final DexItemFactory dexItemFactory;
     private final ProguardConfiguration proguardConfiguration;
+    public static final String TEMPORARY_PACKAGE_NAME = "TEMPORARY_PACKAGE_NAME_FOR_";
 
-    public DefaultRepackagingConfiguration(
-        DexItemFactory dexItemFactory, ProguardConfiguration proguardConfiguration) {
-      this.dexItemFactory = dexItemFactory;
-      this.proguardConfiguration = proguardConfiguration;
+    public DefaultRepackagingConfiguration(AppView<?> appView) {
+      this.appView = appView;
+      this.dexItemFactory = appView.dexItemFactory();
+      this.proguardConfiguration = appView.options().getProguardConfiguration();
     }
 
     @Override
     public String getNewPackageDescriptor(ProgramPackage pkg, Set<String> seenPackageDescriptors) {
       String newPackageDescriptor =
           DescriptorUtils.getBinaryNameFromJavaType(proguardConfiguration.getPackagePrefix());
-      if (proguardConfiguration.getPackageObfuscationMode().isRepackageClasses()) {
+      PackageObfuscationMode packageObfuscationMode =
+          proguardConfiguration.getPackageObfuscationMode();
+      if (packageObfuscationMode.isRepackageClasses()) {
         return newPackageDescriptor;
       }
+      assert packageObfuscationMode.isFlattenPackageHierarchy()
+          || packageObfuscationMode.isMinification();
       if (!newPackageDescriptor.isEmpty()) {
         newPackageDescriptor += DESCRIPTOR_PACKAGE_SEPARATOR;
       }
-      newPackageDescriptor += pkg.getLastPackageName();
+      if (packageObfuscationMode.isMinification()) {
+        assert !proguardConfiguration.hasApplyMappingFile();
+        // Always keep top-level classes since there packages can never be minified.
+        if (pkg.getPackageDescriptor().equals("")
+            || proguardConfiguration.getKeepPackageNamesPatterns().matches(pkg)
+            || mayHavePinnedPackagePrivateOrProtectedItem(pkg)) {
+          return pkg.getPackageDescriptor();
+        }
+        // For us to rename shaking/A to a/a if we have a class shaking/Kept, we have to propose
+        // a different name than the last package name - the class will be minified in
+        // ClassNameMinifier.
+        newPackageDescriptor += TEMPORARY_PACKAGE_NAME + pkg.getLastPackageName();
+      } else {
+        newPackageDescriptor += pkg.getLastPackageName();
+      }
       String finalPackageDescriptor = newPackageDescriptor;
       for (int i = 1; seenPackageDescriptors.contains(finalPackageDescriptor); i++) {
         finalPackageDescriptor = newPackageDescriptor + INNER_CLASS_SEPARATOR + i;
@@ -318,6 +340,24 @@
       return finalPackageDescriptor;
     }
 
+    private boolean mayHavePinnedPackagePrivateOrProtectedItem(ProgramPackage pkg) {
+      // Go through all package classes and members to see if there is a pinned package-private
+      // item, in which case we cannot move it because there may be a reflective access to it.
+      for (DexProgramClass clazz : pkg.classesInPackage()) {
+        if (clazz.getAccessFlags().isPackagePrivateOrProtected()
+            && appView.getKeepInfo().getClassInfo(clazz).isPinned()) {
+          return true;
+        }
+        for (DexEncodedMember<?, ?> member : clazz.members()) {
+          if (member.getAccessFlags().isPackagePrivateOrProtected()
+              && appView.getKeepInfo().getMemberInfo(member, clazz).isPinned()) {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+
     @Override
     public DexType getRepackagedType(
         DexProgramClass clazz,
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 0b02310..813ae66 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -160,14 +160,6 @@
     this.isVerbose = isVerbose;
   }
 
-  public static <T, ST extends StackTraceElementProxy<T, ST>> Retrace<T, ST> createRetrace(
-      StackTraceLineParser<T, ST> stackTraceLineParser,
-      StackTraceElementProxyRetracer<T, ST> proxyRetracer,
-      DiagnosticsHandler diagnosticsHandler,
-      boolean isVerbose) {
-    return new Retrace<>(stackTraceLineParser, proxyRetracer, diagnosticsHandler, isVerbose);
-  }
-
   /**
    * Retraces a stack frame and calls the consumer for each retraced line
    *
diff --git a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
index 957e357..825ac52 100644
--- a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -47,7 +47,7 @@
   }
 
   /**
-   * Retraces a list of stack-traces strings and returns a list. Ambiguous and inline frames will be
+   * Retraces a list of stack-trace lines and returns a list. Ambiguous and inline frames will be
    * appended automatically to the retraced string.
    *
    * @param stackTrace the incoming stack trace
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 762617e..057a80f 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -115,10 +115,6 @@
         return isAnnotationTypeLive;
 
       case DexAnnotation.VISIBILITY_BUILD:
-        if (DexAnnotation.isSynthesizedClassMapAnnotation(annotation, dexItemFactory)) {
-          // TODO(sgjesse) When should these be removed?
-          return true;
-        }
         if (!config.runtimeInvisibleAnnotations) {
           return false;
         }
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index da98174..81239aa 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
@@ -72,6 +73,14 @@
    */
   public abstract KeepFieldInfo getFieldInfo(DexEncodedField field, DexProgramClass holder);
 
+  public KeepMemberInfo<?, ?> getMemberInfo(DexEncodedMember<?, ?> member, DexProgramClass holder) {
+    if (member.isDexEncodedField()) {
+      return getFieldInfo(member.asDexEncodedField(), holder);
+    }
+    assert member.isDexEncodedMethod();
+    return getMethodInfo(member.asDexEncodedMethod(), holder);
+  }
+
   public final KeepClassInfo getClassInfo(DexType type, DexDefinitionSupplier definitions) {
     DexProgramClass clazz = asProgramClassOrNull(definitions.definitionFor(type));
     return clazz == null ? keepInfoForNonProgramClass() : getClassInfo(clazz);
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java b/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java
index 58315d4..a60967f 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java
@@ -91,6 +91,11 @@
     return this == NONE;
   }
 
+  public boolean isMainDexTypeThatShouldIncludeDependencies(DexType type) {
+    // Dependencies of 'type' are only needed if 'type' is a direct/executed main-dex type.
+    return classList.contains(type) || tracedRoots.contains(type);
+  }
+
   public boolean isMainDex(ProgramDefinition definition) {
     return isFromList(definition) || isTracedRoot(definition) || isDependency(definition);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
index 3632f93..2cce6ff 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -264,9 +264,9 @@
               dexItemFactory.annotationMethodParameters,
               dexItemFactory.annotationSourceDebugExtension,
               dexItemFactory.annotationSynthesizedClass,
-              dexItemFactory.annotationSynthesizedClassMap,
               dexItemFactory.annotationThrows,
-              dexItemFactory.serializedLambdaType)
+              dexItemFactory.serializedLambdaType,
+              dexItemFactory.unsafeType)
           .addAll(dexItemFactory.getJavaConversionTypes())
           .addAll(dexItemFactory.getJ$ConversionTypes())
           .build();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 8fdd330..6d3a8ca 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -397,6 +397,12 @@
         synthesizeKeepRulesForRecompilation();
       }
 
+      if (packageObfuscationMode == PackageObfuscationMode.NONE
+          && obfuscating
+          && !hasApplyMappingFile()) {
+        packageObfuscationMode = PackageObfuscationMode.MINIFICATION;
+      }
+
       return buildRaw();
     }
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java
index 48c4703..f31087d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.shaking;
 
-import com.android.tools.r8.graph.DexType;
-
 public class ProguardPackageMatcher {
   private final String pattern;
 
@@ -13,8 +11,8 @@
     this.pattern = pattern;
   }
 
-  public boolean matches(DexType type) {
-    return matchPackageNameImpl(pattern, 0, type.getPackageName(), 0);
+  public boolean matches(String packageName) {
+    return matchPackageNameImpl(pattern, 0, packageName, 0);
   }
 
   private static boolean matchPackageNameImpl(
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java
index 6d45459..6dcd07b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramPackage;
 import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
 import it.unimi.dsi.fastutil.objects.ObjectIterator;
@@ -56,9 +57,17 @@
   }
 
   public boolean matches(DexType type) {
+    return matches(type.getPackageName());
+  }
+
+  public boolean matches(ProgramPackage pkg) {
+    return matches(pkg.getPackageName());
+  }
+
+  private boolean matches(String pkgName) {
     for (Object2BooleanMap.Entry<ProguardPackageMatcher> packageName :
         packageNames.object2BooleanEntrySet()) {
-      if (packageName.getKey().matches(type)) {
+      if (packageName.getKey().matches(pkgName)) {
         // If we match a negation, abort as non-match. If we match a positive, return true.
         return !packageName.getBooleanValue();
       }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 82f3204..bff9202 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.MainDexInfo;
 import java.util.Comparator;
-import java.util.Set;
 
 /**
  * A synthesizing context is a description of the context that gives rise to a synthetic item.
@@ -123,14 +122,15 @@
     appView.rewritePrefix.rewriteType(hygienicType, rewrittenType);
   }
 
+  // TODO(b/180074885): Remove this once main-dex is traced at the end of of compilation.
   void addIfDerivedFromMainDexClass(
-      DexProgramClass externalSyntheticClass,
-      MainDexInfo mainDexInfo,
-      Set<DexType> allMainDexTypes) {
+      DexProgramClass externalSyntheticClass, MainDexInfo mainDexInfo) {
+    if (mainDexInfo.isMainDex(externalSyntheticClass)) {
+      return;
+    }
     // The input context type (not the annotated context) determines if the derived class is to be
-    // in main dex.
-    // TODO(b/168584485): Once resolved allMainDexTypes == mainDexClasses.
-    if (allMainDexTypes.contains(inputContextType)) {
+    // in main dex, as it is the input context type that is traced as part of main-dex tracing.
+    if (mainDexInfo.isMainDexTypeThatShouldIncludeDependencies(inputContextType)) {
       mainDexInfo.addSyntheticClass(externalSyntheticClass);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 65de13f..2e70db6 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -32,11 +31,9 @@
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
 import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
 import com.android.tools.r8.utils.structural.RepresentativeMap;
-import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Sets;
 import com.google.common.hash.HashCode;
 import java.util.ArrayList;
@@ -48,7 +45,6 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.TreeSet;
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 
@@ -262,7 +258,6 @@
   Result computeFinalSynthetics(AppView<?> appView) {
     assert verifyNoNestedSynthetics();
     DexApplication application;
-    MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
     Builder lensBuilder = new Builder();
     ImmutableMap.Builder<DexType, SyntheticMethodReference> finalMethodsBuilder =
         ImmutableMap.builder();
@@ -290,11 +285,6 @@
     ImmutableMap<DexType, SyntheticProgramClassReference> finalClasses =
         finalClassesBuilder.build();
 
-    handleSynthesizedClassMapping(
-        finalSyntheticProgramDefinitions, application, options, mainDexInfo, lensBuilder.typeMap);
-
-    assert appView.appInfo().getMainDexInfo() == mainDexInfo;
-
     Set<DexType> prunedSynthetics = Sets.newIdentityHashSet();
     committed.forEachNonLegacyItem(
         reference -> {
@@ -354,96 +344,6 @@
     return true;
   }
 
-  private void handleSynthesizedClassMapping(
-      List<DexProgramClass> finalSyntheticClasses,
-      DexApplication application,
-      InternalOptions options,
-      MainDexInfo mainDexInfo,
-      Map<DexType, DexType> derivedMainDexTypesToIgnore) {
-    boolean includeSynthesizedClassMappingInOutput = shouldAnnotateSynthetics(options);
-    if (includeSynthesizedClassMappingInOutput) {
-      updateSynthesizedClassMapping(application, finalSyntheticClasses);
-    }
-    updateMainDexListWithSynthesizedClassMap(application, mainDexInfo, derivedMainDexTypesToIgnore);
-    if (!includeSynthesizedClassMappingInOutput) {
-      clearSynthesizedClassMapping(application);
-    }
-  }
-
-  private void updateSynthesizedClassMapping(
-      DexApplication application, List<DexProgramClass> finalSyntheticClasses) {
-    ListMultimap<DexProgramClass, DexProgramClass> originalToSynthesized =
-        ArrayListMultimap.create();
-    for (DexType type : committed.getLegacyTypes()) {
-      DexProgramClass clazz = DexProgramClass.asProgramClassOrNull(application.definitionFor(type));
-      if (clazz != null) {
-        for (DexProgramClass origin : clazz.getSynthesizedFrom()) {
-          originalToSynthesized.put(origin, clazz);
-        }
-      }
-    }
-    for (DexProgramClass clazz : finalSyntheticClasses) {
-      for (DexProgramClass origin : clazz.getSynthesizedFrom()) {
-        originalToSynthesized.put(origin, clazz);
-      }
-    }
-    for (Map.Entry<DexProgramClass, Collection<DexProgramClass>> entry :
-        originalToSynthesized.asMap().entrySet()) {
-      DexProgramClass original = entry.getKey();
-      // Use a tree set to make sure that we have an ordering on the types.
-      // These types are put in an array in annotations in the output and we
-      // need a consistent ordering on them.
-      TreeSet<DexType> synthesized = new TreeSet<>(DexType::compareTo);
-      entry.getValue().stream()
-          .map(dexProgramClass -> dexProgramClass.type)
-          .forEach(synthesized::add);
-      synthesized.addAll(
-          DexAnnotation.readAnnotationSynthesizedClassMap(original, application.dexItemFactory));
-
-      DexAnnotation updatedAnnotation =
-          DexAnnotation.createAnnotationSynthesizedClassMap(
-              synthesized, application.dexItemFactory);
-
-      original.setAnnotations(original.annotations().getWithAddedOrReplaced(updatedAnnotation));
-    }
-  }
-
-  private void updateMainDexListWithSynthesizedClassMap(
-      DexApplication application,
-      MainDexInfo mainDexInfo,
-      Map<DexType, DexType> derivedMainDexTypesToIgnore) {
-    if (mainDexInfo.isEmpty()) {
-      return;
-    }
-    List<DexProgramClass> newMainDexClasses = new ArrayList<>();
-    mainDexInfo.forEachExcludingDependencies(
-        dexType -> {
-          DexProgramClass programClass =
-              DexProgramClass.asProgramClassOrNull(application.definitionFor(dexType));
-          if (programClass != null) {
-            Collection<DexType> derived =
-                DexAnnotation.readAnnotationSynthesizedClassMap(
-                    programClass, application.dexItemFactory);
-            for (DexType type : derived) {
-              DexType mappedType = derivedMainDexTypesToIgnore.getOrDefault(type, type);
-              DexProgramClass syntheticClass =
-                  DexProgramClass.asProgramClassOrNull(application.definitionFor(mappedType));
-              if (syntheticClass != null) {
-                newMainDexClasses.add(syntheticClass);
-              }
-            }
-          }
-        });
-    newMainDexClasses.forEach(mainDexInfo::addSyntheticClass);
-  }
-
-  private void clearSynthesizedClassMapping(DexApplication application) {
-    for (DexProgramClass clazz : application.classes()) {
-      clazz.setAnnotations(
-          clazz.annotations().getWithout(application.dexItemFactory.annotationSynthesizedClassMap));
-    }
-  }
-
   private static DexApplication buildLensAndProgram(
       AppView<?> appView,
       Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> syntheticMethodGroups,
@@ -454,23 +354,8 @@
     DexApplication application = appView.appInfo().app();
     DexItemFactory factory = appView.dexItemFactory();
     List<DexProgramClass> newProgramClasses = new ArrayList<>();
-
-    // TODO(b/168584485): Remove this once class-mapping support is removed.
-    Set<DexType> derivedMainDexTypes = Sets.newIdentityHashSet();
-    MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
-    mainDexInfo.forEachExcludingDependencies(
-        mainDexType -> {
-          derivedMainDexTypes.add(mainDexType);
-          DexProgramClass mainDexClass =
-              DexProgramClass.asProgramClassOrNull(
-                  appView.appInfo().definitionForWithoutExistenceAssert(mainDexType));
-          if (mainDexClass != null) {
-            derivedMainDexTypes.addAll(
-                DexAnnotation.readAnnotationSynthesizedClassMap(mainDexClass, factory));
-          }
-        });
-
     Set<DexType> pruned = Sets.newIdentityHashSet();
+
     syntheticMethodGroups.forEach(
         (syntheticType, syntheticGroup) -> {
           SyntheticMethodDefinition representative = syntheticGroup.getRepresentative();
@@ -571,8 +456,7 @@
             addMainDexAndSynthesizedFromForMember(
                 member,
                 externalSyntheticClass,
-                mainDexInfo,
-                derivedMainDexTypes,
+                appView.appInfo().getMainDexInfo(),
                 appForLookup::programDefinitionFor);
           }
         });
@@ -593,8 +477,7 @@
             addMainDexAndSynthesizedFromForMember(
                 member,
                 externalSyntheticClass,
-                mainDexInfo,
-                derivedMainDexTypes,
+                appView.appInfo().getMainDexInfo(),
                 appForLookup::programDefinitionFor);
           }
         });
@@ -644,11 +527,8 @@
       SyntheticDefinition<?, ?, ?> member,
       DexProgramClass externalSyntheticClass,
       MainDexInfo mainDexInfo,
-      Set<DexType> derivedMainDexTypes,
       Function<DexType, DexProgramClass> definitions) {
-    member
-        .getContext()
-        .addIfDerivedFromMainDexClass(externalSyntheticClass, mainDexInfo, derivedMainDexTypes);
+    member.getContext().addIfDerivedFromMainDexClass(externalSyntheticClass, mainDexInfo);
     // TODO(b/168584485): Remove this once class-mapping support is removed.
     DexProgramClass from = definitions.apply(member.getContext().getSynthesizingContextType());
     if (from != null) {
@@ -661,7 +541,6 @@
     // This is currently also disabled on CF to CF desugaring to avoid missing class references to
     // the annotated classes.
     // TODO(b/147485959): Find an alternative encoding for synthetics to avoid missing-class refs.
-    // TODO(b/168584485): Remove support for main-dex tracing with the class-map annotation.
     return options.intermediate && !options.cfToCfDesugar;
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index 6e501fc..7181e84 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -39,7 +39,8 @@
   O_MR1(27),
   P(28),
   Q(29),
-  R(30);
+  R(30),
+  S(31);
 
   public static final AndroidApiLevel LATEST = R;
 
diff --git a/src/main/java/com/android/tools/r8/utils/DexUtils.java b/src/main/java/com/android/tools/r8/utils/DexUtils.java
new file mode 100644
index 0000000..265a130
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/DexUtils.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2021, 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.utils;
+
+public class DexUtils {
+  public static String getDefaultDexFileName(int fileIndex) {
+    return fileIndex == 0
+        ? "classes" + FileUtils.DEX_EXTENSION
+        : ("classes" + (fileIndex + 1) + FileUtils.DEX_EXTENSION);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 4e787b6..ea310aa 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1046,8 +1046,11 @@
   }
 
   public enum PackageObfuscationMode {
-    // General package obfuscation.
+    // No package obfuscation.
     NONE,
+    // Strategy based on ordinary package obfuscation when no package-obfuscation mode is specified
+    // by the users. In practice this falls back to FLATTEN but with keeping package-names.
+    MINIFICATION,
     // Repackaging all classes into the single user-given (or top-level) package.
     REPACKAGE,
     // Repackaging all packages into the single user-given (or top-level) package.
@@ -1065,6 +1068,10 @@
       return this == REPACKAGE;
     }
 
+    public boolean isMinification() {
+      return this == MINIFICATION;
+    }
+
     public boolean isSome() {
       return !isNone();
     }
@@ -1242,10 +1249,7 @@
     public Consumer<String> processingContextsConsumer = null;
 
     public Function<AppView<AppInfoWithLiveness>, RepackagingConfiguration>
-        repackagingConfigurationFactory =
-            appView ->
-                new DefaultRepackagingConfiguration(
-                    appView.dexItemFactory(), appView.options().getProguardConfiguration());
+        repackagingConfigurationFactory = DefaultRepackagingConfiguration::new;
 
     public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
         ConsumerUtils.emptyBiConsumer();
diff --git a/src/test/examples/naming101/c.java b/src/test/examples/naming101/c.java
index 21744bc..7477024 100644
--- a/src/test/examples/naming101/c.java
+++ b/src/test/examples/naming101/c.java
@@ -4,5 +4,5 @@
 package naming101;
 
 public class c {
-  public static int i = 1;
+  static int i = 1;
 }
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 82cfebf..43bbd99 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -361,15 +361,25 @@
   abstract D8IncrementalTestRunner test(String testName, String packageName, String mainClass);
 
   @Override
-  protected void testIntermediateWithMainDexList(String packageName, Path input,
-      int expectedMainDexListSize, String... mainDexClasses) throws Throwable {
+  protected void testIntermediateWithMainDexList(
+      String packageName,
+      Path input,
+      int expectedMainDexListSize,
+      List<String> mainDexClasses,
+      List<String> mainDexOverApproximation)
+      throws Throwable {
     // Skip those tests.
     Assume.assumeTrue(false);
   }
 
   @Override
-  protected Path buildDexThroughIntermediate(String packageName, Path input, OutputMode outputMode,
-      AndroidApiLevel minApi, String... mainDexClasses) throws Throwable {
+  protected Path buildDexThroughIntermediate(
+      String packageName,
+      Path input,
+      OutputMode outputMode,
+      AndroidApiLevel minApi,
+      List<String> mainDexClasses)
+      throws Throwable {
     // tests using this should already been skipped.
     throw new Unreachable();
   }
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index 4c88d91..4913bc5 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.D8Command.Builder;
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
+import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
@@ -86,4 +87,16 @@
     builder.addMainDexRulesFiles(mainDexRuleFiles);
     return self();
   }
+
+  public D8TestBuilder addMainDexRules(String... rules) {
+    builder.addMainDexRules(Arrays.asList(rules), Origin.unknown());
+    return self();
+  }
+
+  public D8TestBuilder addMainDexKeepClassRules(Class<?>... classes) {
+    for (Class<?> clazz : classes) {
+      addMainDexRules("-keep class " + clazz.getTypeName());
+    }
+    return self();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index 2d21521..118f3b1 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -1780,6 +1780,7 @@
               anyDexVm())
           .put("lang.Thread.getState.Thread_getState_A01", anyDexVm())
           .put("lang.Thread.join.Thread_join_A01", anyDexVm())
+          .put("lang.ThreadGroup.destroy.ThreadGroup_destroy_A01", match(JAVA_RUNTIME))
           .put(
               "util.concurrent.ScheduledThreadPoolExecutor.getTaskCount.ScheduledThreadPoolExecutor_getTaskCount_A01",
               any())
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java
index 37f4222..1696327 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -24,6 +24,7 @@
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.hamcrest.Matcher;
+import org.junit.rules.TemporaryFolder;
 
 public abstract class KotlinTestBase extends TestBase {
 
@@ -107,6 +108,11 @@
   }
 
   public static KotlinCompileMemoizer getCompileMemoizer(
+      Collection<Path> sources, CfRuntime runtime, TemporaryFolder temporaryFolder) {
+    return new KotlinCompileMemoizer(sources, runtime, temporaryFolder);
+  }
+
+  public static KotlinCompileMemoizer getCompileMemoizer(
       Collection<Path> sources, String sharedFolder) {
     return compileMemoizers.computeIfAbsent(
         sharedFolder, ignore -> new KotlinCompileMemoizer(sources));
@@ -131,12 +137,22 @@
   public static class KotlinCompileMemoizer {
 
     private final Collection<Path> sources;
+    private final CfRuntime runtime;
+    private final TemporaryFolder temporaryFolder;
+
     private Consumer<KotlinCompilerTool> kotlinCompilerToolConsumer = x -> {};
     private final Map<KotlinCompiler, Map<KotlinTargetVersion, Path>> compiledPaths =
         new IdentityHashMap<>();
 
     public KotlinCompileMemoizer(Collection<Path> sources) {
+      this(sources, CfRuntime.getCheckedInJdk9(), null);
+    }
+
+    public KotlinCompileMemoizer(
+        Collection<Path> sources, CfRuntime runtime, TemporaryFolder temporaryFolder) {
       this.sources = sources;
+      this.runtime = runtime;
+      this.temporaryFolder = temporaryFolder;
     }
 
     public KotlinCompileMemoizer configure(Consumer<KotlinCompilerTool> consumer) {
@@ -159,10 +175,11 @@
           targetVersion,
           ignored -> {
             try {
-              return kotlinc(compiler, targetVersion)
-                  .addSourceFiles(sources)
-                  .apply(kotlinCompilerToolConsumer)
-                  .compile();
+              KotlinCompilerTool kotlinc =
+                  temporaryFolder == null
+                      ? kotlinc(compiler, targetVersion)
+                      : kotlinc(runtime, temporaryFolder, compiler, targetVersion);
+              return kotlinc.addSourceFiles(sources).apply(kotlinCompilerToolConsumer).compile();
             } catch (IOException e) {
               throw new RuntimeException(e);
             }
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
deleted file mode 100644
index 74dfeb4..0000000
--- a/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.R8Command.Builder;
-import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.utils.InternalOptions;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class R8RunExamplesKotlinTest extends R8RunExamplesCommon {
-
-  @Override
-  protected void configure(InternalOptions options) {
-    super.configure(options);
-  }
-
-  @Parameters(name = "{0}_{1}_{2}_{3}_{5}_{6}")
-  public static Collection<String[]> data() {
-    String[] tests = {
-        "loops.LoopKt"
-    };
-
-    List<String[]> fullTestList = new ArrayList<>(tests.length * 2);
-    for (String test : tests) {
-      fullTestList.add(
-          makeTest(Input.JAVAC, CompilerUnderTest.D8, CompilationMode.DEBUG, test, Output.DEX));
-      fullTestList.add(
-          makeTest(Input.JAVAC, CompilerUnderTest.D8, CompilationMode.RELEASE, test, Output.DEX));
-      fullTestList.add(makeTest(Input.DX, CompilerUnderTest.R8, CompilationMode.DEBUG, test));
-      fullTestList.add(makeTest(Input.DX, CompilerUnderTest.R8, CompilationMode.RELEASE, test));
-      fullTestList.add(
-          makeTest(Input.JAVAC, CompilerUnderTest.R8, CompilationMode.DEBUG, test, Output.CF));
-      fullTestList.add(
-          makeTest(Input.JAVAC, CompilerUnderTest.R8, CompilationMode.RELEASE, test, Output.CF));
-    }
-    return fullTestList;
-  }
-
-  @Override
-  public Builder addInputFile(Builder builder) {
-    return super.addInputFile(builder)
-        .addProgramFiles(ToolHelper.getKotlinAnnotationJar(KotlinCompiler.latest()));
-  }
-
-  @Override
-  protected String getExampleDir() {
-    return ToolHelper.EXAMPLES_KOTLIN_BUILD_DIR;
-  }
-
-  @Override
-  protected Map<String, TestCondition> getFailingRun() {
-    return Collections.emptyMap();
-  }
-
-  @Override
-  protected Map<String, TestCondition> getFailingRunCf() {
-    return Collections.emptyMap();
-  }
-
-  @Override
-  protected Set<String> getFailingCompileCfToDex() {
-    return Collections.emptySet();
-  }
-
-  @Override
-  protected Set<String> getFailingRunCfToDex() {
-    return Collections.emptySet();
-  }
-
-  @Override
-  protected Set<String> getFailingCompileCf() {
-    return Collections.emptySet();
-  }
-
-  @Override
-  protected Set<String> getFailingOutputCf() {
-    return Collections.emptySet();
-  }
-
-  @Override
-  protected Map<String, TestCondition> getOutputNotIdenticalToJVMOutput() {
-    return Collections.emptyMap();
-  }
-
-  @Override
-  protected Map<String, TestCondition> getSkip() {
-    return Collections.emptyMap();
-  }
-
-  public R8RunExamplesKotlinTest(
-      String pkg,
-      String input,
-      String compiler,
-      String mode,
-      String mainClass,
-      String output) {
-    super(pkg, input, compiler, mode, mainClass, output);
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 2773cf1..52480be 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -9,15 +9,19 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OffOrAuto;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.android.tools.r8.utils.TestDescriptionWatcher;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -27,6 +31,8 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
 import com.google.common.io.ByteStreams;
 import java.io.IOException;
 import java.io.InputStream;
@@ -35,13 +41,12 @@
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutionException;
+import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.UnaryOperator;
@@ -114,6 +119,24 @@
       return withBuilderTransformation(builder -> builder.addMainDexClasses(classes));
     }
 
+    C withMainDexKeepClassRules(List<String> classes) {
+      return withBuilderTransformation(
+          builder -> {
+            if (builder instanceof D8Command.Builder) {
+              ((D8Command.Builder) builder)
+                  .addMainDexRules(
+                      ListUtils.map(classes, c -> "-keep class " + c), Origin.unknown());
+            } else if (builder instanceof R8Command.Builder) {
+              ((R8Command.Builder) builder)
+                  .addMainDexRules(
+                      ListUtils.map(classes, c -> "-keep class " + c), Origin.unknown());
+            } else {
+              fail("Unexpected builder type: " + builder.getClass());
+            }
+            return builder;
+          });
+    }
+
     C withInterfaceMethodDesugaring(OffOrAuto behavior) {
       return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
     }
@@ -465,15 +488,20 @@
     testIntermediateWithMainDexList(
         "lambdadesugaring",
         1,
-        "lambdadesugaring.LambdaDesugaring$I");
+        ImmutableList.of("lambdadesugaring.LambdaDesugaring$I"),
+        ImmutableList.of());
   }
 
   @Test
   public void testLambdaDesugaringWithMainDexList2() throws Throwable {
     // Main dex class has many lambdas.
-    testIntermediateWithMainDexList("lambdadesugaring",
-        33,
-        "lambdadesugaring.LambdaDesugaring$Refs$B");
+    testIntermediateWithMainDexList(
+        "lambdadesugaring",
+        // TODO(b/180074885): Over approximation not present in R8.
+        this instanceof R8RunExamplesAndroidOTest ? 51 : 52,
+        ImmutableList.of("lambdadesugaring.LambdaDesugaring$Refs$B"),
+        // TODO(b/180074885): Over approximation due to invoke-dynamic reference adds as dependency.
+        ImmutableList.of("lambdadesugaring.LambdaDesugaring$Refs$D"));
   }
 
   @Test
@@ -483,7 +511,11 @@
         "interfacemethods",
         Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION),
         2,
-        "interfacemethods.I1");
+        ImmutableList.of("interfacemethods.I1"),
+        // TODO(b/180074885): Over approximation due to including I1-CC by being derived from I1,
+        //  but after desugaring I1 does not reference I1$-CC (the static method is moved), so it
+        //  is incorrect to include I1-CC in the main dex.
+        ImmutableList.of("interfacemethods.I1$-CC"));
   }
 
 
@@ -494,7 +526,11 @@
         "interfacemethods",
         Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION),
         2,
-        "interfacemethods.I2");
+        ImmutableList.of("interfacemethods.I2"),
+        // TODO(b/180074885): Over approximation due to including I2$-CC by being derived from I2,
+        //  but after desugaring I2 does not reference I2$-CC (the default method is moved), so it
+        //  is incorrect to include I2$-CC in the main dex.
+        ImmutableList.of("interfacemethods.I2$-CC"));
   }
 
   @Test
@@ -510,20 +546,23 @@
   private void testIntermediateWithMainDexList(
       String packageName,
       int expectedMainDexListSize,
-      String... mainDexClasses)
+      List<String> mainDexClasses,
+      List<String> mainDexOverApproximation)
       throws Throwable {
     testIntermediateWithMainDexList(
         packageName,
         Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION),
         expectedMainDexListSize,
-        mainDexClasses);
+        mainDexClasses,
+        mainDexOverApproximation);
   }
 
   protected void testIntermediateWithMainDexList(
       String packageName,
       Path input,
       int expectedMainDexListSize,
-      String... mainDexClasses)
+      List<String> mainDexClasses,
+      List<String> mainDexOverApproximation)
       throws Throwable {
     AndroidApiLevel minApi = AndroidApiLevel.K;
 
@@ -534,16 +573,18 @@
             .withMinApiLevel(minApi)
             .withOptionConsumer(option -> option.minimalMainDex = true)
             .withOptionConsumer(option -> option.enableInheritanceClassInDexDistributor = false)
-            .withMainDexClass(mainDexClasses)
+            .withMainDexKeepClassRules(mainDexClasses)
             .withKeepAll();
     Path fullDexes = temp.getRoot().toPath().resolve(packageName + "full" + ZIP_EXTENSION);
     full.build(input, fullDexes);
 
     // Builds with intermediate in both output mode.
-    Path dexesThroughIndexedIntermediate = buildDexThroughIntermediate(
-        packageName, input, OutputMode.DexIndexed, minApi, mainDexClasses);
-    Path dexesThroughFilePerInputClassIntermediate = buildDexThroughIntermediate(
-        packageName, input, OutputMode.DexFilePerClassFile, minApi, mainDexClasses);
+    Path dexesThroughIndexedIntermediate =
+        buildDexThroughIntermediate(
+            packageName, input, OutputMode.DexIndexed, minApi, mainDexClasses);
+    Path dexesThroughFilePerInputClassIntermediate =
+        buildDexThroughIntermediate(
+            packageName, input, OutputMode.DexFilePerClassFile, minApi, mainDexClasses);
 
     // Collect main dex types.
     CodeInspector fullInspector = getMainDexInspector(fullDexes);
@@ -551,20 +592,45 @@
         getMainDexInspector(dexesThroughIndexedIntermediate);
     CodeInspector filePerInputClassIntermediateInspector =
         getMainDexInspector(dexesThroughFilePerInputClassIntermediate);
-    Collection<String> fullMainClasses = new HashSet<>();
+    Set<String> fullMainClasses = new HashSet<>();
     fullInspector.forAllClasses(
         clazz -> fullMainClasses.add(clazz.getFinalDescriptor()));
-    Collection<String> indexedIntermediateMainClasses = new HashSet<>();
+    Set<String> indexedIntermediateMainClasses = new HashSet<>();
     indexedIntermediateInspector.forAllClasses(
         clazz -> indexedIntermediateMainClasses.add(clazz.getFinalDescriptor()));
-    Collection<String> filePerInputClassIntermediateMainClasses = new HashSet<>();
+    Set<String> filePerInputClassIntermediateMainClasses = new HashSet<>();
     filePerInputClassIntermediateInspector.forAllClasses(
         clazz -> filePerInputClassIntermediateMainClasses.add(clazz.getFinalDescriptor()));
 
     // Check.
     Assert.assertEquals(expectedMainDexListSize, fullMainClasses.size());
-    Assert.assertEquals(fullMainClasses, indexedIntermediateMainClasses);
-    Assert.assertEquals(fullMainClasses, filePerInputClassIntermediateMainClasses);
+    SetView<String> adjustedFull =
+        Sets.difference(
+            fullMainClasses,
+            new HashSet<>(
+                ListUtils.map(mainDexOverApproximation, DescriptorUtils::javaTypeToDescriptor)));
+    assertEqualSets(adjustedFull, indexedIntermediateMainClasses);
+    assertEqualSets(adjustedFull, filePerInputClassIntermediateMainClasses);
+  }
+
+  <T> void assertEqualSets(Set<T> expected, Set<T> actual) {
+    SetView<T> missing = Sets.difference(expected, actual);
+    SetView<T> unexpected = Sets.difference(actual, expected);
+    if (missing.isEmpty() && unexpected.isEmpty()) {
+      return;
+    }
+    StringBuilder builder = new StringBuilder("Sets differ.");
+    if (!missing.isEmpty()) {
+      builder.append("\nMissing items: [\n  ");
+      StringUtils.append(builder, missing, "\n  ", BraceType.NONE);
+      builder.append("\n]");
+    }
+    if (!unexpected.isEmpty()) {
+      builder.append("\nUnexpected items: [\n  ");
+      StringUtils.append(builder, unexpected, "\n  ", BraceType.NONE);
+      builder.append("\n]");
+    }
+    fail(builder.toString());
   }
 
   protected Path buildDexThroughIntermediate(
@@ -572,7 +638,7 @@
       Path input,
       OutputMode outputMode,
       AndroidApiLevel minApi,
-      String... mainDexClasses)
+      List<String> mainDexClasses)
       throws Throwable {
     Path intermediateDex =
         temp.getRoot().toPath().resolve(packageName + "intermediate" + ZIP_EXTENSION);
@@ -591,7 +657,7 @@
         test(packageName + "dex", packageName, "N/A")
             .withOptionConsumer(option -> option.minimalMainDex = true)
             .withOptionConsumer(option -> option.enableInheritanceClassInDexDistributor = false)
-            .withMainDexClass(mainDexClasses)
+            .withMainDexKeepClassRules(mainDexClasses)
             .withMinApiLevel(minApi)
             .withKeepAll();
 
@@ -644,8 +710,7 @@
     }
   }
 
-  protected CodeInspector getMainDexInspector(Path zip)
-      throws IOException, ExecutionException {
+  protected CodeInspector getMainDexInspector(Path zip) throws IOException {
     try (ZipFile zipFile = new ZipFile(zip.toFile(), StandardCharsets.UTF_8)) {
       try (InputStream in =
           zipFile.getInputStream(zipFile.getEntry(ToolHelper.DEFAULT_DEX_FILENAME))) {
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 1ad5db8..aad62cb 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8;
 
-
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
 import com.android.tools.r8.references.MethodReference;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 883a2b4..7721c08 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -103,14 +103,10 @@
   public static final String EXAMPLES_ANDROID_N_DIR = TESTS_DIR + "examplesAndroidN/";
   public static final String EXAMPLES_ANDROID_O_DIR = TESTS_DIR + "examplesAndroidO/";
   public static final String EXAMPLES_ANDROID_P_DIR = TESTS_DIR + "examplesAndroidP/";
-  public static final String EXAMPLES_KOTLIN_DIR = TESTS_DIR + "examplesKotlin/";
   public static final String TESTS_BUILD_DIR = BUILD_DIR + "test/";
   public static final String JDK_TESTS_BUILD_DIR = TESTS_BUILD_DIR + "jdk11Tests/";
   public static final String EXAMPLES_BUILD_DIR = TESTS_BUILD_DIR + "examples/";
   public static final String EXAMPLES_CF_DIR = EXAMPLES_BUILD_DIR + "classes/";
-  public static final String EXAMPLES_KOTLIN_BUILD_DIR = TESTS_BUILD_DIR + "examplesKotlin/";
-  public static final String EXAMPLES_KOTLIN_RESOURCE_DIR =
-      TESTS_BUILD_DIR + "kotlinR8TestResources/";
   public static final String EXAMPLES_ANDROID_N_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidN/";
   public static final String EXAMPLES_ANDROID_O_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidO/";
   public static final String EXAMPLES_ANDROID_P_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidP/";
@@ -821,6 +817,8 @@
       case J_MR1:
       case J_MR2:
       case K_WATCH:
+        // TODO(b/1813562600): Add android jar for S.
+      case S:
         return false;
       default:
         return true;
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerBottomTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerBottomTest.java
new file mode 100644
index 0000000..3c71dbe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerBottomTest.java
@@ -0,0 +1,116 @@
+// 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.accessrelaxation;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.utils.DescriptorUtils;
+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 PackagePrivateOverridePublicizerBottomTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final String[] EXPECTED = new String[] {"SubViewModel.clear()", "ViewModel.clear()"};
+  private final String[] EXPECTED_ART_4 =
+      new String[] {"SubViewModel.clear()", "SubViewModel.clear()"};
+  private final String NEW_DESCRIPTOR = "Lfoo/bar/baz/SubViewModel;";
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public PackagePrivateOverridePublicizerBottomTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(ViewModel.class)
+        .addProgramClassFileData(
+            getSubViewModelInAnotherPackage(), getRewrittenSubViewModelInMain())
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::assertSuccessOutput);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(ViewModel.class)
+        .addProgramClassFileData(
+            getSubViewModelInAnotherPackage(), getRewrittenSubViewModelInMain())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .enableInliningAnnotations()
+        .enableNoVerticalClassMergingAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .allowAccessModification()
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/181328496): This should be EXPECTED.
+        .assertSuccessWithOutputLines(EXPECTED_ART_4);
+  }
+
+  private byte[] getSubViewModelInAnotherPackage() throws Exception {
+    return transformer(SubViewModel.class).setClassDescriptor(NEW_DESCRIPTOR).transform();
+  }
+
+  private byte[] getRewrittenSubViewModelInMain() throws Exception {
+    return transformer(Main.class)
+        .replaceClassDescriptorInMethodInstructions(
+            DescriptorUtils.javaTypeToDescriptor(SubViewModel.class.getTypeName()), NEW_DESCRIPTOR)
+        .transform();
+  }
+
+  private void assertSuccessOutput(TestRunResult<?> result) {
+    if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik()) {
+      result.assertSuccessWithOutputLines(EXPECTED_ART_4);
+    } else {
+      result.assertSuccessWithOutputLines(EXPECTED);
+    }
+  }
+
+  @SuppressWarnings("override") /* after changing the package the clear method is not overridden */
+  @NoVerticalClassMerging
+  public static class ViewModel {
+
+    @NeverInline
+    void clear() {
+      System.out.println("ViewModel.clear()");
+    }
+  }
+
+  @NeverClassInline
+  @SuppressWarnings("override") /* after changing the package the clear method is not overridden */
+  public static class /* foo.bar.baz. */ SubViewModel extends ViewModel {
+
+    @NeverInline
+    void clear() {
+      System.out.println("SubViewModel.clear()");
+    }
+
+    public void callBridge() {
+      clear();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      SubViewModel viewModel = new SubViewModel();
+      viewModel.callBridge();
+      ((ViewModel) viewModel).clear();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerTest.java
index 630ff62..5a9da04 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerTest.java
@@ -23,6 +23,7 @@
 
   private final TestParameters parameters;
   private final String[] EXPECTED = new String[] {"SubViewModel.clear()", "ViewModel.clear()"};
+  private final String[] R8_OUT = new String[] {"SubViewModel.clear()", "SubViewModel.clear()"};
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
@@ -50,12 +51,9 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .allowAccessModification()
-        .addOptionsModification(
-            options -> {
-              options.enablePackagePrivateAwarePublicization = true;
-            })
         .run(parameters.getRuntime(), Main.class)
-        .apply(this::assertSuccessOutput);
+        // TODO(b/172496438): This should be EXPECTED.
+        .assertSuccessWithOutputLines(R8_OUT);
   }
 
   private void assertSuccessOutput(TestRunResult<?> result) {
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java b/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java
index 9040a68..d63ead6 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java
@@ -25,6 +25,7 @@
   public int nonStaticField = 42;
 
   // Virtual method to target.
+  @Override
   public CallSite virtualMethod(MethodHandles.Lookup caller, String name, MethodType type)
       throws Exception {
     return new ConstantCallSite(caller.findStatic(caller.lookupClass(), name, type));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
index 04bb9d7..c551a26 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
@@ -32,18 +32,19 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
+        .addKeepPackageNamesRule(Main.class.getPackage())
         .addHorizontallyMergedClassesInspector(
             HorizontallyMergedClassesInspector::assertNoClassesMerged)
         .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatMatches(
-            allOf(
-                containsString("java.lang.UnsatisfiedLinkError:"),
-                containsString("com.android.tools.r8.classmerging.horizontal.b.a(")))
         .inspectFailure(
             codeInspector -> {
               assertThat(codeInspector.clazz(A.class), isPresent());
               assertThat(codeInspector.clazz(B.class), isPresent());
-            });
+            })
+        .assertFailureWithErrorThatMatches(
+            allOf(
+                containsString("java.lang.UnsatisfiedLinkError:"),
+                containsString("com.android.tools.r8.classmerging.horizontal.b.a(")));
   }
 
   @NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
index 034f226..68e882e 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
@@ -388,7 +388,7 @@
         buildWithMemberNamesRule(testForR8(parameters.getBackend())).compile());
   }
 
-  // Tests for "-keepclassmembernames" and *no* minification.
+  // Tests for "-keepclassmembernames" and minification.
 
   private <
           C extends BaseCompilerCommand,
diff --git a/src/test/java/com/android/tools/r8/compatproguard/KeepClassMemberNamesMinificationTest.java b/src/test/java/com/android/tools/r8/compatproguard/KeepClassMemberNamesMinificationTest.java
new file mode 100644
index 0000000..009cf9f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compatproguard/KeepClassMemberNamesMinificationTest.java
@@ -0,0 +1,120 @@
+// 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.compatproguard;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+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 KeepClassMemberNamesMinificationTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public KeepClassMemberNamesMinificationTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testForRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(Main.class, A.class, ReflectiveCallerOfA.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello World!", "Hello World!");
+  }
+
+  @Test
+  public void testPG() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testKeepNames(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
+  }
+
+  @Test
+  public void testR8Compat() throws Exception {
+    testKeepNames(
+        testForR8Compat(parameters.getBackend())
+            .enableInliningAnnotations()
+            .enableNeverClassInliningAnnotations());
+  }
+
+  @Test
+  public void testR8Full() throws Exception {
+    testKeepNames(
+        testForR8(parameters.getBackend())
+            .enableInliningAnnotations()
+            .enableNeverClassInliningAnnotations());
+  }
+
+  private void testKeepNames(TestShrinkerBuilder<?, ?, ?, ?, ?> shrinkerBuilder) throws Exception {
+    shrinkerBuilder
+        .addProgramClasses(Main.class, A.class, ReflectiveCallerOfA.class)
+        .addKeepMainRule(Main.class)
+        .addKeepClassAndMembersRulesWithAllowObfuscation(A.class)
+        .addKeepRules("-keepclassmembernames class ** { *; }")
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject aClass = inspector.clazz(A.class);
+              assertThat(aClass, isPresentAndRenamed());
+              assertThat(aClass.uniqueFieldWithName("foo"), isPresentAndNotRenamed());
+              assertThat(aClass.uniqueMethodWithName("foo"), isPresentAndNotRenamed());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello World!", "Hello World!");
+  }
+
+  @NeverClassInline
+  public static class A {
+
+    public String foo = "Hello World!";
+
+    public void foo() {
+      System.out.println("Hello World!");
+    }
+
+    @NeverInline
+    public void callMySelf() throws Exception {
+      ReflectiveCallerOfA.callA(this.getClass().getName());
+    }
+  }
+
+  public static class ReflectiveCallerOfA {
+
+    @NeverInline
+    public static void callA(String className) throws Exception {
+      Class<?> aClass = Class.forName(className);
+      Object o = aClass.getDeclaredConstructor().newInstance();
+      System.out.println(aClass.getDeclaredField("foo").get(o));
+      aClass.getDeclaredMethod("foo").invoke(o);
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) throws Exception {
+      new A().callMySelf();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java
new file mode 100644
index 0000000..539af51
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.debug;
+
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+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 ContinuousKotlinSteppingTest extends DebugTestBase {
+
+  private static final String MAIN_METHOD_NAME = "main";
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+  }
+
+  private final TestParameters parameters;
+  private final KotlinTestParameters kotlinParameters;
+
+  public ContinuousKotlinSteppingTest(
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    this.parameters = parameters;
+    this.kotlinParameters = kotlinParameters;
+  }
+
+  @Test
+  public void testContinuousSingleStepKotlinApp() throws Throwable {
+    KotlinDebugD8Config d8Config =
+        KotlinDebugD8Config.build(kotlinParameters, parameters.getApiLevel());
+    runContinuousTest("KotlinApp", d8Config, MAIN_METHOD_NAME);
+  }
+
+  @Test
+  public void testContinuousSingleStepKotlinInline() throws Throwable {
+    KotlinDebugD8Config d8Config =
+        KotlinDebugD8Config.build(kotlinParameters, parameters.getApiLevel());
+    runContinuousTest("KotlinInline", d8Config, MAIN_METHOD_NAME);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index 4552be6..beed8fc 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -4,8 +4,12 @@
 
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.KotlinCompilerTool;
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestBase.KotlinCompileMemoizer;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -26,16 +30,12 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 import java.util.stream.Collectors;
-import org.apache.harmony.jpda.tests.framework.jdwp.Value;
-import org.junit.AfterClass;
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
@@ -47,14 +47,11 @@
 
   private static final String MAIN_METHOD_NAME = "main";
 
-  private static final TemporaryFolder testTemp = ToolHelper.getTemporaryFolderForTest();
-
   // A list of self-contained jars to process (which do not depend on other jar files).
   private static List<Pair<Path, Predicate<Version>>> listOfJars() {
     return new ConfigListBuilder()
         .add(DebugTestBase.DEBUGGEE_JAR, ContinuousSteppingTest::allVersions)
         .add(DebugTestBase.DEBUGGEE_JAVA8_JAR, ContinuousSteppingTest::allVersions)
-        .addAllKotlinDebugJars(testTemp, ContinuousSteppingTest::allVersions)
         .addAll(
             findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR)),
             ContinuousSteppingTest::fromAndroidN)
@@ -64,8 +61,6 @@
         .build();
   }
 
-  private static final Map<Path, DebugTestConfig> compiledJarConfig = new HashMap<>();
-
   private final String mainClass;
   private final Path jarPath;
 
@@ -87,9 +82,15 @@
 
     public ConfigListBuilder addAllKotlinDebugJars(
         TemporaryFolder temp, Predicate<Version> predicate) {
+      KotlinCompileMemoizer compiledJars =
+          KotlinTestBase.getCompileMemoizer(
+                  KotlinTestBase.getKotlinFilesInResource("debug"),
+                  CfRuntime.getCheckedInJdk9(),
+                  temp)
+              .configure(KotlinCompilerTool::includeRuntime);
       for (KotlinTestParameters kotlinParameter :
           TestBase.getKotlinTestParameters().withAllCompilersAndTargetVersions().build()) {
-        add(KotlinD8Config.compileKotlinMemoized.apply(temp, kotlinParameter), predicate);
+        add(compiledJars.getForConfiguration(kotlinParameter), predicate);
       }
       return this;
     }
@@ -117,14 +118,8 @@
     }
   }
 
-  @AfterClass
-  public static void tearDown() {
-    testTemp.delete();
-  }
-
   @Parameters(name = "{0} from {1}")
   public static Collection<Object[]> getData() throws IOException {
-    testTemp.create();
     List<Object[]> testCases = new ArrayList<>();
     for (Pair<Path, Predicate<Version>> pair : listOfJars()) {
       if (pair.getSecond().test(ToolHelper.getDexVm().getVersion())) {
@@ -133,14 +128,14 @@
         for (String className : mainClasses) {
           testCases.add(new Object[]{className, jarPath});
         }
-
-        DebugTestConfig config = new D8DebugTestConfig().compileAndAdd(testTemp, jarPath);
-        compiledJarConfig.put(jarPath, config);
       }
     }
     return testCases;
   }
 
+  private static final Function<Path, DebugTestConfig> compiledJars =
+      memoizeFunction(path -> new D8DebugTestConfig().compileAndAdd(getStaticTemp(), path));
+
   public ContinuousSteppingTest(String mainClass, Path jarPath) {
     this.mainClass = mainClass;
     this.jarPath = jarPath;
@@ -148,10 +143,9 @@
 
   @Test
   public void testContinuousSingleStep() throws Throwable {
-    assert compiledJarConfig.containsKey(jarPath);
-    DebugTestConfig config = compiledJarConfig.get(jarPath);
+    DebugTestConfig config = compiledJars.apply(jarPath);
     assert config != null;
-    runContinuousTest(mainClass, config);
+    runContinuousTest(mainClass, config, MAIN_METHOD_NAME);
   }
 
   // Returns a list of classes with a "public static void main(String[])" method in the given jar
@@ -206,22 +200,4 @@
         && m.getParameterCount() == 1
         && m.getParameterTypes()[0] == String[].class;
   }
-
-  private void runContinuousTest(String debuggeeClassName, DebugTestConfig config)
-      throws Throwable {
-    runDebugTest(
-        config,
-        debuggeeClassName,
-        breakpoint(debuggeeClassName, MAIN_METHOD_NAME),
-        run(),
-        stepUntil(StepKind.OVER, StepLevel.INSTRUCTION, debuggeeState -> {
-          // Fetch local variables.
-          Map<String, Value> localValues = debuggeeState.getLocalValues();
-          Assert.assertNotNull(localValues);
-
-          // Always step until we actually exit the program.
-          return false;
-        }));
-  }
-
 }
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 6dc126c..a083bcf 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -2208,4 +2208,24 @@
       }
     }
   }
+
+  protected void runContinuousTest(
+      String debuggeeClassName, DebugTestConfig config, String mainMethodName) throws Throwable {
+    runDebugTest(
+        config,
+        debuggeeClassName,
+        breakpoint(debuggeeClassName, mainMethodName),
+        run(),
+        stepUntil(
+            StepKind.OVER,
+            StepLevel.INSTRUCTION,
+            debuggeeState -> {
+              // Fetch local variables.
+              Map<String, Value> localValues = debuggeeState.getLocalValues();
+              Assert.assertNotNull(localValues);
+
+              // Always step until we actually exit the program.
+              return false;
+            }));
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java b/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
deleted file mode 100644
index a7e31a4..0000000
--- a/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2018, 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.debug;
-
-import static com.android.tools.r8.TestBase.kotlinc;
-import static com.android.tools.r8.TestBase.memoizeBiFunction;
-
-import com.android.tools.r8.KotlinTestBase;
-import com.android.tools.r8.KotlinTestParameters;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestRuntime;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.function.BiFunction;
-import org.junit.rules.TemporaryFolder;
-
-/** Shared test configuration for D8 compiled resources from the "kotlinR8TestResources/debug". */
-class KotlinD8Config extends D8DebugTestConfig {
-
-  public static BiFunction<TemporaryFolder, KotlinTestParameters, Path> compileKotlinMemoized =
-      memoizeBiFunction(KotlinD8Config::compileWithKotlinC);
-
-  private static Path compileWithKotlinC(TemporaryFolder temp, KotlinTestParameters parameters)
-      throws IOException {
-    return kotlinc(
-            TestRuntime.getCheckedInJdk9(),
-            temp,
-            parameters.getCompiler(),
-            parameters.getTargetVersion())
-        .addSourceFiles(KotlinTestBase.getKotlinFilesInResource("debug"))
-        .includeRuntime()
-        .compile();
-  }
-
-  private static BiFunction<KotlinTestParameters, AndroidApiLevel, Path> compiledResourcesMemoized =
-      memoizeBiFunction(KotlinD8Config::getCompiledResources);
-
-  private static Path getCompiledResources(
-      KotlinTestParameters kotlinTestParameters, AndroidApiLevel apiLevel) throws IOException {
-    Path outputPath =
-        TestBase.getStaticTemp().newFolder().toPath().resolve("d8_debug_test_resources_kotlin.jar");
-    Path kotlinJar = compileKotlinMemoized.apply(TestBase.getStaticTemp(), kotlinTestParameters);
-    D8DebugTestConfig.d8Compile(Collections.singletonList(kotlinJar), apiLevel, null)
-        .write(outputPath, OutputMode.DexIndexed);
-    return outputPath;
-  }
-
-  public static KotlinD8Config build(
-      KotlinTestParameters kotlinTestParameters, AndroidApiLevel apiLevel) {
-    try {
-      KotlinD8Config kotlinD8Config = new KotlinD8Config();
-      kotlinD8Config.addPaths(compiledResourcesMemoized.apply(kotlinTestParameters, apiLevel));
-      return kotlinD8Config;
-    } catch (Throwable e) {
-      throw new RuntimeException(e);
-    }
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinDebugD8Config.java b/src/test/java/com/android/tools/r8/debug/KotlinDebugD8Config.java
new file mode 100644
index 0000000..348d43a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/KotlinDebugD8Config.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2018, 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.debug;
+
+import static com.android.tools.r8.KotlinTestBase.getCompileMemoizer;
+import static com.android.tools.r8.TestBase.memoizeBiFunction;
+
+import com.android.tools.r8.KotlinCompilerTool;
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestBase.KotlinCompileMemoizer;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.function.BiFunction;
+
+/** Shared test configuration for D8 compiled resources from the "kotlinR8TestResources/debug". */
+class KotlinDebugD8Config extends D8DebugTestConfig {
+
+  static final KotlinCompileMemoizer compiledKotlinJars =
+      getCompileMemoizer(KotlinTestBase.getKotlinFilesInResource("debug"))
+          .configure(KotlinCompilerTool::includeRuntime);
+
+  private static final BiFunction<KotlinTestParameters, AndroidApiLevel, Path>
+      compiledResourcesMemoized = memoizeBiFunction(KotlinDebugD8Config::getCompiledResources);
+
+  private static Path getCompiledResources(
+      KotlinTestParameters kotlinTestParameters, AndroidApiLevel apiLevel) throws IOException {
+    Path outputPath =
+        TestBase.getStaticTemp().newFolder().toPath().resolve("d8_debug_test_resources_kotlin.jar");
+    Path kotlinJar = compiledKotlinJars.getForConfiguration(kotlinTestParameters);
+    D8DebugTestConfig.d8Compile(Collections.singletonList(kotlinJar), apiLevel, null)
+        .write(outputPath, OutputMode.DexIndexed);
+    return outputPath;
+  }
+
+  public static KotlinDebugD8Config build(
+      KotlinTestParameters kotlinTestParameters, AndroidApiLevel apiLevel) {
+    try {
+      KotlinDebugD8Config kotlinDebugD8Config = new KotlinDebugD8Config();
+      kotlinDebugD8Config.addPaths(compiledResourcesMemoized.apply(kotlinTestParameters, apiLevel));
+      return kotlinDebugD8Config;
+    } catch (Throwable e) {
+      throw new RuntimeException(e);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index 00333e0..d205f26 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -39,8 +39,8 @@
     this.kotlinParameters = kotlinParameters;
   }
 
-  protected KotlinD8Config getD8Config() {
-    return KotlinD8Config.build(kotlinParameters, parameters.getApiLevel());
+  protected KotlinDebugD8Config getD8Config() {
+    return KotlinDebugD8Config.build(kotlinParameters, parameters.getApiLevel());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinLoopD8Config.java b/src/test/java/com/android/tools/r8/debug/KotlinLoopD8Config.java
new file mode 100644
index 0000000..c1f20d3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/KotlinLoopD8Config.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2018, 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.debug;
+
+import static com.android.tools.r8.KotlinTestBase.getCompileMemoizer;
+import static com.android.tools.r8.TestBase.memoizeBiFunction;
+
+import com.android.tools.r8.KotlinCompilerTool;
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestBase.KotlinCompileMemoizer;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.function.BiFunction;
+
+/** Shared test configuration for D8 compiled resources from the "kotlinR8TestResources/loops". */
+class KotlinLoopD8Config extends D8DebugTestConfig {
+
+  static final KotlinCompileMemoizer compiledKotlinJars =
+      getCompileMemoizer(KotlinTestBase.getKotlinFilesInResource("loops"))
+          .configure(KotlinCompilerTool::includeRuntime);
+
+  private static final BiFunction<KotlinTestParameters, AndroidApiLevel, Path>
+      compiledResourcesMemoized = memoizeBiFunction(KotlinLoopD8Config::getCompiledResources);
+
+  private static Path getCompiledResources(
+      KotlinTestParameters kotlinTestParameters, AndroidApiLevel apiLevel) throws IOException {
+    Path outputPath =
+        TestBase.getStaticTemp().newFolder().toPath().resolve("d8_debug_test_resources_kotlin.jar");
+    Path kotlinJar = compiledKotlinJars.getForConfiguration(kotlinTestParameters);
+    D8DebugTestConfig.d8Compile(Collections.singletonList(kotlinJar), apiLevel, null)
+        .write(outputPath, OutputMode.DexIndexed);
+    return outputPath;
+  }
+
+  public static KotlinLoopD8Config build(
+      KotlinTestParameters kotlinTestParameters, AndroidApiLevel apiLevel) {
+    try {
+      KotlinLoopD8Config kotlinDebugD8Config = new KotlinLoopD8Config();
+      kotlinDebugD8Config.addPaths(compiledResourcesMemoized.apply(kotlinTestParameters, apiLevel));
+      return kotlinDebugD8Config;
+    } catch (Throwable e) {
+      throw new RuntimeException(e);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinLoopTest.java b/src/test/java/com/android/tools/r8/debug/KotlinLoopTest.java
index 85c3ec5..ef55a6d 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinLoopTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinLoopTest.java
@@ -3,15 +3,34 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
-import com.android.tools.r8.ToolHelper;
-import java.nio.file.Paths;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+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 KotlinLoopTest extends KotlinDebugTestBase {
 
+  private final TestParameters parameters;
+  private final KotlinTestParameters kotlinParameters;
+
+  @Parameters(name = "{0}, {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+  }
+
+  public KotlinLoopTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    this.parameters = parameters;
+    this.kotlinParameters = kotlinParameters;
+  }
+
   DebugTestConfig config() {
-    return new D8DebugTestConfig()
-        .compileAndAdd(temp, Paths.get(ToolHelper.EXAMPLES_KOTLIN_BUILD_DIR, "loops.jar"));
+    return KotlinLoopD8Config.build(kotlinParameters, parameters.getApiLevel());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinTest.java b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
index 07e9e05..4c9f8c0 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
@@ -32,8 +32,8 @@
     this.kotlinParameters = kotlinParameters;
   }
 
-  protected KotlinD8Config getD8Config() {
-    return KotlinD8Config.build(kotlinParameters, parameters.getApiLevel());
+  protected KotlinDebugD8Config getD8Config() {
+    return KotlinDebugD8Config.build(kotlinParameters, parameters.getApiLevel());
   }
 
   // TODO(shertz) simplify test
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
index 05f84af..81f0674 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
@@ -153,7 +153,7 @@
     testForD8(parameters.getBackend())
         .addProgramClasses(CLASSES)
         .setMinApi(parameters.getApiLevel())
-        .addMainDexListClasses(MiniAssert.class, TestClass.class, User2.class)
+        .addMainDexRules(keepMainProguardConfiguration(TestClass.class))
         .setProgramConsumer(mainDexConsumer)
         .compile()
         .inspect(this::checkExpectedSynthetics)
@@ -185,44 +185,8 @@
     testForD8()
         .addProgramFiles(perClassOutput)
         .setMinApi(parameters.getApiLevel())
-        .addMainDexListClasses(MiniAssert.class, TestClass.class, User2.class)
-        .setProgramConsumer(mainDexConsumer)
-        .compile()
-        .inspect(this::checkExpectedSynthetics)
-        .run(parameters.getRuntime(), TestClass.class, getRunArgs())
-        .assertSuccessWithOutput(EXPECTED);
-    checkMainDex(mainDexConsumer);
-  }
-
-  // TODO(b/168584485): This test should be removed once support is dropped.
-  @Test
-  public void testD8MergingWithTraceCf() throws Exception {
-    assumeTrue(parameters.isDexRuntime());
-    Path out1 =
-        testForD8()
-            .addProgramClasses(User1.class)
-            .addClasspathClasses(CLASSES)
-            .setIntermediate(true)
-            .setMinApi(parameters.getApiLevel())
-            .compile()
-            .writeToZip();
-
-    Path out2 =
-        testForD8()
-            .addProgramClasses(User2.class)
-            .addClasspathClasses(CLASSES)
-            .setIntermediate(true)
-            .setMinApi(parameters.getApiLevel())
-            .compile()
-            .writeToZip();
-
-    MainDexConsumer mainDexConsumer = new MainDexConsumer();
-    testForD8(parameters.getBackend())
-        .addProgramClasses(TestClass.class, MiniAssert.class)
-        .addProgramFiles(out1, out2)
-        .setMinApi(parameters.getApiLevel())
-        .addMainDexListClassReferences(
-            traceMainDex(CLASSES, Collections.emptyList()).getMainDexList())
+        // Trace the classes run by main which will pick up their dependencies.
+        .addMainDexRules(keepMainProguardConfiguration(TestClass.class))
         .setProgramConsumer(mainDexConsumer)
         .compile()
         .inspect(this::checkExpectedSynthetics)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index ccd3cdf..0ee446d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -99,10 +99,8 @@
             shrinkDesugaredLibrary)
         .addRunClasspathFiles(CUSTOM_LIB)
         .run(parameters.getRuntime(), Executor.class)
-        .apply(
-            r ->
-                r.assertSuccessWithOutput(
-                    supportAllCallbacksFromLibrary ? EXPECTED_RESULT : FAILING_EXPECTED_RESULT));
+        .assertSuccessWithOutput(
+            supportAllCallbacksFromLibrary ? EXPECTED_RESULT : FAILING_EXPECTED_RESULT);
   }
 
   private void assertDoubleForEach(CodeInspector inspector) {
@@ -164,10 +162,8 @@
             shrinkDesugaredLibrary)
         .addRunClasspathFiles(CUSTOM_LIB)
         .run(parameters.getRuntime(), Executor.class)
-        .apply(
-            r ->
-                r.assertSuccessWithOutput(
-                    supportAllCallbacksFromLibrary ? EXPECTED_RESULT : FAILING_EXPECTED_RESULT));
+        .assertSuccessWithOutput(
+            supportAllCallbacksFromLibrary ? EXPECTED_RESULT : FAILING_EXPECTED_RESULT);
   }
 
   static class CustomLibClass {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
index b62045b..a96a874 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
@@ -8,7 +8,6 @@
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import com.android.tools.r8.desugar.desugaredlibrary.conversiontests.MoreFunctionConversionTest.CustomLibClass;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
index f1884e9..2abe133 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
@@ -96,6 +96,7 @@
             })
         .addProgramFiles(classesMatching(outerNestName))
         .applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
+        .addKeepPackageNamesRule("nesthostexample")
         .addInliningAnnotations()
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
index 073c7b1..9f41a17 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
@@ -99,6 +99,7 @@
             .enableInliningAnnotations()
             .addProgramFiles(bothNestsAndOutsideClassToCompile)
             .applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
+            .addKeepPackageNamesRule("nesthostexample")
             .compile()
             .inspect(NestAttributesUpdateTest::assertNestAttributesCorrect);
     for (int i = 0; i < mainClasses.length; i++) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
index 227ce07..7cc81c3 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
@@ -86,6 +86,7 @@
         .enableNeverClassInliningAnnotations()
         .enableInliningAnnotations()
         .minification(minification)
+        .addKeepPackageNamesRule(libClass.getPackage())
         .setMinApi(parameters.getApiLevel())
         .compile()
         .writeToZip();
diff --git a/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
index 4be7975..a94f02a 100644
--- a/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
@@ -49,8 +49,9 @@
     if (parameters.getRuntime().asCf().isNewerThan(CfVm.JDK8)) {
       runResult.assertFailureWithErrorThatMatches(
           containsString(
-              "java.lang.IncompatibleClassChangeError: Method"
-                  + " com.android.tools.r8.graph.invokestatic.InvokeStaticOnInterfaceTest$I.foo()V"
+              "java.lang.IncompatibleClassChangeError: Method "
+                  + I.class.getTypeName()
+                  + ".foo()V"
                   + " must be InterfaceMethodref constant"));
     } else {
       runResult.assertSuccessWithOutputLines("Hello World!");
@@ -79,13 +80,14 @@
             .enableInliningAnnotations()
             .enableNoVerticalClassMergingAnnotations()
             .addOptionsModification(o -> o.testing.allowInvokeErrors = true)
+            .noMinification()
             .addKeepMainRule(Main.class)
             .run(parameters.getRuntime(), Main.class);
     if (parameters.getRuntime().asCf().isNewerThan(CfVm.JDK8)) {
       runResult.assertFailureWithErrorThatMatches(
           containsString(
               "java.lang.IncompatibleClassChangeError: Method"
-                  + " com.android.tools.r8.graph.invokestatic.a.a()V"
+                  + " com.android.tools.r8.graph.invokestatic.InvokeStaticOnInterfaceTest$I.foo()V"
                   + " must be InterfaceMethodref constant"));
     } else {
       runResult.assertSuccessWithOutputLines("Hello World!");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/DeadSwitchCaseWithSharedTargetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/DeadSwitchCaseWithSharedTargetTest.java
new file mode 100644
index 0000000..6fd4739
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/DeadSwitchCaseWithSharedTargetTest.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2021, 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.switches;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DeadSwitchCaseWithSharedTargetTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public DeadSwitchCaseWithSharedTargetTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addKeepRules(
+            "-assumevalues class " + Main.class.getTypeName() + " {",
+            "  static int FIELD return 27..30;",
+            "}")
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("?", "?", "O", "P", "Q", "R", "?");
+  }
+
+  static class Main {
+
+    static int FIELD;
+    static boolean alwaysTrue = System.currentTimeMillis() >= 0;
+
+    public static void main(String[] args) {
+      test(25);
+      test(26);
+      test(27);
+      test(28);
+      test(29);
+      test(30);
+      test(31);
+    }
+
+    static void test(int i) {
+      if (alwaysTrue) {
+        FIELD = i;
+      }
+      switch (FIELD) {
+        case 26:
+        case 27:
+          System.out.println("O");
+          break;
+        case 28:
+          System.out.println("P");
+          break;
+        case 29:
+          System.out.println("Q");
+          break;
+        case 30:
+          System.out.println("R");
+          break;
+        default:
+          System.out.println("?");
+          break;
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
index 679177e..68614ad 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -74,4 +74,20 @@
   public void testDontObfuscate() throws Exception {
     test(ImmutableList.of("-dontobfuscate"));
   }
+
+  @Test
+  public void testRepackage() throws Exception {
+    test(
+        ImmutableList.of(
+            "-keep,allowobfuscation class kotlin.Metadata { *; }", "-repackageclasses ''"));
+  }
+
+  @Test
+  public void testRepackageWithKeepAttributes() throws Exception {
+    test(
+        ImmutableList.of(
+            "-keep,allowobfuscation class kotlin.Metadata { *; }",
+            "-repackageclasses ''",
+            "-keepattributes *"));
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
index 265aa24..9c01d63 100644
--- a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
@@ -10,12 +10,12 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertNotEquals;
 
-import com.android.tools.r8.TestBase;
+import com.android.tools.r8.KotlinCompilerTool;
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import java.nio.file.Paths;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -23,18 +23,28 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class KotlinEnumSwitchTest extends TestBase {
+public class KotlinEnumSwitchTest extends KotlinTestBase {
 
   private final boolean enableSwitchMapRemoval;
   private final TestParameters parameters;
 
-  @Parameters(name = "{1}, enable switch map removal: {0}")
+  @Parameters(name = "{1}, enable switch map removal: {0}, {2}")
   public static List<Object[]> data() {
     return buildParameters(
-        BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+        BooleanUtils.values(),
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
-  public KotlinEnumSwitchTest(boolean enableSwitchMapRemoval, TestParameters parameters) {
+  private static final KotlinCompileMemoizer kotlinJars =
+      getCompileMemoizer(getKotlinFilesInResource("enumswitch"))
+          .configure(KotlinCompilerTool::includeRuntime);
+
+  public KotlinEnumSwitchTest(
+      boolean enableSwitchMapRemoval,
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.enableSwitchMapRemoval = enableSwitchMapRemoval;
     this.parameters = parameters;
   }
@@ -43,8 +53,7 @@
   public void test() throws Exception {
     testForR8(parameters.getBackend())
         .addProgramFiles(
-            Paths.get(ToolHelper.EXAMPLES_KOTLIN_BUILD_DIR, "enumswitch.jar"),
-            getMostRecentKotlinAnnotationJar())
+            kotlinJars.getForConfiguration(kotlinParameters), getMostRecentKotlinAnnotationJar())
         .addKeepMainRule("enumswitch.EnumSwitchKt")
         .addOptionsModification(
             options -> {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
index 1bbcb9e..d3d99ee 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
@@ -116,12 +116,11 @@
 
   @Test
   public void testD8DesugaredLambdasInMainDexList() throws Exception {
-    Path mainDexList = writeTextToTempFile(testClassMainDexName);
     TestMainDexListConsumer consumer = new TestMainDexListConsumer();
     testForD8()
         .setMinApi(AndroidApiLevel.K)
         .addProgramClasses(ImmutableList.of(TestClass.class, MyConsumer.class))
-        .addMainDexListFiles(ImmutableList.of(mainDexList))
+        .addMainDexListClasses(TestClass.class)
         .setMainDexListConsumer(consumer)
         .compile();
     assertTrue(consumer.called);
@@ -129,7 +128,6 @@
 
   @Test
   public void testD8DesugaredLambdasInMainDexListMerging() throws Exception {
-    Path mainDexList = writeTextToTempFile(testClassMainDexName);
     // Build intermediate dex code first.
     Path dexOutput =
         testForD8()
@@ -143,7 +141,7 @@
     testForD8()
         .setMinApi(AndroidApiLevel.K)
         .addProgramFiles(dexOutput)
-        .addMainDexListFiles(ImmutableList.of(mainDexList))
+        .addMainDexKeepClassRules(TestClass.class)
         .setMainDexListConsumer(consumer)
         .compile();
     assertTrue(consumer.called);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
index 9fa7fe9..5e88d7d 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
@@ -72,7 +72,7 @@
     D8TestCompileResult compileResult =
         testForD8()
             .addProgramFiles(intermediateResult.writeToZip())
-            .addMainDexListClasses(TestClass.class, A.class)
+            .addMainDexKeepClassRules(TestClass.class, A.class)
             .setMinApiThreshold(parameters.getApiLevel())
             .compile();
     checkCompilationResult(compileResult);
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
index 8dfba19..3022ea4 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
@@ -169,7 +169,7 @@
             .append(" (referenced from: ")
             .append(referencedFrom);
     if (numberOfContexts > 1) {
-      builder.append(", and ").append(numberOfContexts - 1).append(" other context");
+      builder.append(" and ").append(numberOfContexts - 1).append(" other context");
       if (numberOfContexts > 2) {
         builder.append("s");
       }
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index 2811374..6a01e83 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -86,6 +86,7 @@
     return String.join(
         System.lineSeparator(),
         adaptResourceFilenamesRule,
+        "-keeppackagenames adaptresourcefilenames**",
         "-keep class adaptresourcefilenames.TestClass {",
         "  public static void main(...);",
         "}");
diff --git a/src/test/java/com/android/tools/r8/naming/AvoidRTest.java b/src/test/java/com/android/tools/r8/naming/AvoidRTest.java
index 8feb257..4a35018 100644
--- a/src/test/java/com/android/tools/r8/naming/AvoidRTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AvoidRTest.java
@@ -47,16 +47,16 @@
     JasminBuilder jasminBuilder = new JasminBuilder();
     R8FullTestBuilder builder = testForR8(parameters.getBackend());
     for (int i = 0; i < 4; i++) {
-      jasminBuilder.addClass("TopLevel" + Integer.toString(i));
+      jasminBuilder.addClass("TopLevel" + i);
     }
     for (int i = 0; i < 4; i++) {
-      jasminBuilder.addClass("p1/SecondLevel" + Integer.toString(i));
+      jasminBuilder.addClass("p1/SecondLevel" + i);
     }
     for (int i = 0; i < 4; i++) {
-      jasminBuilder.addClass("p1/p2/ThirdLevel" + Integer.toString(i));
+      jasminBuilder.addClass("p1/p2/ThirdLevel" + i);
     }
     for (int i = 0; i < 4; i++) {
-      jasminBuilder.addClass("p2/SecondLevel" + Integer.toString(i));
+      jasminBuilder.addClass("p2/SecondLevel" + i);
     }
     builder.addProgramClassFileData(jasminBuilder.buildClasses());
     Set<String> usedDescriptors = new HashSet<>();
@@ -84,7 +84,7 @@
     JasminBuilder jasminBuilder = new JasminBuilder();
     R8FullTestBuilder builder = testForR8(parameters.getBackend());
     for (int i = 0; i < 26 * 2; i++) {
-      jasminBuilder.addClass("TestClass" + Integer.toString(i));
+      jasminBuilder.addClass("TestClass" + i);
     }
     builder.addProgramClassFileData(jasminBuilder.buildClasses());
     Set<String> usedNames = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java b/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
index e0ef67d..4683db2 100644
--- a/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
@@ -5,8 +5,9 @@
 package com.android.tools.r8.naming;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static junit.framework.TestCase.assertTrue;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
@@ -16,8 +17,6 @@
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import java.io.IOException;
 import java.nio.file.Path;
-import java.util.HashSet;
-import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -51,16 +50,11 @@
   public void test() throws IOException, CompilationFailedException, ExecutionException {
     Path dictionary = temp.getRoot().toPath().resolve("dictionary.txt");
     FileUtils.writeTextFile(dictionary, "a");
-
-    Set<String> finalNames = new HashSet<>();
-    finalNames.add(A.class.getPackage().getName() + ".a");
-    finalNames.add(B.class.getPackage().getName() + ".b");
-
     testForR8(parameters.getBackend())
         .addProgramClasses(A.class, B.class, C.class)
-        .noTreeShaking()
         .addKeepRules("-classobfuscationdictionary " + dictionary.toString())
         .addKeepMainRule(C.class)
+        .addKeepClassRulesWithAllowObfuscation(A.class, B.class)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), C.class)
         .assertSuccessWithOutput("HELLO WORLD!")
@@ -68,11 +62,12 @@
             inspector -> {
               ClassSubject clazzA = inspector.clazz(A.class);
               assertThat(clazzA, isPresent());
-              assertTrue(finalNames.contains(clazzA.getFinalName()));
-              finalNames.remove(clazzA.getFinalName());
               ClassSubject clazzB = inspector.clazz(B.class);
               assertThat(clazzB, isPresent());
-              assertTrue(finalNames.contains(clazzB.getFinalName()));
+              assertEquals(
+                  clazzA.getDexProgramClass().type.getPackageName(),
+                  clazzB.getDexProgramClass().type.getPackageName());
+              assertNotEquals(clazzA.getFinalName(), clazzB.getFinalName());
             });
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java b/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java
index 6804b54..bb7e06d 100644
--- a/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java
+++ b/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java
@@ -50,6 +50,7 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepClassRulesWithAllowObfuscation(A.class)
         .addKeepMainRule(Main.class)
+        .addKeepPackageNamesRule(Main.class.getPackage())
         .addKeepRules("-classobfuscationdictionary " + dictionary.toString())
         .applyIf(dontUseMixedCase, b -> b.addKeepRules("-dontusemixedcaseclassnames"))
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java b/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
index f72abf5..bb73c3f 100644
--- a/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
@@ -10,6 +10,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
+import com.android.tools.r8.ProguardVersion;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.naming.keeppackagenames.Top;
@@ -47,6 +48,7 @@
       assertEquals(
           getPackageNameFromDescriptor(top.getOriginalDescriptor()),
           getPackageNameFromDescriptor(top.getFinalDescriptor()));
+      assertEquals(PACKAGE_NAME, getPackageNameFromDescriptor(top.getFinalDescriptor()));
 
       ClassSubject sub = inspector.clazz(SubClass.class);
       assertThat(sub, isPresentAndRenamed());
@@ -55,13 +57,13 @@
           assertNotEquals(
               getPackageNameFromDescriptor(sub.getOriginalDescriptor()),
               getPackageNameFromDescriptor(sub.getFinalDescriptor()));
-          assertThat(
-              getPackageNameFromDescriptor(sub.getFinalDescriptor()), containsString(PACKAGE_NAME));
           break;
         case DOUBLE_ASTERISKS:
           assertEquals(
               getPackageNameFromDescriptor(sub.getOriginalDescriptor()),
               getPackageNameFromDescriptor(sub.getFinalDescriptor()));
+          assertThat(
+              getPackageNameFromDescriptor(sub.getFinalDescriptor()), containsString(PACKAGE_NAME));
           break;
       }
     }
@@ -80,7 +82,7 @@
 
   @Test
   public void testProguard() throws Exception {
-    testForProguard()
+    testForProguard(ProguardVersion.V6_0_1)
         .addProgramClasses(CLASSES)
         .addKeepAllClassesRuleWithAllowObfuscation()
         .addKeepRules(config.getKeepRule())
diff --git a/src/test/java/com/android/tools/r8/naming/MinificationMixedCaseAndNumbersTest.java b/src/test/java/com/android/tools/r8/naming/MinificationMixedCaseAndNumbersTest.java
index f27f20f..5c050a6 100644
--- a/src/test/java/com/android/tools/r8/naming/MinificationMixedCaseAndNumbersTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinificationMixedCaseAndNumbersTest.java
@@ -34,7 +34,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public MinificationMixedCaseAndNumbersTest(TestParameters parameters) {
@@ -55,8 +55,9 @@
         .addInnerClasses(MinificationMixedCaseAndNumbersTest.class)
         .addKeepMainRule(Main.class)
         .noTreeShaking()
-        .addKeepRules("-dontusemixedcaseclassnames")
-        .setMinApi(parameters.getRuntime())
+        .addKeepRules(
+            "-dontusemixedcaseclassnames", "-keeppackagenames com.android.tools.r8.naming")
+        .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPackagePrivateMemberTest.java b/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPackagePrivateMemberTest.java
new file mode 100644
index 0000000..8064520
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPackagePrivateMemberTest.java
@@ -0,0 +1,92 @@
+// 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.naming;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MinifyPackageWithKeepToPackagePrivateMemberTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public MinifyPackageWithKeepToPackagePrivateMemberTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClassFileData(
+            transformer(A.class).removeInnerClasses().transform(),
+            transformer(ReflectiveCallerOfA.class).removeInnerClasses().transform(),
+            transformer(Main.class).removeInnerClasses().transform())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello World!", "Hello World!");
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(
+            transformer(A.class).removeInnerClasses().transform(),
+            transformer(ReflectiveCallerOfA.class).removeInnerClasses().transform(),
+            transformer(Main.class).removeInnerClasses().transform())
+        .addKeepMainRule(Main.class)
+        .addKeepClassAndMembersRulesWithAllowObfuscation(A.class)
+        .addKeepRules("-keepclassmembernames class ** { *; }")
+        .addKeepRules(
+            "-identifiernamestring class "
+                + ReflectiveCallerOfA.class.getTypeName()
+                + " { java.lang.String className; }")
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello World!", "Hello World!");
+  }
+
+  static class A {
+
+    A() {}
+
+    String foo = "Hello World!";
+
+    void foo() {
+      System.out.println("Hello World!");
+    }
+  }
+
+  public static class ReflectiveCallerOfA {
+
+    private static String className =
+        "com.android.tools.r8.naming.MinifyPackageWithKeepToPackagePrivateMemberTest$A";
+
+    @NeverInline
+    public static void callA() throws Exception {
+      Class<?> aClass = Class.forName(className);
+      Object o = aClass.getDeclaredConstructor().newInstance();
+      System.out.println(aClass.getDeclaredField("foo").get(o));
+      aClass.getDeclaredMethod("foo").invoke(o);
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) throws Exception {
+      ReflectiveCallerOfA.callA();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPublicMemberTest.java b/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPublicMemberTest.java
new file mode 100644
index 0000000..240506d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPublicMemberTest.java
@@ -0,0 +1,129 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+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 MinifyPackageWithKeepToPublicMemberTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final boolean minify;
+  private final String[] EXPECTED = new String[] {"Hello World!", "Hello World!"};
+
+  @Parameters(name = "{0}, minifyA: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+  }
+
+  public MinifyPackageWithKeepToPublicMemberTest(TestParameters parameters, boolean minify) {
+    this.parameters = parameters;
+    this.minify = minify;
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClassFileData(
+            transformer(A.class).removeInnerClasses().transform(),
+            transformer(ReflectiveCallerOfA.class).removeInnerClasses().transform(),
+            transformer(Main.class).removeInnerClasses().transform())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(
+            transformer(A.class).removeInnerClasses().transform(),
+            transformer(ReflectiveCallerOfA.class).removeInnerClasses().transform(),
+            transformer(Main.class).removeInnerClasses().transform())
+        .addKeepMainRule(Main.class)
+        .applyIf(
+            minify,
+            builder -> builder.addKeepClassAndMembersRulesWithAllowObfuscation(A.class),
+            builder -> builder.addKeepClassAndMembersRules(A.class))
+        .addKeepRules("-keepclassmembernames class ** { *; }")
+        .addKeepRules(
+            "-identifiernamestring class "
+                + ReflectiveCallerOfA.class.getTypeName()
+                + " { java.lang.String className; }")
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject aClazz = inspector.clazz(A.class);
+              assertThat(aClazz, isPresentAndRenamed(minify));
+              ClassSubject reflectiveCallerOfAClass = inspector.clazz(ReflectiveCallerOfA.class);
+              assertThat(reflectiveCallerOfAClass, isPresentAndRenamed());
+              ClassSubject mainClass = inspector.clazz(Main.class);
+              assertThat(mainClass, isPresentAndNotRenamed());
+              assertNotEquals(
+                  mainClass.getDexProgramClass().type.getPackageDescriptor(),
+                  reflectiveCallerOfAClass.getDexProgramClass().type.getPackageDescriptor());
+              if (minify) {
+                assertEquals(
+                    aClazz.getDexProgramClass().type.getPackageDescriptor(),
+                    reflectiveCallerOfAClass.getDexProgramClass().type.getPackageDescriptor());
+              } else {
+                assertNotEquals(
+                    aClazz.getDexProgramClass().type.getPackageDescriptor(),
+                    reflectiveCallerOfAClass.getDexProgramClass().type.getPackageDescriptor());
+              }
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  public static class A {
+
+    public A() {}
+
+    public String foo = "Hello World!";
+
+    public void foo() {
+      System.out.println("Hello World!");
+    }
+  }
+
+  public static class ReflectiveCallerOfA {
+
+    private static String className =
+        "com.android.tools.r8.naming.MinifyPackageWithKeepToPublicMemberTest$A";
+
+    @NeverInline
+    public static void callA() throws Exception {
+      Class<?> aClass = Class.forName(className);
+      Object o = aClass.getDeclaredConstructor().newInstance();
+      System.out.println(aClass.getDeclaredField("foo").get(o));
+      aClass.getDeclaredMethod("foo").invoke(o);
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) throws Exception {
+      ReflectiveCallerOfA.callA();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
index 66ec3bb..9079f61 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
@@ -238,9 +238,9 @@
         b.getDexProgramClass().getType().getPackageName());
 
     ClassSubject sub_a = inspector.clazz("naming044.sub.SubA");
-    assertEquals(2, countPackageDepth(sub_a));
+    assertEquals(1, countPackageDepth(sub_a));
     ClassSubject sub_b = inspector.clazz("naming044.sub.SubB");
-    assertEquals(2, countPackageDepth(sub_b));
+    assertEquals(1, countPackageDepth(sub_b));
     assertEquals(
         sub_a.getDexProgramClass().getType().getPackageName(),
         sub_b.getDexProgramClass().getType().getPackageName());
@@ -327,9 +327,9 @@
 
     // All packages are renamed somehow. Need to check package hierarchy is consistent.
     ClassSubject aa = inspector.clazz("naming101.a.a");
-    assertEquals(2, countPackageDepth(aa));
+    assertEquals(1, countPackageDepth(aa));
     ClassSubject abc = inspector.clazz("naming101.a.b.c");
-    assertEquals(3, countPackageDepth(abc));
+    assertEquals(1, countPackageDepth(abc));
 
     // naming101.a/a; -> La/a/a; --prefix--> La/a
     // naming101.a/b/c; -> La/a/a/a; --prefix--> La/a/a --prefix--> La/a
@@ -356,14 +356,20 @@
         ba.getDexProgramClass().getType().getPackageName(),
         bb.getDexProgramClass().getType().getPackageName());
 
-    // All other classes can be repackaged to naming101.a, but naming101.a.a exists to make a name
-    // conflict. Thus, those should not be renamed to 'a'.
-    List<String> klasses = ImmutableList.of("naming101.c", "naming101.d", "naming101.a.b.c");
+    // We cannot repackage c or d since these have package-private members and a
+    // keep,allowobfuscation. For us to be able to repackage them, we have to use
+    // -allowaccesmodification.
+    List<String> klasses = ImmutableList.of("naming101.c", "naming101.d");
     for (String klass : klasses) {
       ClassSubject k = inspector.clazz(klass);
-      assertEquals("naming101.a", k.getDexProgramClass().getType().getPackageName());
-      assertNotEquals("naming101.a.a", k.getFinalName());
+      assertNotEquals("naming101.a", k.getDexProgramClass().getType().getPackageName());
     }
+
+    // All other classes can be repackaged to naming101.a, but naming101.a.a exists to make a name
+    // conflict. Thus, those should not be renamed to 'a'.
+    ClassSubject namingAbc = inspector.clazz("naming101.a.b.c");
+    assertEquals("naming101.a", namingAbc.getDexProgramClass().getType().getPackageName());
+    assertNotEquals("naming101.a.a", namingAbc.getFinalName());
   }
 
   private static void test101_rule104(CodeInspector inspector) {
@@ -411,11 +417,12 @@
     ClassSubject topD = inspector.clazz("naming101.d");
     assertEquals("naming101", topD.getDexProgramClass().getType().getPackageName());
 
-    // Due to the keep rule for naming101.c, package prefix naming101 is reserved.
-    // That is, every renamed class should have naming101 as parent package prefix.
-    for (String klass : klasses) {
-      ClassSubject t = inspector.clazz(klass);
-      assertTrue(t.getFinalName().startsWith("naming101."));
-    }
+    // The remaining classes are in subpackages of naming101 and will therefore not have
+    // package-private access to namin101.c
+    ClassSubject subAC = inspector.clazz("naming101.a.c");
+    assertEquals(1, countPackageDepth(subAC));
+
+    ClassSubject subABC = inspector.clazz("naming101.a.b.c");
+    assertEquals(1, countPackageDepth(subABC));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java b/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
index 133e607..0ac480f 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
@@ -57,9 +57,9 @@
         .inspect(
             inspector -> {
               ClassSubject clazzTop = inspector.clazz(Top.class);
-              assertTrue(clazzTop.getFinalName().endsWith(".a.a"));
+              assertTrue(clazzTop.getFinalName().endsWith("a.a"));
               ClassSubject clazzA = inspector.clazz(A.class);
-              assertTrue(clazzA.getFinalName().endsWith(".b.a"));
+              assertTrue(clazzA.getFinalName().endsWith("b.a"));
             });
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/PackagePrivateAllowAccessModificationPackageTest.java b/src/test/java/com/android/tools/r8/naming/PackagePrivateAllowAccessModificationPackageTest.java
index 473df64..4be6559 100644
--- a/src/test/java/com/android/tools/r8/naming/PackagePrivateAllowAccessModificationPackageTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackagePrivateAllowAccessModificationPackageTest.java
@@ -53,10 +53,6 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .addOptionsModification(
-            options -> {
-              options.enablePackagePrivateAwarePublicization = true;
-            })
         .compile()
         .inspect(
             inspector -> {
@@ -64,8 +60,7 @@
               assertThat(clazz, isPresentAndRenamed());
             })
         .run(parameters.getRuntime(), Main.class)
-        // TODO(b/172496438): This should not fail.
-        .assertFailureWithErrorThatThrows(IllegalAccessError.class);
+        .assertSuccessWithOutputLines(EXPECTED);
   }
 
   @NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java b/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
index 10ce37d..d9b9a3c 100644
--- a/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
+++ b/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
@@ -10,15 +10,15 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.TestBase;
+import com.android.tools.r8.KotlinCompilerTool;
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
-import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ImmutableSet;
 import java.io.IOException;
-import java.nio.file.Paths;
+import java.util.List;
 import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,16 +33,26 @@
  * which is why the ASMified code is generated from kotlin.
  */
 @RunWith(Parameterized.class)
-public class TestRunner extends TestBase {
+public class TestRunner extends KotlinTestBase {
 
   private final TestParameters parameters;
 
+  private static final KotlinCompileMemoizer kotlinJars =
+      getCompileMemoizer(getKotlinFilesInResource("lambdas_kstyle_generics"))
+          .configure(KotlinCompilerTool::includeRuntime);
+
   @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        getKotlinTestParameters()
+            .withAllCompilers()
+            .withTargetVersion(KotlinTargetVersion.JAVA_8)
+            .build());
   }
 
-  public TestRunner(TestParameters parameters) {
+  public TestRunner(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
@@ -52,12 +62,7 @@
     testForR8(parameters.getBackend())
         .addProgramClassFileData(Lambda1.dump(), Lambda2.dump(), Main.dump(), Alpha.dump())
         .addProgramFiles(
-            Paths.get(
-                ToolHelper.TESTS_BUILD_DIR,
-                "kotlinR8TestResources",
-                "JAVA_8",
-                "lambdas_kstyle_generics" + FileUtils.JAR_EXTENSION),
-            getMostRecentKotlinAnnotationJar())
+            kotlinJars.getForConfiguration(kotlinParameters), getMostRecentKotlinAnnotationJar())
         .addKeepMainRule(Main.class)
         .addKeepAllAttributes()
         .addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java b/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java
index f662aa6..696704f 100644
--- a/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java
+++ b/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.naming.b155249069.package_b.A;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import java.io.IOException;
@@ -29,6 +30,8 @@
   private final TestParameters parameters;
   private final boolean dontUseMixedCase;
 
+  private final String renamedATypeName = "Testpackage.A";
+
   @Parameters(name = "{0}, dontusemixedcaseclassnames: {1}")
   public static List<Object[]> data() {
     return buildParameters(
@@ -45,12 +48,22 @@
   public void testR8() throws ExecutionException, CompilationFailedException, IOException {
     Path packageDictionary = temp.getRoot().toPath().resolve("packagedictionary.txt");
     // Suggest the name 'a' for the package, to force a collision with A.A.
-    FileUtils.writeTextFile(packageDictionary, "a");
+    FileUtils.writeTextFile(packageDictionary, "testpackage");
     testForR8(parameters.getBackend())
-        .addProgramClasses(Main.class, com.android.tools.r8.naming.b155249069.A.A.class, A.class)
+        .addProgramClasses(A.class)
+        .addProgramClassFileData(
+            transformer(Main.class)
+                .replaceClassDescriptorInMethodInstructions(
+                    DescriptorUtils.javaTypeToDescriptor(
+                        com.android.tools.r8.naming.b155249069.A.A.class.getTypeName()),
+                    DescriptorUtils.javaTypeToDescriptor(renamedATypeName))
+                .transform(),
+            transformer(com.android.tools.r8.naming.b155249069.A.A.class)
+                .setClassDescriptor(DescriptorUtils.javaTypeToDescriptor(renamedATypeName))
+                .transform())
         .setMinApi(parameters.getApiLevel())
-        // Keep A.A such that the package A is kept.
-        .addKeepClassRules(com.android.tools.r8.naming.b155249069.A.A.class)
+        // Keep testpackage.A such that the package A is kept.
+        .addKeepClassRules(renamedATypeName)
         .addKeepClassRulesWithAllowObfuscation(A.class)
         .addKeepMainRule(Main.class)
         .addKeepRules("-packageobfuscationdictionary " + packageDictionary.toString())
@@ -59,8 +72,7 @@
         .assertSuccessWithOutputLines("A.A.foo()", "package_b.B.foo()")
         .inspect(
             inspector -> {
-              ClassSubject aSubject =
-                  inspector.clazz(com.android.tools.r8.naming.b155249069.A.A.class);
+              ClassSubject aSubject = inspector.clazz(renamedATypeName);
               ClassSubject bSubject = inspector.clazz(A.class);
               if (dontUseMixedCase) {
                 assertNotEquals(
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
index 63201fa..df0bfa6 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
@@ -4,12 +4,14 @@
 
 package com.android.tools.r8.repackage;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -50,12 +52,16 @@
         .inspect(
             inspector -> {
               // Find the generated lambda class
-              assertThat(
-                  ClassWithLambda.class,
-                  isRepackagedIf(inspector, repackage && parameters.isDexRuntime()));
               if (!parameters.isDexRuntime()) {
+                assertThat(ClassWithLambda.class, isNotRepackaged(inspector));
                 return;
               }
+              if (repackage) {
+                assertThat(ClassWithLambda.class, isRepackaged(inspector));
+              } else {
+                ClassSubject classWithLambdaSubject = inspector.clazz(ClassWithLambda.class);
+                assertThat(classWithLambdaSubject, isPresentAndRenamed());
+              }
               inspector.forAllClasses(
                   clazz -> {
                     if (clazz.isSynthesizedJavaLambdaClass()) {
diff --git a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
index 617676f..c5c0859 100644
--- a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
@@ -24,7 +24,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
   }
 
   public MissingInterfaceTest(TestParameters parameters) {
@@ -39,6 +39,7 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(TestClassForB112849320.class)
         .addOptionsModification(options -> options.enableInlining = false)
+        .addKeepPackageNamesRule(GoingToBeMissed.class.getPackage())
         .compile()
         .addRunClasspathFiles(
             buildOnDexRuntime(
diff --git a/src/test/java/com/android/tools/r8/testing/ToolHelperTest.java b/src/test/java/com/android/tools/r8/testing/ToolHelperTest.java
index b0e1ab2..5492f54 100644
--- a/src/test/java/com/android/tools/r8/testing/ToolHelperTest.java
+++ b/src/test/java/com/android/tools/r8/testing/ToolHelperTest.java
@@ -30,7 +30,9 @@
         ToolHelper.getFirstSupportedAndroidJar(AndroidApiLevel.K_WATCH), AndroidApiLevel.L);
     // All android.jar's for API level L are present.
     for (AndroidApiLevel androidApiLevel : AndroidApiLevel.values()) {
-      if (androidApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L)) {
+      // TODO(b/181356260): Add AndroidJar for S.
+      if (androidApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L)
+          && androidApiLevel.isLessThan(AndroidApiLevel.S)) {
         checkExpectedAndroidJar(
             ToolHelper.getFirstSupportedAndroidJar(androidApiLevel), androidApiLevel);
       }
diff --git a/src/test/examplesKotlin/enumswitch/EnumSwitch.kt b/src/test/kotlinR8TestResources/enumswitch/EnumSwitch.kt
similarity index 100%
rename from src/test/examplesKotlin/enumswitch/EnumSwitch.kt
rename to src/test/kotlinR8TestResources/enumswitch/EnumSwitch.kt
diff --git a/src/test/examplesKotlin/loops/Loop.kt b/src/test/kotlinR8TestResources/loops/Loop.kt
similarity index 100%
rename from src/test/examplesKotlin/loops/Loop.kt
rename to src/test/kotlinR8TestResources/loops/Loop.kt
diff --git a/third_party/openjdk/desugar_jdk_libs.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs.tar.gz.sha1
index 5b0c632..c53b59b 100644
--- a/third_party/openjdk/desugar_jdk_libs.tar.gz.sha1
+++ b/third_party/openjdk/desugar_jdk_libs.tar.gz.sha1
@@ -1 +1 @@
-babe03fe1d812b8eddebfc3e9a1ea4add98d4647
\ No newline at end of file
+2e91f1ecbf04376cd0d8a9bd27615aa399f374c1
\ No newline at end of file
diff --git a/tools/r8_release.py b/tools/r8_release.py
index d356586..72a5e02 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -163,7 +163,8 @@
 
 def update_prebuilds(r8_checkout, version, checkout):
   path = os.path.join(r8_checkout, 'tools', 'update_prebuilds_in_android.py')
-  subprocess.check_call([path, '--targets=lib', '--maps', '--version=' + version, checkout])
+  commit_arg = '--commit_hash=' if len(version) == 40 else '--version='
+  subprocess.check_call([path, '--targets=lib', '--maps', commit_arg + version, checkout])
 
 
 def release_studio_or_aosp(r8_checkout, path, options, git_message):