Merge commit 'a6d9f379e79bd2f7182718afa6d4d076b30330cf' into dev-release
diff --git a/build.gradle b/build.gradle
index 1fd0955..d3b511c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -838,7 +838,7 @@
     // Execute r8 commands against a stable r8 with relocated dependencies.
     // TODO(b/139725780): See if we can remove or lower the heap size (-Xmx6g).
     return [org.gradle.internal.jvm.Jvm.current().getJavaExecutable(),
-            "-Xmx6g", "-ea", "-jar", r8WithRelocatedDeps.outputs.files[0]] + args
+            "-Xmx8g", "-ea", "-jar", r8WithRelocatedDeps.outputs.files[0]] + args
 }
 
 def r8CfCommandLine(input, output, pgConfs = [], args = ["--release"], libs = []) {
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index 2915114..7dd1027 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -10,10 +11,28 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Arrays;
 
-public class BaseCompilerCommandParser {
+public class BaseCompilerCommandParser<
+    C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>> {
 
-  static void parseMinApi(BaseCompilerCommand.Builder builder, String minApiString, Origin origin) {
+  static final Iterable<String> ASSERTIONS_USAGE_MESSAGE =
+      Arrays.asList(
+          "  --force-enable-assertions[:[<class name>|<package name>...]]",
+          "  --force-ea[:[<class name>|<package name>...]]",
+          "                          # Forcefully enable javac generated assertion code.",
+          "  --force-disable-assertions[:[<class name>|<package name>...]]",
+          "  --force-da[:[<class name>|<package name>...]]",
+          "                          # Forcefully disable javac generated assertion code. This",
+          "                          # is the default handling of javac assertion code when",
+          "                          # generating DEX file format.",
+          "  --force-passthrough-assertions[:[<class name>|<package name>...]]",
+          "  --force-pa[:[<class name>|<package name>...]]",
+          "                          # Don't change javac generated assertion code. This",
+          "                          # is the default handling of javac assertion code when",
+          "                          # generating class file format.");
+
+  void parseMinApi(B builder, String minApiString, Origin origin) {
     int minApi;
     try {
       minApi = Integer.parseInt(minApiString);
@@ -28,6 +47,81 @@
     builder.setMinApiLevel(minApi);
   }
 
+  private static String PACAKGE_ASSERTION_POSTFIX = "...";
+
+  private void addAssertionTransformation(
+      B builder, AssertionTransformation transformation, String scope) {
+    if (scope == null) {
+      builder.addAssertionsConfiguration(
+          b -> b.setTransformation(transformation).setScopeAll().build());
+    } else {
+      assert scope.length() > 0;
+      if (scope.endsWith(PACAKGE_ASSERTION_POSTFIX)) {
+        builder.addAssertionsConfiguration(
+            b ->
+                b.setTransformation(transformation)
+                    .setScopePackage(
+                        scope.substring(0, scope.length() - PACAKGE_ASSERTION_POSTFIX.length()))
+                    .build());
+      } else {
+        builder.addAssertionsConfiguration(
+            b -> b.setTransformation(transformation).setScopeClass(scope).build());
+      }
+    }
+  }
+
+  boolean tryParseAssertionArgument(B builder, String arg, Origin origin) {
+    String FORCE_ENABLE_ASSERTIONS = "--force-enable-assertions";
+    String FORCE_EA = "--force-ea";
+    String FORCE_DISABLE_ASSERTIONS = "--force-disable-assertions";
+    String FORCE_DA = "--force-da";
+    String FORCE_PASSTHROUGH_ASSERTIONS = "--force-passthrough-assertions";
+    String FORCE_PA = "--force-pa";
+
+    AssertionTransformation transformation = null;
+    String remaining = null;
+    if (arg.startsWith(FORCE_ENABLE_ASSERTIONS)) {
+      transformation = AssertionTransformation.ENABLE;
+      remaining = arg.substring(FORCE_ENABLE_ASSERTIONS.length());
+    } else if (arg.startsWith(FORCE_EA)) {
+      transformation = AssertionTransformation.ENABLE;
+      remaining = arg.substring(FORCE_EA.length());
+    } else if (arg.startsWith(FORCE_DISABLE_ASSERTIONS)) {
+      transformation = AssertionTransformation.DISABLE;
+      remaining = arg.substring(FORCE_DISABLE_ASSERTIONS.length());
+    } else if (arg.startsWith(FORCE_DA)) {
+      transformation = AssertionTransformation.DISABLE;
+      remaining = arg.substring(FORCE_DA.length());
+    } else if (arg.startsWith(FORCE_PASSTHROUGH_ASSERTIONS)) {
+      transformation = AssertionTransformation.PASSTHROUGH;
+      remaining = arg.substring(FORCE_PASSTHROUGH_ASSERTIONS.length());
+    } else if (arg.startsWith(FORCE_PA)) {
+      transformation = AssertionTransformation.PASSTHROUGH;
+      remaining = arg.substring(FORCE_PA.length());
+    }
+    if (transformation != null) {
+      if (remaining.length() == 0) {
+        addAssertionTransformation(builder, transformation, null);
+        return true;
+      } else {
+        if (remaining.length() == 1 || remaining.charAt(0) != ':') {
+          return false;
+        }
+        String classOrPackageScope = remaining.substring(1);
+        if (classOrPackageScope.contains(";")
+            || classOrPackageScope.contains("[")
+            || classOrPackageScope.contains("/")) {
+          builder.error(
+              new StringDiagnostic("Illegal assertion scope: " + classOrPackageScope, origin));
+        }
+        addAssertionTransformation(builder, transformation, remaining.substring(1));
+        return true;
+      }
+    } else {
+      return false;
+    }
+  }
+
   /**
    * This method must match the lookup in
    * {@link com.android.tools.r8.JdkClassFileProvider#fromJdkHome}.
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 1099cf9..fda08a6 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.nio.file.Files;
@@ -21,7 +22,7 @@
 import java.util.List;
 import java.util.Set;
 
-public class D8CommandParser extends BaseCompilerCommandParser {
+public class D8CommandParser extends BaseCompilerCommandParser<D8Command, D8Command.Builder> {
 
   private static final Set<String> OPTIONS_WITH_PARAMETER =
       ImmutableSet.of(
@@ -108,31 +109,33 @@
   static final String USAGE_MESSAGE =
       String.join(
           "\n",
-          Arrays.asList(
-              "Usage: d8 [options] <input-files>",
-              " where <input-files> are any combination of dex, class, zip, jar, or apk files",
-              " and options are:",
-              "  --debug                 # Compile with debugging information (default).",
-              "  --release               # Compile without debugging information.",
-              "  --output <file>         # Output result in <outfile>.",
-              "                          # <file> must be an existing directory or a zip file.",
-              "  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.",
-              "  --classpath <file>      # Add <file> as a classpath resource.",
-              "  --min-api <number>      # Minimum Android API level compatibility, default: "
-                  + AndroidApiLevel.getDefault().getLevel()
-                  + ".",
-              "  --intermediate          # Compile an intermediate result intended for later",
-              "                          # merging.",
-              "  --file-per-class        # Produce a separate dex file per input class",
-              "  --no-desugaring         # Force disable desugaring.",
-              "  --desugared-lib <file>  # Specify desugared library configuration.",
-              "                          # <file> is a desugared library configuration (json).",
-              "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
-              "  --main-dex-list-output <file>",
-              "                          # Output resulting main dex list in <file>.",
-              "  --version               # Print the version of d8.",
-              "  --help                  # Print this message."));
-
+          Iterables.concat(
+              Arrays.asList(
+                  "Usage: d8 [options] <input-files>",
+                  " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+                  " and options are:",
+                  "  --debug                 # Compile with debugging information (default).",
+                  "  --release               # Compile without debugging information.",
+                  "  --output <file>         # Output result in <outfile>.",
+                  "                          # <file> must be an existing directory or a zip file.",
+                  "  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.",
+                  "  --classpath <file>      # Add <file> as a classpath resource.",
+                  "  --min-api <number>      # Minimum Android API level compatibility, default: "
+                      + AndroidApiLevel.getDefault().getLevel()
+                      + ".",
+                  "  --intermediate          # Compile an intermediate result intended for later",
+                  "                          # merging.",
+                  "  --file-per-class        # Produce a separate dex file per input class",
+                  "  --no-desugaring         # Force disable desugaring.",
+                  "  --desugared-lib <file>  # Specify desugared library configuration.",
+                  "                          # <file> is a desugared library configuration (json).",
+                  "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
+                  "  --main-dex-list-output <file>",
+                  "                          # Output resulting main dex list in <file>."),
+              ASSERTIONS_USAGE_MESSAGE,
+              Arrays.asList(
+                  "  --version               # Print the version of d8.",
+                  "  --help                  # Print this message.")));
   /**
    * Parse the D8 command-line.
    *
@@ -143,7 +146,7 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static D8Command.Builder parse(String[] args, Origin origin) {
-    return parse(args, origin, D8Command.builder());
+    return new D8CommandParser().parse(args, origin, D8Command.builder());
   }
 
   /**
@@ -157,10 +160,10 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static D8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
-    return parse(args, origin, D8Command.builder(handler));
+    return new D8CommandParser().parse(args, origin, D8Command.builder(handler));
   }
 
-  private static D8Command.Builder parse(String[] args, Origin origin, D8Command.Builder builder) {
+  private D8Command.Builder parse(String[] args, Origin origin, D8Command.Builder builder) {
     CompilationMode compilationMode = null;
     Path outputPath = null;
     OutputMode outputMode = null;
@@ -250,11 +253,12 @@
         builder.setDisableDesugaring(true);
       } else if (arg.equals("--desugared-lib")) {
         builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
-      } else {
-        if (arg.startsWith("--")) {
+      } else if (arg.startsWith("--")) {
+        if (!tryParseAssertionArgument(builder, arg, origin)) {
           builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
           continue;
         }
+      } else {
         builder.addProgramFiles(Paths.get(arg));
       }
     }
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 1ddd338..d190d6c 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -82,6 +81,7 @@
       int minApiLevel,
       Reporter diagnosticsHandler,
       DesugaredLibraryConfiguration libraryConfiguration,
+      List<AssertionsConfiguration> assertionsConfiguration,
       DexItemFactory factory) {
     super(
         inputApp,
@@ -94,7 +94,7 @@
         false,
         false,
         (name, checksum) -> true,
-        ImmutableList.of());
+        assertionsConfiguration);
     this.d8Command = d8Command;
     this.r8Command = r8Command;
     this.libraryConfiguration = libraryConfiguration;
@@ -315,6 +315,7 @@
           getMinApiLevel(),
           getReporter(),
           libraryConfiguration,
+          getAssertionsConfiguration(),
           factory);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index 1dc83c1..bf35b67 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -10,12 +10,13 @@
 import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Set;
 
-public class L8CommandParser extends BaseCompilerCommandParser {
+public class L8CommandParser extends BaseCompilerCommandParser<L8Command, L8Command.Builder> {
 
   private static final Set<String> OPTIONS_WITH_PARAMETER =
       ImmutableSet.of("--output", "--lib", "--min-api", "--desugared-lib");
@@ -32,23 +33,27 @@
   static final String USAGE_MESSAGE =
       String.join(
           "\n",
-          Arrays.asList(
-              "Usage: l8 [options] <input-files>",
-              " where <input-files> are any combination of dex, class, zip, jar, or apk files",
-              " and options are:",
-              "  --debug                 # Compile with debugging information (default).",
-              "  --release               # Compile without debugging information.",
-              "  --output <file>         # Output result in <outfile>.",
-              "                          # <file> must be an existing directory or a zip file.",
-              "  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.",
-              "  --min-api <number>      # Minimum Android API level compatibility, default: "
-                  + AndroidApiLevel.getDefault().getLevel()
-                  + ".",
-              "  --pg-conf <file>        # Proguard configuration <file>.",
-              "  --desugared-lib <file>  # Specify desugared library configuration.",
-              "                          # <file> is a desugared library configuration (json).",
-              "  --version               # Print the version of l8.",
-              "  --help                  # Print this message."));
+          Iterables.concat(
+              Arrays.asList(
+                  "Usage: l8 [options] <input-files>",
+                  " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+                  " and options are:",
+                  "  --debug                 # Compile with debugging information (default).",
+                  "  --release               # Compile without debugging information.",
+                  "  --output <file>         # Output result in <outfile>.",
+                  "                          # <file> must be an existing directory or a zip file.",
+                  "  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.",
+                  "  --min-api <number>      # Minimum Android API level compatibility, default: "
+                      + AndroidApiLevel.getDefault().getLevel()
+                      + ".",
+                  "  --pg-conf <file>        # Proguard configuration <file>.",
+                  "  --desugared-lib <file>  # Specify desugared library configuration.",
+                  "                          # <file> is a desugared library configuration"
+                      + " (json)."),
+              ASSERTIONS_USAGE_MESSAGE,
+              Arrays.asList(
+                  "  --version               # Print the version of l8.",
+                  "  --help                  # Print this message.")));
 
   /**
    * Parse the D8 command-line.
@@ -60,7 +65,7 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static L8Command.Builder parse(String[] args, Origin origin) {
-    return parse(args, origin, L8Command.builder());
+    return new L8CommandParser().parse(args, origin, L8Command.builder());
   }
 
   /**
@@ -74,10 +79,10 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static L8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
-    return parse(args, origin, L8Command.builder(handler));
+    return new L8CommandParser().parse(args, origin, L8Command.builder(handler));
   }
 
-  private static L8Command.Builder parse(String[] args, Origin origin, L8Command.Builder builder) {
+  private L8Command.Builder parse(String[] args, Origin origin, L8Command.Builder builder) {
     CompilationMode compilationMode = null;
     Path outputPath = null;
     OutputMode outputMode = null;
@@ -139,11 +144,12 @@
         builder.addProguardConfigurationFiles(Paths.get(nextArg));
       } else if (arg.equals("--desugared-lib")) {
         builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
-      } else {
-        if (arg.startsWith("--")) {
+      } else if (arg.startsWith("--")) {
+        if (!tryParseAssertionArgument(builder, arg, origin)) {
           builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
           continue;
         }
+      } else {
         builder.addProgramFiles(Paths.get(arg));
       }
     }
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index dd9ab8e..f1541bd 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -8,12 +8,13 @@
 import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Set;
 
-public class R8CommandParser extends BaseCompilerCommandParser {
+public class R8CommandParser extends BaseCompilerCommandParser<R8Command, R8Command.Builder> {
 
   private static final Set<String> OPTIONS_WITH_PARAMETER =
       ImmutableSet.of(
@@ -49,36 +50,40 @@
   static final String USAGE_MESSAGE =
       String.join(
           "\n",
-          Arrays.asList(
-              "Usage: r8 [options] <input-files>",
-              " where <input-files> are any combination of dex, class, zip, jar, or apk files",
-              " and options are:",
-              "  --release                # Compile without debugging information (default).",
-              "  --debug                  # Compile with debugging information.",
-              "  --dex                    # Compile program to DEX file format (default).",
-              "  --classfile              # Compile program to Java classfile format.",
-              "  --output <file>          # Output result in <file>.",
-              "                           # <file> must be an existing directory or a zip file.",
-              "  --lib <file|jdk-home>    # Add <file|jdk-home> as a library resource.",
-              "  --classpath <file>       # Add <file> as a classpath resource.",
-              "  --min-api <number>       # Minimum Android API level compatibility, default: "
-                  + AndroidApiLevel.getDefault().getLevel()
-                  + ".",
-              "  --pg-conf <file>         # Proguard configuration <file>.",
-              "  --pg-map-output <file>   # Output the resulting name and line mapping to <file>.",
-              "  --desugared-lib <file>   # Specify desugared library configuration.",
-              "                           # <file> is a desugared library configuration (json).",
-              "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
-              "  --no-minification        # Force disable minification of names.",
-              "  --no-data-resources      # Ignore all data resources.",
-              "  --no-desugaring          # Force disable desugaring.",
-              "  --main-dex-rules <file>  # Proguard keep rules for classes to place in the",
-              "                           # primary dex file.",
-              "  --main-dex-list <file>   # List of classes to place in the primary dex file.",
-              "  --main-dex-list-output <file>  ",
-              "                           # Output the full main-dex list in <file>.",
-              "  --version                # Print the version of r8.",
-              "  --help                   # Print this message."));
+          Iterables.concat(
+              Arrays.asList(
+                  "Usage: r8 [options] <input-files>",
+                  " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+                  " and options are:",
+                  "  --release               # Compile without debugging information (default).",
+                  "  --debug                 # Compile with debugging information.",
+                  "  --dex                   # Compile program to DEX file format (default).",
+                  "  --classfile             # Compile program to Java classfile format.",
+                  "  --output <file>         # Output result in <file>.",
+                  "                          # <file> must be an existing directory or a zip file.",
+                  "  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.",
+                  "  --classpath <file>      # Add <file> as a classpath resource.",
+                  "  --min-api <number>      # Minimum Android API level compatibility, default: "
+                      + AndroidApiLevel.getDefault().getLevel()
+                      + ".",
+                  "  --pg-conf <file>        # Proguard configuration <file>.",
+                  "  --pg-map-output <file>  # Output the resulting name and line mapping to"
+                      + " <file>.",
+                  "  --desugared-lib <file>  # Specify desugared library configuration.",
+                  "                          # <file> is a desugared library configuration (json).",
+                  "  --no-tree-shaking       # Force disable tree shaking of unreachable classes.",
+                  "  --no-minification       # Force disable minification of names.",
+                  "  --no-data-resources     # Ignore all data resources.",
+                  "  --no-desugaring         # Force disable desugaring.",
+                  "  --main-dex-rules <file> # Proguard keep rules for classes to place in the",
+                  "                          # primary dex file.",
+                  "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
+                  "  --main-dex-list-output <file>  ",
+                  "                          # Output the full main-dex list in <file>."),
+              ASSERTIONS_USAGE_MESSAGE,
+              Arrays.asList(
+                  "  --version               # Print the version of r8.",
+                  "  --help                  # Print this message.")));
   /**
    * Parse the R8 command-line.
    *
@@ -213,10 +218,12 @@
         builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
       } else if (arg.equals("--no-data-resources")) {
         state.includeDataResources = false;
-      } else {
-        if (arg.startsWith("--")) {
+      } else if (arg.startsWith("--")) {
+        if (!tryParseAssertionArgument(builder, arg, argsOrigin)) {
           builder.error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
+          continue;
         }
+      } else {
         builder.addProgramFiles(Paths.get(arg));
       }
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 77545740..815e721 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -250,7 +250,7 @@
       if (function != null) {
         // Found a compatible function that is likely asked to keep.
         builder.add(method);
-      } else if (!method.isKotlinProperty(properties)) {
+      } else if (!method.isKotlinProperty(properties, appView)) {
         // This could be a newly merged method that is not part of properties.
         builder.add(method);
       }
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 a928529..d7dc501 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -137,7 +137,7 @@
   private CompilationState compilationState = CompilationState.NOT_PROCESSED;
   private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.DEFAULT_INSTANCE;
   private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.BOTTOM;
-  private int classFileVersion = -1;
+  private int classFileVersion;
 
   private DexEncodedMethod defaultInterfaceMethodImplementation = null;
 
@@ -151,6 +151,18 @@
   // Any newly added `public` method should check if `this` instance is obsolete.
   private boolean obsolete = false;
 
+  // This flag indicates if the method has been synthesized by D8/R8. Such method do not require
+  // a proguard mapping file entry. This flag is different from the synthesized access flag. When a
+  // non synthesized method is inlined into a synthesized method, the method no longer has the
+  // synthesized access flag, but the d8R8Synthesized flag is still there. Methods can also have
+  // the synthesized access flag prior to D8/R8 compilation, in which case d8R8Synthesized is not
+  // set.
+  private final boolean d8R8Synthesized;
+
+  public boolean isD8R8Synthesized() {
+    return d8R8Synthesized;
+  }
+
   private void checkIfObsolete() {
     assert !obsolete;
   }
@@ -200,26 +212,49 @@
       DexAnnotationSet annotations,
       ParameterAnnotationsList parameterAnnotationsList,
       Code code) {
+    this(method, accessFlags, annotations, parameterAnnotationsList, code, -1);
+  }
+
+  public DexEncodedMethod(
+      DexMethod method,
+      MethodAccessFlags accessFlags,
+      DexAnnotationSet annotations,
+      ParameterAnnotationsList parameterAnnotationsList,
+      Code code,
+      int classFileVersion) {
+    this(method, accessFlags, annotations, parameterAnnotationsList, code, classFileVersion, false);
+  }
+
+  public DexEncodedMethod(
+      DexMethod method,
+      MethodAccessFlags accessFlags,
+      DexAnnotationSet annotations,
+      ParameterAnnotationsList parameterAnnotationsList,
+      Code code,
+      boolean d8R8Synthesized) {
+    this(method, accessFlags, annotations, parameterAnnotationsList, code, -1, d8R8Synthesized);
+  }
+
+  public DexEncodedMethod(
+      DexMethod method,
+      MethodAccessFlags accessFlags,
+      DexAnnotationSet annotations,
+      ParameterAnnotationsList parameterAnnotationsList,
+      Code code,
+      int classFileVersion,
+      boolean d8R8Synthesized) {
     this.method = method;
     this.accessFlags = accessFlags;
     this.annotations = annotations;
     this.parameterAnnotationsList = parameterAnnotationsList;
     this.code = code;
+    this.classFileVersion = classFileVersion;
+    this.d8R8Synthesized = d8R8Synthesized;
+
     assert code == null || !shouldNotHaveCode();
     assert parameterAnnotationsList != null;
   }
 
-  public DexEncodedMethod(
-      DexMethod method,
-      MethodAccessFlags flags,
-      DexAnnotationSet annotationSet,
-      ParameterAnnotationsList annotationsList,
-      Code code,
-      int classFileVersion) {
-    this(method, flags, annotationSet, annotationsList, code);
-    this.classFileVersion = classFileVersion;
-  }
-
   public OptionalBool isLibraryMethodOverride() {
     return isNonPrivateVirtualMethod() ? isLibraryMethodOverride : OptionalBool.FALSE;
   }
@@ -398,35 +433,20 @@
     return null;
   }
 
-  // E.g., property `prop: T` is mapped to `getProp()T`, `setProp(T)V`, `prop$annotations()V`.
-  // TODO(b/70169921): Handle different name patterns via @JvmName.
-  boolean isKotlinProperty(List<KmProperty> properties) {
-    // TODO(b/70169921): Avoid decoding.
-    String methodName = method.name.toString();
-    if (!methodName.startsWith("get")
-        && !methodName.startsWith("set")
-        && !methodName.endsWith("$annotations")) {
-      return false;
+  boolean isKotlinProperty(List<KmProperty> properties, AppView<?> appView) {
+    return findCompatibleKotlinProperty(properties, appView) != null;
+  }
+
+  KmProperty findCompatibleKotlinProperty(List<KmProperty> properties, AppView<?> appView) {
+    if (isStaticMember()) {
+      return null;
     }
     for (KmProperty property : properties) {
-      String propertyName = property.getName();
-      assert propertyName.length() > 0;
-      String annotations = propertyName + "$annotations";
-      if (methodName.equals(annotations)) {
-        return true;
-      }
-      String capitalized =
-          Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
-      String getter = "get" + capitalized;
-      if (methodName.equals(getter)) {
-        return true;
-      }
-      String setter = "set" + capitalized;
-      if (methodName.equals(setter)) {
-        return true;
+      if (KotlinMetadataSynthesizer.isCompatibleProperty(property, this, appView)) {
+        return property;
       }
     }
-    return false;
+    return null;
   }
 
   public boolean isOnlyInlinedIntoNestMembers() {
@@ -924,23 +944,11 @@
     return builder.build();
   }
 
-  public DexEncodedMethod toRenamedMethod(DexString name, DexItemFactory factory) {
-    checkIfObsolete();
-    if (method.name == name) {
-      return this;
-    }
-    DexMethod newMethod = factory.createMethod(method.holder, method.proto, name);
-    Builder builder = builder(this);
-    builder.setMethod(newMethod);
-    setObsolete();
-    return builder.build();
-  }
-
   public DexEncodedMethod toInitializerForwardingBridge(DexClass holder, DexMethod newMethod) {
     assert accessFlags.isPrivate()
         : "Expected to create bridge for private constructor as part of nest-based access"
             + " desugaring";
-    Builder builder = builder(this);
+    Builder builder = syntheticBuilder(this);
     builder.setMethod(newMethod);
     ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
         ForwardMethodSourceCode.builder(newMethod);
@@ -988,7 +996,12 @@
               }
             });
     return new DexEncodedMethod(
-        newMethod, accessFlags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
+        newMethod,
+        accessFlags,
+        DexAnnotationSet.empty(),
+        ParameterAnnotationsList.empty(),
+        code,
+        true);
   }
 
   public DexEncodedMethod toRenamedHolderMethod(DexType newHolderType, DexItemFactory factory) {
@@ -1012,13 +1025,18 @@
                 interfaceType, companionMethod, libraryMethod, extraDispatchCases, appView)
             .generateCfCode();
     return new DexEncodedMethod(
-        newMethod, accessFlags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
+        newMethod,
+        accessFlags,
+        DexAnnotationSet.empty(),
+        ParameterAnnotationsList.empty(),
+        code,
+        true);
   }
 
   public DexEncodedMethod toStaticForwardingBridge(DexClass holder, DexMethod newMethod) {
     assert accessFlags.isPrivate()
         : "Expected to create bridge for private method as part of nest-based access desugaring";
-    Builder builder = builder(this);
+    Builder builder = syntheticBuilder(this);
     builder.setMethod(newMethod);
     ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
         ForwardMethodSourceCode.builder(newMethod);
@@ -1056,7 +1074,7 @@
     DexMethod newMethod =
         definitions.dexItemFactory().createMethod(holder.type, method.proto, method.name);
     Invoke.Type type = accessFlags.isStatic() ? Invoke.Type.STATIC : Invoke.Type.SUPER;
-    Builder builder = builder(this);
+    Builder builder = syntheticBuilder(this);
     builder.setMethod(newMethod);
     if (accessFlags.isAbstract()) {
       // If the forwarding target is abstract, we can just create an abstract method. While it
@@ -1113,7 +1131,8 @@
         newFlags,
         target.annotations,
         target.parameterAnnotationsList,
-        new SynthesizedCode(forwardSourceCodeBuilder::build));
+        new SynthesizedCode(forwardSourceCodeBuilder::build),
+        true);
   }
 
   public DexEncodedMethod toStaticMethodWithoutThis() {
@@ -1247,6 +1266,10 @@
     }
   }
 
+  private static Builder syntheticBuilder(DexEncodedMethod from) {
+    return new Builder(from, true);
+  }
+
   private static Builder builder(DexEncodedMethod from) {
     return new Builder(from);
   }
@@ -1261,8 +1284,13 @@
     private CompilationState compilationState;
     private MethodOptimizationInfo optimizationInfo;
     private final int classFileVersion;
+    private boolean d8R8Synthesized;
 
     private Builder(DexEncodedMethod from) {
+      this(from, from.d8R8Synthesized);
+    }
+
+    private Builder(DexEncodedMethod from, boolean d8R8Synthesized) {
       // Copy all the mutable state of a DexEncodedMethod here.
       method = from.method;
       accessFlags = from.accessFlags.copy();
@@ -1271,6 +1299,7 @@
       compilationState = from.compilationState;
       optimizationInfo = from.optimizationInfo.mutableCopy();
       classFileVersion = from.classFileVersion;
+      this.d8R8Synthesized = d8R8Synthesized;
 
       if (from.parameterAnnotationsList.isEmpty()
           || from.parameterAnnotationsList.size() == method.proto.parameters.size()) {
@@ -1356,7 +1385,13 @@
           || parameterAnnotations.size() == method.proto.parameters.size();
       DexEncodedMethod result =
           new DexEncodedMethod(
-              method, accessFlags, annotations, parameterAnnotations, code, classFileVersion);
+              method,
+              accessFlags,
+              annotations,
+              parameterAnnotations,
+              code,
+              classFileVersion,
+              d8R8Synthesized);
       result.compilationState = compilationState;
       result.optimizationInfo = optimizationInfo;
       return result;
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 1fc834d..09a9055 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -719,10 +719,8 @@
             : "Unable to map field `" + field.field.toSourceString() + "` back to original program";
       }
       for (DexEncodedMethod method : clazz.methods()) {
-        if (method.accessFlags.isSynthetic()) {
-          // This could be a bridge that has been inserted, for example, as a result of member
-          // rebinding. Consider only skipping the check below for methods that have been
-          // synthesized by R8.
+        if (method.isD8R8Synthesized()) {
+          // Methods synthesized by D8/R8 may not be mapped.
           continue;
         }
         DexMethod originalMethod = getOriginalMethodSignature(method.method);
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 d71b495..5203cb9 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
@@ -281,7 +281,7 @@
         }
       }
       for (Phi phi : trivials) {
-        phi.removeTrivialPhi();
+        phi.removeTrivialPhi(null, affectedValues);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index b3a6d66..6ef43a6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -219,7 +219,7 @@
           options.desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
               ? null
               : new InterfaceMethodRewriter(appView, this);
-      this.lambdaRewriter = new LambdaRewriter(appView, this);
+      this.lambdaRewriter = new LambdaRewriter(appView);
       this.desugaredLibraryAPIConverter = new DesugaredLibraryAPIConverter(appView);
       this.twrCloseResourceRewriter = null;
       this.lambdaMerger = null;
@@ -244,7 +244,7 @@
       this.methodOptimizationInfoCollector = null;
       return;
     }
-    this.lambdaRewriter = options.enableDesugaring ? new LambdaRewriter(appView, this) : null;
+    this.lambdaRewriter = options.enableDesugaring ? new LambdaRewriter(appView) : null;
     this.interfaceMethodRewriter =
         options.isInterfaceMethodDesugaringEnabled()
             ? new InterfaceMethodRewriter(appView, this)
@@ -402,8 +402,9 @@
   private void synthesizeLambdaClasses(Builder<?> builder, ExecutorService executorService)
       throws ExecutionException {
     if (lambdaRewriter != null) {
-      lambdaRewriter.adjustAccessibility();
-      lambdaRewriter.synthesizeLambdaClasses(builder, executorService);
+      lambdaRewriter.adjustAccessibility(this);
+      lambdaRewriter.synthesizeLambdaClasses(builder);
+      lambdaRewriter.optimizeSynthesizedClasses(this, executorService);
     }
   }
 
@@ -814,7 +815,7 @@
 
     if (lambdaRewriter != null) {
       lambdaRewriter.synthesizeLambdaClassesForWave(
-          wave, executorService, delayedOptimizationFeedback, lensCodeRewriter);
+          wave, executorService, delayedOptimizationFeedback, lensCodeRewriter, this);
     }
   }
 
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 8f33a5e..8c632a2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -128,12 +128,14 @@
 
       // Due to emulated dispatch, we have to rewrite invoke-super differently or we end up in
       // infinite loops. We do direct resolution. This is a very uncommon case.
-      if (invoke.isInvokeSuper()) {
+      if (invoke.isInvokeSuper()
+          && rewritableMethods.matchesVirtualRewrite(invoke.getInvokedMethod())) {
         DexEncodedMethod dexEncodedMethod =
             appView
                 .appInfo()
                 .lookupSuperTarget(invoke.getInvokedMethod(), code.method.method.holder);
-        if (!dexEncodedMethod.isFinal()) { // Final methods can be rewritten as a normal invoke.
+        // Final methods can be rewritten as a normal invoke.
+        if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) {
           DexMethod retargetMethod =
               appView
                   .options()
@@ -212,7 +214,7 @@
       Code code = provider.generateTemplateMethod(appView.options(), method);
       DexEncodedMethod dexEncodedMethod =
           new DexEncodedMethod(
-              method, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
+              method, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code, true);
       boolean addToMainDexList =
           referencingClasses.stream()
               .anyMatch(clazz -> appView.appInfo().isInMainDexList(clazz.type));
@@ -390,7 +392,7 @@
         factory.createMethod(
             interfaceType, emulatedDispatchMethod.proto, emulatedDispatchMethod.name);
     return new DexEncodedMethod(
-        newMethod, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null);
+        newMethod, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null, true);
   }
 
   private DexEncodedMethod generateHolderDispatchMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 565af2f..0204455 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -365,7 +365,8 @@
             new SynthesizedCode(
                 callerPosition ->
                     new ExceptionThrowingSourceCode(
-                        clazz.type, method, callerPosition, dexItemFactory.icceType)));
+                        clazz.type, method, callerPosition, dexItemFactory.icceType)),
+            true);
     addSyntheticMethod(clazz.asProgramClass(), newEncodedMethod);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index e13f26f..a0fb1c8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -164,7 +164,8 @@
             newAccessFlags,
             method.annotations.keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)),
             method.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()),
-            new SynthesizedCode(forwardSourceCodeBuilder::build));
+            new SynthesizedCode(forwardSourceCodeBuilder::build),
+            true);
     // Optimize to generate DexCode instead of SynthesizedCode.
     converter.optimizeSynthesizedMethod(newVirtualMethod);
     return newVirtualMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index e3e7ab0..bf48a16 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -403,7 +403,8 @@
         newFlags,
         DexAnnotationSet.empty(),
         ParameterAnnotationsList.empty(),
-        code);
+        code,
+        true);
   }
 
   private List<DexEncodedMethod> allImplementedMethods(DexClass libraryClass) {
@@ -474,7 +475,8 @@
         accessFlags,
         DexAnnotationSet.empty(),
         ParameterAnnotationsList.empty(),
-        code);
+        code,
+        true);
   }
 
   // Wrapper finalization section.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 9599834..ccb0f3c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -94,7 +94,7 @@
         DexEncodedMethod.setDebugInfoWithFakeThisParameter(
             code, companionMethod.getArity(), appView);
         DexEncodedMethod implMethod = new DexEncodedMethod(
-            companionMethod, newFlags, virtual.annotations, virtual.parameterAnnotationsList, code);
+            companionMethod, newFlags, virtual.annotations, virtual.parameterAnnotationsList, code, true);
         virtual.setDefaultInterfaceMethodImplementation(implMethod);
         companionMethods.add(implMethod);
         graphLensBuilder.move(virtual.method, implMethod.method);
@@ -128,7 +128,7 @@
             + "either be public or private in " + iface.origin;
         DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(oldMethod);
         companionMethods.add(new DexEncodedMethod(companionMethod, newFlags,
-            direct.annotations, direct.parameterAnnotationsList, direct.getCode()));
+            direct.annotations, direct.parameterAnnotationsList, direct.getCode(),true));
         graphLensBuilder.move(oldMethod, companionMethod);
       } else {
         if (originalFlags.isPrivate()) {
@@ -147,7 +147,7 @@
           DexEncodedMethod.setDebugInfoWithFakeThisParameter(
               code, companionMethod.getArity(), appView);
           companionMethods.add(new DexEncodedMethod(companionMethod,
-              newFlags, direct.annotations, direct.parameterAnnotationsList, code));
+              newFlags, direct.annotations, direct.parameterAnnotationsList, code,true));
           graphLensBuilder.move(oldMethod, companionMethod);
         } else {
           // Since there are no interface constructors at this point,
@@ -247,7 +247,8 @@
                   Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false),
               DexAnnotationSet.empty(),
               ParameterAnnotationsList.empty(),
-              new SynthesizedCode(forwardSourceCodeBuilder::build));
+              new SynthesizedCode(forwardSourceCodeBuilder::build),
+              true);
       newEncodedMethod.getMutableOptimizationInfo().markNeverInline();
       dispatchMethods.add(newEncodedMethod);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index ce77849..18edf5f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.utils.InternalOptions;
@@ -83,7 +84,7 @@
     this.type = lambdaClassType;
     this.descriptor = descriptor;
 
-    DexItemFactory factory = rewriter.factory;
+    DexItemFactory factory = rewriter.getFactory();
     DexProto constructorProto = factory.createProto(
         factory.voidType, descriptor.captures.values);
     this.constructor = factory.createMethod(
@@ -103,6 +104,11 @@
   // Generate unique lambda class type for lambda descriptor and instantiation point context.
   static DexType createLambdaClassType(
       LambdaRewriter rewriter, DexType accessedFrom, LambdaDescriptor match) {
+    return createLambdaClassType(rewriter.getFactory(), accessedFrom, match);
+  }
+
+  public static DexType createLambdaClassType(
+      DexItemFactory factory, DexType accessedFrom, LambdaDescriptor match) {
     StringBuilder lambdaClassDescriptor = new StringBuilder("L");
 
     // We always create lambda class in the same package where it is referenced.
@@ -123,7 +129,7 @@
 
     // Add unique lambda descriptor id
     lambdaClassDescriptor.append(match.uniqueId).append(';');
-    return rewriter.factory.createType(lambdaClassDescriptor.toString());
+    return factory.createType(lambdaClassDescriptor.toString());
   }
 
   final DexProgramClass getOrCreateLambdaClass() {
@@ -132,7 +138,7 @@
 
   private DexProgramClass synthesizeLambdaClass() {
     DexMethod mainMethod =
-        rewriter.factory.createMethod(type, descriptor.erasedProto, descriptor.name);
+        rewriter.getFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
 
     DexProgramClass clazz =
         new DexProgramClass(
@@ -143,9 +149,9 @@
             // classloader (package private access is not allowed across classloaders, b/72538146).
             ClassAccessFlags.fromDexAccessFlags(
                 Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
-            rewriter.factory.objectType,
+            rewriter.getFactory().objectType,
             buildInterfaces(),
-            rewriter.factory.createString("lambda"),
+            rewriter.getFactory().createString("lambda"),
             null,
             Collections.emptyList(),
             null,
@@ -155,9 +161,9 @@
             synthesizeInstanceFields(),
             synthesizeDirectMethods(),
             synthesizeVirtualMethods(mainMethod),
-            rewriter.factory.getSkipNameValidationForTesting(),
+            rewriter.getFactory().getSkipNameValidationForTesting(),
             LambdaClass::computeChecksumForSynthesizedClass);
-    rewriter.converter.appView.appInfo().addSynthesizedClass(clazz);
+    rewriter.getAppInfo().addSynthesizedClass(clazz);
 
     // The method addSynthesizedFrom() may be called concurrently. To avoid a Concurrent-
     // ModificationException we must use synchronization.
@@ -186,8 +192,12 @@
   }
 
   final DexField getCaptureField(int index) {
-    return rewriter.factory.createField(this.type,
-        descriptor.captures.values[index], rewriter.factory.createString("f$" + index));
+    return rewriter
+        .getFactory()
+        .createField(
+            this.type,
+            descriptor.captures.values[index],
+            rewriter.getFactory().createString("f$" + index));
   }
 
   final boolean isStateless() {
@@ -218,11 +228,13 @@
                 Constants.ACC_PUBLIC | Constants.ACC_FINAL, false),
             DexAnnotationSet.empty(),
             ParameterAnnotationsList.empty(),
-            new LambdaMainMethodSynthesizedCode(this, mainMethod));
+            new LambdaMainMethodSynthesizedCode(this, mainMethod),
+            true);
 
     // Synthesize bridge methods.
     for (DexProto bridgeProto : descriptor.bridges) {
-      DexMethod bridgeMethod = rewriter.factory.createMethod(type, bridgeProto, descriptor.name);
+      DexMethod bridgeMethod =
+          rewriter.getFactory().createMethod(type, bridgeProto, descriptor.name);
       methods[index++] =
           new DexEncodedMethod(
               bridgeMethod,
@@ -234,7 +246,8 @@
                   false),
               DexAnnotationSet.empty(),
               ParameterAnnotationsList.empty(),
-              new LambdaBridgeMethodSynthesizedCode(this, mainMethod, bridgeMethod));
+              new LambdaBridgeMethodSynthesizedCode(this, mainMethod, bridgeMethod),
+              true);
     }
     return methods;
   }
@@ -254,7 +267,8 @@
                 true),
             DexAnnotationSet.empty(),
             ParameterAnnotationsList.empty(),
-            new LambdaConstructorSynthesizedCode(this));
+            new LambdaConstructorSynthesizedCode(this),
+            true);
 
     // Class constructor for stateless lambda classes.
     if (stateless) {
@@ -265,7 +279,8 @@
                   Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true),
               DexAnnotationSet.empty(),
               ParameterAnnotationsList.empty(),
-              new LambdaClassConstructorSynthesizedCode(this));
+              new LambdaClassConstructorSynthesizedCode(this),
+              true);
     }
     return methods;
   }
@@ -349,10 +364,6 @@
     assert implMethod.holder == accessedFrom;
     assert descriptor.targetFoundInClass(accessedFrom);
     assert descriptor.getAccessibility() != null;
-    // When coming from javac these are also private, but we don't assert that, as the
-    // accessibility could have been modified (e.g. due to -allowaccessmodification).
-
-    assert descriptor.getAccessibility().isSynthetic();
 
     if (implHandle.type.isInvokeStatic()) {
       return new StaticLambdaImplTarget();
@@ -363,26 +374,30 @@
     // If the lambda$ method is an instance-private method on an interface we convert it into a
     // public static method as it will be placed on the companion class.
     if (implHandle.type.isInvokeDirect()
-        && rewriter.converter.appView.definitionFor(implMethod.holder).isInterface()) {
+        && rewriter.getAppView().definitionFor(implMethod.holder).isInterface()) {
       DexProto implProto = implMethod.proto;
       DexType[] implParams = implProto.parameters.values;
       DexType[] newParams = new DexType[implParams.length + 1];
       newParams[0] = implMethod.holder;
       System.arraycopy(implParams, 0, newParams, 1, implParams.length);
 
-      DexProto newProto = rewriter.factory.createProto(implProto.returnType, newParams);
+      DexProto newProto = rewriter.getFactory().createProto(implProto.returnType, newParams);
       return new InterfaceLambdaImplTarget(
-          rewriter.factory.createMethod(implMethod.holder, newProto, implMethod.name));
+          rewriter.getFactory().createMethod(implMethod.holder, newProto, implMethod.name));
     } else {
       // Otherwise we need to ensure the method can be reached publicly by virtual dispatch.
       // To avoid potential conflicts on the name of the lambda method once dispatch becomes virtual
       // we add the method-holder name as suffix to the lambda-method name.
       return new InstanceLambdaImplTarget(
-          rewriter.factory.createMethod(
-              implMethod.holder,
-              implMethod.proto,
-              rewriter.factory.createString(
-                  implMethod.name.toString() + "$" + implMethod.holder.getName())));
+          rewriter
+              .getFactory()
+              .createMethod(
+                  implMethod.holder,
+                  implMethod.proto,
+                  rewriter
+                      .getFactory()
+                      .createString(
+                          implMethod.name.toString() + "$" + implMethod.holder.getName())));
     }
   }
 
@@ -408,9 +423,12 @@
     DexType[] accessorParams = new DexType[1 + implParams.length];
     accessorParams[0] = descriptor.getImplReceiverType();
     System.arraycopy(implParams, 0, accessorParams, 1, implParams.length);
-    DexProto accessorProto = rewriter.factory.createProto(implProto.returnType, accessorParams);
-    DexMethod accessorMethod = rewriter.factory.createMethod(
-        accessedFrom, accessorProto, generateUniqueLambdaMethodName());
+    DexProto accessorProto =
+        rewriter.getFactory().createProto(implProto.returnType, accessorParams);
+    DexMethod accessorMethod =
+        rewriter
+            .getFactory()
+            .createMethod(accessedFrom, accessorProto, generateUniqueLambdaMethodName());
 
     return new ClassMethodWithAccessorTarget(accessorMethod);
   }
@@ -427,8 +445,13 @@
     // We need to generate an accessor method in `accessedFrom` class/interface
     // for accessing the original static impl-method. The accessor method will be
     // static, package private with exactly same signature and the original method.
-    DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom,
-        descriptor.implHandle.asMethod().proto, generateUniqueLambdaMethodName());
+    DexMethod accessorMethod =
+        rewriter
+            .getFactory()
+            .createMethod(
+                accessedFrom,
+                descriptor.implHandle.asMethod().proto,
+                generateUniqueLambdaMethodName());
     return new ClassMethodWithAccessorTarget(accessorMethod);
   }
 
@@ -449,10 +472,12 @@
     // and return the newly created instance.
     DexMethod implMethod = implHandle.asMethod();
     DexType returnType = implMethod.holder;
-    DexProto accessorProto = rewriter.factory.createProto(
-        returnType, implMethod.proto.parameters.values);
-    DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom,
-        accessorProto, generateUniqueLambdaMethodName());
+    DexProto accessorProto =
+        rewriter.getFactory().createProto(returnType, implMethod.proto.parameters.values);
+    DexMethod accessorMethod =
+        rewriter
+            .getFactory()
+            .createMethod(accessedFrom, accessorProto, generateUniqueLambdaMethodName());
     return new ClassMethodWithAccessorTarget(accessorMethod);
   }
 
@@ -464,8 +489,9 @@
   }
 
   private DexString generateUniqueLambdaMethodName() {
-    return rewriter.factory.createString(
-        LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId);
+    return rewriter
+        .getFactory()
+        .createString(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId);
   }
 
   // Represents information about the method lambda class need to delegate the call to. It may
@@ -484,18 +510,18 @@
     }
 
     // Ensure access of the referenced symbol(s).
-    abstract void ensureAccessibility();
+    abstract void ensureAccessibility(IRConverter converter);
 
     DexClass definitionFor(DexType type) {
-      return rewriter.converter.appView.appInfo().app().definitionFor(type);
+      return rewriter.getAppInfo().app().definitionFor(type);
     }
 
     DexProgramClass programDefinitionFor(DexType type) {
-      return rewriter.converter.appView.appInfo().app().programDefinitionFor(type);
+      return rewriter.getAppInfo().app().programDefinitionFor(type);
     }
 
     boolean holderIsInterface() {
-      InternalOptions options = rewriter.converter.appView.options();
+      InternalOptions options = rewriter.getAppView().options();
       if (!options.isGeneratingClassFiles()) {
         // When generating dex the value of this flag on invokes does not matter (unused).
         // We cannot know if definitionFor(implMethod.holder) is null or not in that case,
@@ -527,7 +553,7 @@
     }
 
     @Override
-    void ensureAccessibility() {}
+    void ensureAccessibility(IRConverter converter) {}
   }
 
   // Used for static private lambda$ methods. Only needs access relaxation.
@@ -538,7 +564,7 @@
     }
 
     @Override
-    void ensureAccessibility() {
+    void ensureAccessibility(IRConverter converter) {
       // We already found the static method to be called, just relax its accessibility.
       assert descriptor.getAccessibility() != null;
       descriptor.getAccessibility().unsetPrivate();
@@ -558,7 +584,7 @@
     }
 
     @Override
-    void ensureAccessibility() {
+    void ensureAccessibility(IRConverter converter) {
       // For all instantiation points for which the compiler creates lambda$
       // methods, it creates these methods in the same class/interface.
       DexMethod implMethod = descriptor.implHandle.asMethod();
@@ -582,12 +608,13 @@
                   newAccessFlags,
                   encodedMethod.annotations,
                   encodedMethod.parameterAnnotationsList,
-                  encodedMethod.getCode());
+                  encodedMethod.getCode(),
+                  true);
           newMethod.copyMetadata(encodedMethod);
           rewriter.methodMapping.put(encodedMethod.method, callTarget);
 
           DexEncodedMethod.setDebugInfoWithFakeThisParameter(
-              newMethod.getCode(), callTarget.getArity(), rewriter.converter.appView);
+              newMethod.getCode(), callTarget.getArity(), rewriter.getAppView());
           implMethodHolder.setDirectMethod(i, newMethod);
           return;
         }
@@ -605,7 +632,7 @@
     }
 
     @Override
-    void ensureAccessibility() {
+    void ensureAccessibility(IRConverter converter) {
       // For all instantiation points for which the compiler creates lambda$
       // methods, it creates these methods in the same class/interface.
       DexMethod implMethod = descriptor.implHandle.asMethod();
@@ -626,7 +653,8 @@
                   newAccessFlags,
                   encodedMethod.annotations,
                   encodedMethod.parameterAnnotationsList,
-                  encodedMethod.getCode());
+                  encodedMethod.getCode(),
+                  true);
           newMethod.copyMetadata(encodedMethod);
           rewriter.methodMapping.put(encodedMethod.method, callTarget);
           // Move the method from the direct methods to the virtual methods set.
@@ -647,7 +675,7 @@
     }
 
     @Override
-    void ensureAccessibility() {
+    void ensureAccessibility(IRConverter converter) {
       // Create a static accessor with proper accessibility.
       DexProgramClass accessorClass = programDefinitionFor(callTarget.holder);
       assert accessorClass != null;
@@ -666,14 +694,15 @@
               ParameterAnnotationsList.empty(),
               new SynthesizedCode(
                   callerPosition ->
-                      new AccessorMethodSourceCode(LambdaClass.this, callerPosition)));
+                      new AccessorMethodSourceCode(LambdaClass.this, callerPosition)),
+              true);
 
       // We may arrive here concurrently so we need must update the methods of the class atomically.
       synchronized (accessorClass) {
         accessorClass.appendDirectMethod(accessorEncodedMethod);
       }
 
-      rewriter.converter.optimizeSynthesizedMethod(accessorEncodedMethod);
+      converter.optimizeSynthesizedMethod(accessorEncodedMethod);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index ed49900..92d04f0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -66,8 +66,6 @@
   private static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
 
   private final AppView<?> appView;
-  final IRConverter converter;
-  final DexItemFactory factory;
 
   final DexMethod objectInitMethod;
 
@@ -93,24 +91,34 @@
     return clazz.getName().startsWith(LAMBDA_CLASS_NAME_PREFIX);
   }
 
-  public LambdaRewriter(AppView<?> appView, IRConverter converter) {
-    assert converter != null;
+  public LambdaRewriter(AppView<?> appView) {
     this.appView = appView;
-    this.converter = converter;
-    this.factory = appView.dexItemFactory();
+    this.constructorName = getFactory().createString(Constants.INSTANCE_INITIALIZER_NAME);
+    DexProto initProto = getFactory().createProto(getFactory().voidType);
+    this.objectInitMethod =
+        getFactory().createMethod(getFactory().objectType, initProto, constructorName);
+    this.classConstructorName = getFactory().createString(Constants.CLASS_INITIALIZER_NAME);
+    this.instanceFieldName = getFactory().createString(LAMBDA_INSTANCE_FIELD_NAME);
+  }
 
-    this.constructorName = factory.createString(Constants.INSTANCE_INITIALIZER_NAME);
-    DexProto initProto = factory.createProto(factory.voidType);
-    this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
-    this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
-    this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
+  public AppView<?> getAppView() {
+    return appView;
+  }
+
+  public AppInfo getAppInfo() {
+    return getAppView().appInfo();
+  }
+
+  public DexItemFactory getFactory() {
+    return getAppView().dexItemFactory();
   }
 
   public void synthesizeLambdaClassesForWave(
       Collection<DexEncodedMethod> wave,
       ExecutorService executorService,
       OptimizationFeedbackDelayed feedback,
-      LensCodeRewriter lensCodeRewriter)
+      LensCodeRewriter lensCodeRewriter,
+      IRConverter converter)
       throws ExecutionException {
     Set<DexProgramClass> synthesizedLambdaClasses = Sets.newIdentityHashSet();
     for (DexEncodedMethod method : wave) {
@@ -133,7 +141,7 @@
       }
     }
 
-    AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+    AppView<AppInfoWithLiveness> appViewWithLiveness = getAppView().withLiveness();
     appViewWithLiveness.setAppInfo(
         appViewWithLiveness.appInfo().withStaticFieldWrites(writesWithContexts));
 
@@ -160,7 +168,7 @@
     // application (and, in particular, the class hierarchy) during wave processing.
     code.registerCodeReferences(
         method,
-        new DefaultUseRegistry(appView.dexItemFactory()) {
+        new DefaultUseRegistry(getFactory()) {
 
           @Override
           public void registerCallSite(DexCallSite callSite) {
@@ -199,7 +207,7 @@
           // We have a descriptor, get the lambda class. In D8, we synthesize the lambda classes
           // during IR processing, and therefore we may need to create it now.
           LambdaClass lambdaClass =
-              appView.enableWholeProgramOptimizations()
+              getAppView().enableWholeProgramOptimizations()
                   ? getKnownLambdaClass(descriptor, currentType)
                   : getOrCreateLambdaClass(descriptor, currentType);
           assert lambdaClass != null;
@@ -212,7 +220,7 @@
       }
     }
     if (!affectedValues.isEmpty()) {
-      new TypeAnalysis(appView).narrowing(affectedValues);
+      new TypeAnalysis(getAppView()).narrowing(affectedValues);
     }
     assert code.isConsistentSSA();
   }
@@ -227,7 +235,7 @@
         for (int i = 0; i < methodCount; i++) {
           DexEncodedMethod encoded = directMethods.get(i);
           DexMethod method = encoded.method;
-          if (method.isLambdaDeserializeMethod(appView.dexItemFactory())) {
+          if (method.isLambdaDeserializeMethod(getFactory())) {
             assert encoded.accessFlags.isStatic();
             assert encoded.accessFlags.isSynthetic();
             clazz.removeDirectMethod(i);
@@ -242,17 +250,18 @@
   }
 
   /** Adjust accessibility of referenced application symbols or creates necessary accessors. */
-  public void adjustAccessibility() {
+  public void adjustAccessibility(IRConverter converter) {
     // For each lambda class perform necessary adjustment of the
     // referenced symbols to make them accessible. This can result in
     // method access relaxation or creation of accessor method.
     for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
       // This call may cause methodMapping to be updated.
-      lambdaClass.target.ensureAccessibility();
+      lambdaClass.target.ensureAccessibility(converter);
     }
-    if (appView.enableWholeProgramOptimizations() && !methodMapping.isEmpty()) {
-      appView.setGraphLense(
-          new LambdaRewriterGraphLense(methodMapping, appView.graphLense(), factory));
+    if (getAppView().enableWholeProgramOptimizations() && !methodMapping.isEmpty()) {
+      getAppView()
+          .setGraphLense(
+              new LambdaRewriterGraphLense(methodMapping, getAppView().graphLense(), getFactory()));
     }
   }
 
@@ -266,14 +275,16 @@
   }
 
   /** Generates lambda classes and adds them to the builder. */
-  public void synthesizeLambdaClasses(Builder<?> builder, ExecutorService executorService)
-      throws ExecutionException {
-    AppInfo appInfo = appView.appInfo();
+  public void synthesizeLambdaClasses(Builder<?> builder) {
     for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
       DexProgramClass synthesizedClass = lambdaClass.getOrCreateLambdaClass();
-      appInfo.addSynthesizedClass(synthesizedClass);
+      getAppInfo().addSynthesizedClass(synthesizedClass);
       builder.addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
     }
+  }
+
+  public void optimizeSynthesizedClasses(IRConverter converter, ExecutorService executorService)
+      throws ExecutionException {
     converter.optimizeSynthesizedClasses(
         knownLambdaClasses.values().stream()
             .map(LambdaClass::getOrCreateLambdaClass)
@@ -299,12 +310,11 @@
     LambdaDescriptor descriptor = getKnown(knownCallSites, callSite);
     return descriptor != null
         ? descriptor
-        : putIfAbsent(
-            knownCallSites, callSite, LambdaDescriptor.infer(callSite, appView.appInfo()));
+        : putIfAbsent(knownCallSites, callSite, LambdaDescriptor.infer(callSite, getAppInfo()));
   }
 
   private boolean isInMainDexList(DexType type) {
-    return appView.appInfo().isInMainDexList(type);
+    return getAppInfo().isInMainDexList(type);
   }
 
   // Returns a lambda class corresponding to the lambda descriptor and context,
@@ -319,11 +329,11 @@
               knownLambdaClasses,
               lambdaClassType,
               new LambdaClass(this, accessedFrom, lambdaClassType, descriptor));
-      if (appView.options().isDesugaredLibraryCompilation()) {
-        DexType rewrittenType = appView.rewritePrefix.rewrittenType(accessedFrom);
+      if (getAppView().options().isDesugaredLibraryCompilation()) {
+        DexType rewrittenType = getAppView().rewritePrefix.rewrittenType(accessedFrom);
         if (rewrittenType == null) {
           rewrittenType =
-              appView
+              getAppView()
                   .options()
                   .desugaredLibraryConfiguration
                   .getEmulateLibraryInterface()
@@ -334,7 +344,7 @@
         }
       }
     }
-    lambdaClass.addSynthesizedFrom(appView.definitionFor(accessedFrom).asProgramClass());
+    lambdaClass.addSynthesizedFrom(getAppView().definitionFor(accessedFrom).asProgramClass());
     if (isInMainDexList(accessedFrom)) {
       lambdaClass.addToMainDexList.set(true);
     }
@@ -353,11 +363,14 @@
     String rewrittenString = rewritten.toString();
     String actualRewrittenPrefix = rewrittenString.substring(0, rewrittenString.lastIndexOf('.'));
     assert javaName.startsWith(actualPrefix);
-    appView.rewritePrefix.rewriteType(
-        lambdaClassType,
-        factory.createType(
-            DescriptorUtils.javaTypeToDescriptor(
-                actualRewrittenPrefix + javaName.substring(actualPrefix.length()))));
+    getAppView()
+        .rewritePrefix
+        .rewriteType(
+            lambdaClassType,
+            getFactory()
+                .createType(
+                    DescriptorUtils.javaTypeToDescriptor(
+                        actualRewrittenPrefix + javaName.substring(actualPrefix.length()))));
   }
 
   private static <K, V> V getKnown(Map<K, V> map, K key) {
@@ -397,7 +410,8 @@
       // The out value might be empty in case it was optimized out.
       lambdaInstanceValue =
           code.createValue(
-              TypeLatticeElement.fromDexType(lambdaClass.type, Nullability.maybeNull(), appView));
+              TypeLatticeElement.fromDexType(
+                  lambdaClass.type, Nullability.maybeNull(), getAppView()));
     } else {
       affectedValues.add(lambdaInstanceValue);
     }
@@ -450,6 +464,6 @@
     BasicBlock currentBlock = newInstance.getBlock();
     BasicBlock nextBlock = instructions.split(code, blocks);
     assert !instructions.hasNext();
-    nextBlock.copyCatchHandlers(code, blocks, currentBlock, appView.options());
+    nextBlock.copyCatchHandlers(code, blocks, currentBlock, getAppView().options());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
index 2565687..7e50b88 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
@@ -21,7 +21,7 @@
   }
 
   final DexItemFactory dexItemFactory() {
-    return lambda.rewriter.factory;
+    return lambda.rewriter.getFactory();
   }
 
   final LambdaDescriptor descriptor() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index cf66050..4c2a36d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -40,7 +40,7 @@
 public abstract class NestBasedAccessDesugaring {
 
   // Short names to avoid creating long strings
-  private static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
+  public static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
   private static final String NEST_ACCESS_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "m";
   private static final String NEST_ACCESS_STATIC_METHOD_NAME_PREFIX =
       NEST_ACCESS_NAME_PREFIX + "sm";
@@ -69,6 +69,7 @@
   }
 
   DexType getNestConstructorType() {
+    assert nestConstructor != null;
     return nestConstructor.type;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
index 786b23c..7677cee 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
@@ -40,7 +40,7 @@
   }
 
   final DexItemFactory factory() {
-    return lambda.rewriter.factory;
+    return lambda.rewriter.getFactory();
   }
 
   final int enforceParameterType(int register, DexType paramType, DexType enforcedType) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 817fdb2..17250cf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -134,7 +134,7 @@
     MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(
         Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
     DexEncodedMethod method = new DexEncodedMethod(twrCloseResourceMethod,
-        flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
+        flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code, true);
 
     // Create utility class.
     DexProgramClass utilityClass =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 6de35d0..ab3274d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -971,12 +971,13 @@
           classInitializationAnalysis.notifyCodeHasChanged();
           strategy.updateTypeInformationIfNeeded(inlinee.code, blockIterator, block);
 
-          // TODO(b/146114533): Fix inlining in synthetic methods.
-          // If we inlined the invoke from a bridge method, it is no longer a bridge method.
-          if (context.accessFlags.isBridge()) {
-            context.accessFlags.unsetSynthetic();
+          // The synthetic and bridge flags are maintained only if the inlinee has also these flags.
+          if (context.accessFlags.isBridge() && !inlinee.code.method.accessFlags.isBridge()) {
             context.accessFlags.unsetBridge();
           }
+          if (context.accessFlags.isSynthetic() && !inlinee.code.method.accessFlags.isSynthetic()) {
+            context.accessFlags.unsetSynthetic();
+          }
 
           context.copyMetadata(singleTarget);
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 2ac3abd..e17e419 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1343,7 +1343,8 @@
               methodAccess,
               DexAnnotationSet.empty(),
               ParameterAnnotationsList.empty(),
-              new OutlineCode(outline));
+              new OutlineCode(outline),
+              true);
       if (appView.options().isGeneratingClassFiles()) {
         direct[count].upgradeClassFileVersion(sites.get(0).getClassFileVersion());
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index 11f0a08..fa41f210d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -187,6 +187,14 @@
     if (baseClazz == null || !baseClazz.isResolvable(appView)) {
       return null;
     }
+
+    // Don't allow the instantiated class to be in a feature, if it is, we can get a
+    // NoClassDefFoundError from dalvik/art.
+    if (baseClazz.isProgramClass()
+        && appView.options().featureSplitConfiguration != null
+        && appView.options().featureSplitConfiguration.isInFeature(baseClazz.asProgramClass())) {
+      return null;
+    }
     // Make sure the (base) type is visible.
     ConstraintWithTarget constraints =
         ConstraintWithTarget.classIsVisible(context, baseType, appView);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 29ad438..856baa9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -225,7 +225,8 @@
             methodAccess,
             DexAnnotationSet.empty(),
             ParameterAnnotationsList.empty(),
-            ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()));
+            ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()),
+            true);
     synthesizedClass.addDirectMethod(encodedMethod);
     return encodedMethod;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index 74a5236..6bc95f2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -127,7 +127,8 @@
                             method,
                             group.getLambdaIdField(factory),
                             implMethods,
-                            callerPosition))));
+                            callerPosition)),
+                true));
       }
     }
 
@@ -177,7 +178,8 @@
             new SynthesizedCode(
                 callerPosition ->
                     createInstanceInitializerSourceCode(
-                        groupClassType, initializerMethod, callerPosition)));
+                        groupClassType, initializerMethod, callerPosition)),
+            true);
 
     // Static class initializer for stateless lambdas.
     if (needsSingletonInstances) {
@@ -194,7 +196,8 @@
               ParameterAnnotationsList.empty(),
               new SynthesizedCode(
                   callerPosition ->
-                      new ClassInitializerSourceCode(method, factory, group, callerPosition)));
+                      new ClassInitializerSourceCode(method, factory, group, callerPosition)),
+              true);
     }
 
     return result;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index 06db71b..f4503ce 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -72,6 +72,9 @@
       superTypes.add(toKmType(addKotlinPrefix("Any;")));
     }
 
+    if (!appView.options().enableKotlinMetadataRewriting) {
+      return;
+    }
     List<KmConstructor> constructors = kmClass.getConstructors();
     List<KmConstructor> originalConstructors = new ArrayList<>(constructors);
     constructors.clear();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
index 68b607df..4306645 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -44,6 +44,9 @@
 
   @Override
   void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+    if (!appView.options().enableKotlinMetadataRewriting) {
+      return;
+    }
     List<KmFunction> functions = kmPackage.getFunctions();
     List<KmFunction> originalExtensions =
         functions.stream()
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
index e0a3935..71538fc 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -44,6 +44,9 @@
 
   @Override
   void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+    if (!appView.options().enableKotlinMetadataRewriting) {
+      return;
+    }
     List<KmFunction> functions = kmPackage.getFunctions();
     List<KmFunction> originalExtensions =
         functions.stream()
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
new file mode 100644
index 0000000..85e3540
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
@@ -0,0 +1,178 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.List;
+import kotlinx.metadata.KmConstructor;
+import kotlinx.metadata.KmConstructorExtensionVisitor;
+import kotlinx.metadata.KmConstructorVisitor;
+import kotlinx.metadata.KmExtensionType;
+import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmFunctionExtensionVisitor;
+import kotlinx.metadata.KmFunctionVisitor;
+import kotlinx.metadata.KmProperty;
+import kotlinx.metadata.KmPropertyExtensionVisitor;
+import kotlinx.metadata.KmPropertyVisitor;
+import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor;
+import kotlinx.metadata.jvm.JvmFieldSignature;
+import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
+
+class KotlinMetadataJvmExtensionUtils {
+
+  private static boolean isValidJvmMethodSignature(String desc) {
+    return desc != null
+        && !desc.isEmpty()
+        && desc.charAt(0) == '('
+        && desc.lastIndexOf('(') == 0
+        && desc.indexOf(')') != -1
+        && desc.indexOf(')') == desc.lastIndexOf(')')
+        && desc.lastIndexOf(')') < desc.length();
+  }
+
+  /**
+   * Extract return type from {@link JvmMethodSignature}.
+   *
+   * Example of JVM signature is: `JvmMethodSignature("getX", "()Ljava/lang/Object;").`
+   * In this case, the return type is "Ljava/lang/Object;".
+   */
+  static String returnTypeFromJvmMethodSignature(JvmMethodSignature signature) {
+    if (signature == null) {
+      return null;
+    }
+    String desc = signature.getDesc();
+    if (!isValidJvmMethodSignature(desc)) {
+      return null;
+    }
+    int index = desc.lastIndexOf(')');
+    assert desc.charAt(0) == '(' && 0 < index && index < desc.length() : signature.asString();
+    return desc.substring(index + 1);
+  }
+
+  /**
+   * Extract parameters from {@link JvmMethodSignature}.
+   *
+   * Example of JVM signature is: `JvmMethodSignature("setX", "(Ljava/lang/Object;)V").`
+   * In this case, the parameter is the list with "Ljava/lang/Object;" as the first element.
+   */
+  static List<String> parameterTypesFromJvmMethodSignature(JvmMethodSignature signature) {
+    if (signature == null) {
+      return null;
+    }
+    String desc = signature.getDesc();
+    if (!isValidJvmMethodSignature(desc)) {
+      return null;
+    }
+    int index = desc.lastIndexOf(')');
+    assert desc.charAt(0) == '(' && 0 < index && index < desc.length() : signature.asString();
+    String params = desc.substring(1, index);
+    if (params.isEmpty()) {
+      return ImmutableList.of();
+    } else {
+      return Arrays.asList(params.split(","));
+    }
+  }
+
+  static class KmConstructorProcessor {
+    private JvmMethodSignature signature = null;
+
+    KmConstructorProcessor(KmConstructor kmConstructor) {
+      kmConstructor.accept(new KmConstructorVisitor() {
+        @Override
+        public KmConstructorExtensionVisitor visitExtensions(KmExtensionType type) {
+          if (type != JvmConstructorExtensionVisitor.TYPE) {
+            return null;
+          }
+          return new JvmConstructorExtensionVisitor() {
+            @Override
+            public void visit(JvmMethodSignature desc) {
+              assert signature == null : signature.asString();
+              signature = desc;
+            }
+          };
+        }
+      });
+    }
+
+    JvmMethodSignature signature() {
+      return signature;
+    }
+  }
+
+  static class KmFunctionProcessor {
+    // Custom name via @JvmName("..."). Otherwise, null.
+    private JvmMethodSignature signature = null;
+
+    KmFunctionProcessor(KmFunction kmFunction) {
+      kmFunction.accept(new KmFunctionVisitor() {
+        @Override
+        public KmFunctionExtensionVisitor visitExtensions(KmExtensionType type) {
+          if (type != JvmFunctionExtensionVisitor.TYPE) {
+            return null;
+          }
+          return new JvmFunctionExtensionVisitor() {
+            @Override
+            public void visit(JvmMethodSignature desc) {
+              assert signature == null : signature.asString();
+              signature = desc;
+            }
+          };
+        }
+      });
+    }
+
+    JvmMethodSignature signature() {
+      return signature;
+    }
+  }
+
+  static class KmPropertyProcessor {
+    private JvmFieldSignature fieldSignature = null;
+    // Custom getter via @get:JvmName("..."). Otherwise, null.
+    private JvmMethodSignature getterSignature = null;
+    // Custom getter via @set:JvmName("..."). Otherwise, null.
+    private JvmMethodSignature setterSignature = null;
+
+    KmPropertyProcessor(KmProperty kmProperty) {
+      kmProperty.accept(new KmPropertyVisitor() {
+        @Override
+        public KmPropertyExtensionVisitor visitExtensions(KmExtensionType type) {
+          if (type != JvmPropertyExtensionVisitor.TYPE) {
+            return null;
+          }
+          return new JvmPropertyExtensionVisitor() {
+            @Override
+            public void visit(
+                int flags,
+                JvmFieldSignature fieldDesc,
+                JvmMethodSignature getterDesc,
+                JvmMethodSignature setterDesc) {
+              assert fieldSignature == null : fieldSignature.asString();
+              fieldSignature = fieldDesc;
+              assert getterSignature == null : getterSignature.asString();
+              getterSignature = getterDesc;
+              assert setterSignature == null : setterSignature.asString();
+              setterSignature = setterDesc;
+            }
+          };
+        }
+      });
+    }
+
+    JvmFieldSignature fieldSignature() {
+      return fieldSignature;
+    }
+
+    JvmMethodSignature getterSignature() {
+      return getterSignature;
+    }
+
+    JvmMethodSignature setterSignature() {
+      return setterSignature;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
index ff9ad15..5f7843f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.kotlin;
 
 import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
+import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.parameterTypesFromJvmMethodSignature;
+import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.returnTypeFromJvmMethodSignature;
 import static com.android.tools.r8.utils.DescriptorUtils.descriptorToInternalName;
 import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKmType;
 import static kotlinx.metadata.FlagsKt.flagsOf;
@@ -14,14 +16,18 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmConstructorProcessor;
+import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmFunctionProcessor;
+import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmPropertyProcessor;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.Box;
 import java.util.List;
 import kotlinx.metadata.KmConstructor;
 import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmProperty;
 import kotlinx.metadata.KmType;
 import kotlinx.metadata.KmValueParameter;
+import kotlinx.metadata.jvm.JvmMethodSignature;
 
 public class KotlinMetadataSynthesizer {
 
@@ -69,6 +75,13 @@
     return kmType;
   }
 
+  private static boolean isCompatible(String desc, DexType type) {
+    if (desc == null || type == null) {
+      return false;
+    }
+    return desc.equals(type.toDescriptorString());
+  }
+
   private static boolean isCompatible(KmType kmType, DexType type, AppView<?> appView) {
     if (kmType == null || type == null) {
       return false;
@@ -85,8 +98,32 @@
     return descriptor.equals(getDescriptorFromKmType(kmType));
   }
 
+  private static boolean isCompatibleJvmMethodSignature(
+      JvmMethodSignature signature, DexEncodedMethod method) {
+    String methodName = method.method.name.toString();
+    if (!signature.getName().equals(methodName)) {
+      return false;
+    }
+    if (!isCompatible(
+        returnTypeFromJvmMethodSignature(signature), method.method.proto.returnType)) {
+      return false;
+    }
+    List<String> parameterTypes = parameterTypesFromJvmMethodSignature(signature);
+    if (parameterTypes == null || parameterTypes.size() != method.method.proto.parameters.size()) {
+      return false;
+    }
+    for (int i = 0; i < method.method.proto.parameters.size(); i++) {
+      if (!isCompatible(parameterTypes.get(i), method.method.proto.parameters.values[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   public static boolean isCompatibleConstructor(
       KmConstructor constructor, DexEncodedMethod method, AppView<?> appView) {
+    // Note that targets for @JvmName don't include constructor. So, it's not necessary to process
+    // JvmMethodSignature inside JvmConstructorExtension.
     List<KmValueParameter> parameters = constructor.getValueParameters();
     if (method.method.proto.parameters.size() != parameters.size()) {
       return false;
@@ -102,6 +139,13 @@
 
   public static boolean isCompatibleFunction(
       KmFunction function, DexEncodedMethod method, AppView<?> appView) {
+    // Check if a custom name is set to avoid name clash.
+    KmFunctionProcessor kmFunctionProcessor = new KmFunctionProcessor(function);
+    JvmMethodSignature jvmMethodSignature = kmFunctionProcessor.signature();
+    if (jvmMethodSignature != null && isCompatibleJvmMethodSignature(jvmMethodSignature, method)) {
+      return true;
+    }
+
     if (!function.getName().equals(method.method.name.toString())) {
       return false;
     }
@@ -121,9 +165,15 @@
     return true;
   }
 
-  // TODO(b/70169921): Handling JVM extensions as well.
   public static boolean isCompatibleExtension(
       KmFunction extension, DexEncodedMethod method, AppView<?> appView) {
+    // Check if a custom name is set to avoid name clash.
+    KmFunctionProcessor kmFunctionProcessor = new KmFunctionProcessor(extension);
+    JvmMethodSignature jvmMethodSignature = kmFunctionProcessor.signature();
+    if (jvmMethodSignature != null && isCompatibleJvmMethodSignature(jvmMethodSignature, method)) {
+      return true;
+    }
+
     if (!extension.getName().equals(method.method.name.toString())) {
       return false;
     }
@@ -149,6 +199,58 @@
     return true;
   }
 
+  public static boolean isCompatibleProperty(
+      KmProperty kmProperty, DexEncodedMethod method, AppView<?> appView) {
+    KmPropertyProcessor kmPropertyProcessor = new KmPropertyProcessor(kmProperty);
+    // Check if a custom getter is defined via @get:JvmName("myGetter").
+    JvmMethodSignature getterSignature = kmPropertyProcessor.getterSignature();
+    if (getterSignature != null && isCompatibleJvmMethodSignature(getterSignature, method)) {
+      return true;
+    }
+    // Check if a custom setter is defined via @set:JvmName("mySetter").
+    JvmMethodSignature setterSignature = kmPropertyProcessor.setterSignature();
+    if (setterSignature != null && isCompatibleJvmMethodSignature(setterSignature, method)) {
+      return true;
+    }
+
+    // E.g., property `prop: T` is mapped to `getProp()T`, `setProp(T)V`, `prop$annotations()V`.
+    // For boolean property, though, getter is mapped to `isProp()Z`.
+    // TODO(b/70169921): Avoid decoding.
+    String methodName = method.method.name.toString();
+    if (!methodName.startsWith("is")
+        && !methodName.startsWith("get")
+        && !methodName.startsWith("set")
+        && !methodName.endsWith("$annotations")) {
+      return false;
+    }
+
+    String propertyName = kmProperty.getName();
+    assert propertyName.length() > 0;
+    String annotations = propertyName + "$annotations";
+    if (methodName.equals(annotations)) {
+      return true;
+    }
+    String capitalized =
+        Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
+    String returnTypeDescriptor = getDescriptorFromKmType(kmProperty.returnType);
+    String getterPrefix =
+        returnTypeDescriptor != null && returnTypeDescriptor.endsWith("Boolean;") ? "is" : "get";
+    String getter = getterPrefix + capitalized;
+    if (methodName.equals(getter)
+        && method.method.proto.parameters.size() == 0
+        && isCompatible(kmProperty.returnType, method.method.proto.returnType, appView)) {
+      return true;
+    }
+    String setter = "set" + capitalized;
+    if (methodName.equals(setter)
+        && method.method.proto.returnType.isVoidType()
+        && method.method.proto.parameters.size() == 1
+        && isCompatible(kmProperty.returnType, method.method.proto.parameters.values[0], appView)) {
+      return true;
+    }
+    return false;
+  }
+
   static KmConstructor toRenamedKmConstructor(
       DexEncodedMethod method,
       KmConstructor original,
@@ -161,9 +263,10 @@
     }
     // TODO(b/70169921): {@link KmConstructor.extensions} is private, i.e., no way to alter!
     //   Thus, we rely on original metadata for now.
-    Box<Boolean> hasJvmExtension = new Box<>(false);
+    KmConstructorProcessor kmConstructorProcessor = new KmConstructorProcessor(original);
+    JvmMethodSignature jvmMethodSignature = kmConstructorProcessor.signature();
     KmConstructor kmConstructor =
-        hasJvmExtension.get()
+        jvmMethodSignature != null
             ? original
             // TODO(b/70169921): Consult kotlinx.metadata.Flag.Constructor to set IS_PRIMARY.
             : new KmConstructor(method.accessFlags.getAsKotlinFlags());
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 7af0752..f12d958 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -135,14 +135,15 @@
   MethodNameMinifier(AppView<AppInfoWithLiveness> appView, MemberNamingStrategy strategy) {
     this.appView = appView;
     this.strategy = strategy;
-    rootReservationState = MethodReservationState.createRoot(getKeyTransform());
+    rootReservationState = MethodReservationState.createRoot(getReservationKeyTransform());
     rootNamingState =
-        MethodNamingState.createRoot(getKeyTransform(), strategy, rootReservationState);
+        MethodNamingState.createRoot(getNamingKeyTransform(), strategy, rootReservationState);
     namingStates.put(null, rootNamingState);
   }
 
-  private Function<DexMethod, ?> getKeyTransform() {
-    if (appView.options().getProguardConfiguration().isOverloadAggressively()) {
+  private Function<DexMethod, ?> getReservationKeyTransform() {
+    if (appView.options().getProguardConfiguration().isOverloadAggressively()
+        && appView.options().isGeneratingClassFiles()) {
       // Use the full proto as key, hence reuse names based on full signature.
       return method -> method.proto;
     } else {
@@ -151,6 +152,12 @@
     }
   }
 
+  private Function<DexMethod, ?> getNamingKeyTransform() {
+    return appView.options().isGeneratingClassFiles()
+        ? getReservationKeyTransform()
+        : method -> null;
+  }
+
   static class MethodRenaming {
 
     final Map<DexMethod, DexString> renaming;
@@ -199,23 +206,9 @@
     MethodNamingState<?> namingState =
         namingStates.computeIfAbsent(
             type, ignore -> parentNamingState.createChild(reservationState));
-    // The names for direct methods should not contribute to the naming of methods in sub-types:
-    // class A {
-    //   public int foo() { ... }   --> a
-    //   private int bar() { ... }  --> b
-    // }
-    //
-    // class B extends A {
-    //   public int baz() { ... }   --> b
-    // }
-    //
-    // A simple way to ensure this is to process virtual methods first and then direct methods.
     DexClass holder = appView.definitionFor(type);
     if (holder != null && strategy.allowMemberRenaming(holder)) {
-      for (DexEncodedMethod method : holder.virtualMethodsSorted()) {
-        assignNameToMethod(holder, method, namingState);
-      }
-      for (DexEncodedMethod method : holder.directMethodsSorted()) {
+      for (DexEncodedMethod method : holder.allMethodsSorted()) {
         assignNameToMethod(holder, method, namingState);
       }
     }
@@ -239,9 +232,7 @@
     if (encodedMethod.method.name != newName) {
       renaming.put(encodedMethod.method, newName);
     }
-    if (!encodedMethod.isPrivateMethod()) {
-      state.addRenaming(newName, encodedMethod.method);
-    }
+    state.addRenaming(newName, encodedMethod.method);
   }
 
   private void reserveNamesInClasses() {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
index c6b7ee0..7926de0 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
@@ -6,6 +6,8 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.naming.MethodNamingState.InternalNewNameState;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.google.common.base.Equivalence.Wrapper;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -72,8 +74,8 @@
   }
 
   boolean isAvailable(DexString candidate, DexMethod method) {
-    Set<DexString> usedBy = getUsedBy(candidate, method);
-    if (usedBy != null && usedBy.contains(method.name)) {
+    Set<Wrapper<DexMethod>> usedBy = getUsedBy(candidate, method);
+    if (usedBy != null && usedBy.contains(MethodSignatureEquivalence.get().wrap(method))) {
       return true;
     }
     boolean isReserved = reservationState.isReserved(candidate, method);
@@ -82,16 +84,13 @@
     }
     // We now have a reserved name. We therefore have to check if the reservation is
     // equal to candidate, otherwise the candidate is not available.
-    if (isReserved && usedBy == null) {
-      Set<DexString> methodReservedNames = reservationState.getReservedNamesFor(method);
-      return methodReservedNames != null && methodReservedNames.contains(candidate);
-    }
-    return false;
+    Set<DexString> methodReservedNames = reservationState.getReservedNamesFor(method);
+    return methodReservedNames != null && methodReservedNames.contains(candidate);
   }
 
-  private Set<DexString> getUsedBy(DexString name, DexMethod method) {
+  private Set<Wrapper<DexMethod>> getUsedBy(DexString name, DexMethod method) {
     InternalNewNameState internalState = getInternalState(method);
-    Set<DexString> nameUsedBy = null;
+    Set<Wrapper<DexMethod>> nameUsedBy = null;
     if (internalState != null) {
       nameUsedBy = internalState.getUsedBy(name);
     }
@@ -105,7 +104,7 @@
     DexString assignedName = null;
     InternalNewNameState internalState = getInternalState(method);
     if (internalState != null) {
-      assignedName = internalState.getAssignedName(method.name);
+      assignedName = internalState.getAssignedName(method);
     }
     if (assignedName == null && parentNamingState != null) {
       assignedName = parentNamingState.getAssignedName(method);
@@ -125,14 +124,13 @@
   static class InternalNewNameState implements InternalNamingState {
 
     private final InternalNewNameState parentInternalState;
-    private Map<DexString, DexString> originalToRenamedNames = new HashMap<>();
-    private Map<DexString, Set<DexString>> usedBy = new HashMap<>();
+    private Map<Wrapper<DexMethod>, DexString> originalToRenamedNames = new HashMap<>();
+    private Map<DexString, Set<Wrapper<DexMethod>>> usedBy = new HashMap<>();
 
     private static final int INITIAL_NAME_COUNT = 1;
     private static final int INITIAL_DICTIONARY_INDEX = 0;
 
-    private int virtualNameCount;
-    private int directNameCount = 0;
+    private int nameCount;
     private int dictionaryIndex;
 
     private InternalNewNameState(InternalNewNameState parentInternalState) {
@@ -141,8 +139,8 @@
           parentInternalState == null
               ? INITIAL_DICTIONARY_INDEX
               : parentInternalState.dictionaryIndex;
-      this.virtualNameCount =
-          parentInternalState == null ? INITIAL_NAME_COUNT : parentInternalState.virtualNameCount;
+      this.nameCount =
+          parentInternalState == null ? INITIAL_NAME_COUNT : parentInternalState.nameCount;
     }
 
     @Override
@@ -155,40 +153,35 @@
       return dictionaryIndex++;
     }
 
-    Set<DexString> getUsedBy(DexString name) {
+    Set<Wrapper<DexMethod>> getUsedBy(DexString name) {
       return usedBy.get(name);
     }
 
-    DexString getAssignedName(DexString originalName) {
-      return originalToRenamedNames.get(originalName);
+    DexString getAssignedName(DexMethod method) {
+      return originalToRenamedNames.get(MethodSignatureEquivalence.get().wrap(method));
     }
 
     void addRenaming(DexString newName, DexMethod method) {
-      originalToRenamedNames.put(method.name, newName);
-      usedBy.computeIfAbsent(newName, ignore -> new HashSet<>()).add(method.name);
+      final Wrapper<DexMethod> wrappedMethod = MethodSignatureEquivalence.get().wrap(method);
+      originalToRenamedNames.put(wrappedMethod, newName);
+      usedBy.computeIfAbsent(newName, ignore -> new HashSet<>()).add(wrappedMethod);
     }
 
     private boolean checkParentPublicNameCountIsLessThanOrEqual() {
       int maxParentCount = 0;
       InternalNewNameState tmp = parentInternalState;
       while (tmp != null) {
-        maxParentCount = Math.max(tmp.virtualNameCount, maxParentCount);
+        maxParentCount = Math.max(tmp.nameCount, maxParentCount);
         tmp = tmp.parentInternalState;
       }
-      assert maxParentCount <= virtualNameCount;
+      assert maxParentCount <= nameCount;
       return true;
     }
 
     @Override
     public int incrementNameIndex(boolean isDirectMethodCall) {
       assert checkParentPublicNameCountIsLessThanOrEqual();
-      if (isDirectMethodCall) {
-        return virtualNameCount + directNameCount++;
-      } else {
-        // TODO(b/144877828): is it guaranteed?
-        assert directNameCount == 0;
-        return virtualNameCount++;
-      }
+      return nameCount++;
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/MethodReservationState.java b/src/main/java/com/android/tools/r8/naming/MethodReservationState.java
index ad5b8cc..2ce35f9 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodReservationState.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodReservationState.java
@@ -7,6 +7,8 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.naming.MethodReservationState.InternalReservationState;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.google.common.base.Equivalence.Wrapper;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -36,7 +38,7 @@
 
   void reserveName(DexString reservedName, DexMethod method) {
     try {
-      getOrCreateInternalState(method).reserveName(method.name, reservedName);
+      getOrCreateInternalState(method).reserveName(method, reservedName);
     } catch (AssertionError err) {
       throw new RuntimeException(
           String.format(
@@ -61,7 +63,7 @@
     InternalReservationState internalState = getInternalState(method);
     Set<DexString> reservedName = null;
     if (internalState != null) {
-      reservedName = internalState.getAssignedNamesFor(method.name);
+      reservedName = internalState.getAssignedNamesFor(method);
     }
     if (reservedName == null && parentNamingState != null) {
       reservedName = parentNamingState.getReservedNamesFor(method);
@@ -75,28 +77,28 @@
   }
 
   static class InternalReservationState {
-    private Map<DexString, Set<DexString>> originalToReservedNames = null;
+    private Map<Wrapper<DexMethod>, Set<DexString>> originalToReservedNames = null;
     private Set<DexString> reservedNames = null;
 
     boolean isReserved(DexString name) {
       return reservedNames != null && reservedNames.contains(name);
     }
 
-    Set<DexString> getAssignedNamesFor(DexString original) {
-      Set<DexString> result = null;
-      if (originalToReservedNames != null) {
-        result = originalToReservedNames.get(original);
+    Set<DexString> getAssignedNamesFor(DexMethod method) {
+      if (originalToReservedNames == null) {
+        return null;
       }
-      return result;
+      return originalToReservedNames.get(MethodSignatureEquivalence.get().wrap(method));
     }
 
-    void reserveName(DexString originalName, DexString name) {
+    void reserveName(DexMethod method, DexString name) {
       if (reservedNames == null) {
         assert originalToReservedNames == null;
         originalToReservedNames = new HashMap<>();
         reservedNames = new HashSet<>();
       }
-      originalToReservedNames.computeIfAbsent(originalName, ignore -> new HashSet<>()).add(name);
+      final Wrapper<DexMethod> wrapped = MethodSignatureEquivalence.get().wrap(method);
+      originalToReservedNames.computeIfAbsent(wrapped, ignore -> new HashSet<>()).add(name);
       reservedNames.add(name);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java b/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java
index 773db00..1c99cc6 100644
--- a/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java
+++ b/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.shaking;
 
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.ProgramMethod;
 import java.util.function.Consumer;
 
 public abstract class DelayedRootSetActionItem {
@@ -18,24 +18,22 @@
   }
 
   public static class InterfaceMethodSyntheticBridgeAction extends DelayedRootSetActionItem {
-    private final DexEncodedMethod methodToKeep;
-    private final DexEncodedMethod singleTarget;
+    private final ProgramMethod methodToKeep;
+    private final ProgramMethod singleTarget;
     private final Consumer<RootSetBuilder> action;
 
     InterfaceMethodSyntheticBridgeAction(
-        DexEncodedMethod methodToKeep,
-        DexEncodedMethod singleTarget,
-        Consumer<RootSetBuilder> action) {
+        ProgramMethod methodToKeep, ProgramMethod singleTarget, Consumer<RootSetBuilder> action) {
       this.methodToKeep = methodToKeep;
       this.singleTarget = singleTarget;
       this.action = action;
     }
 
-    public DexEncodedMethod getMethodToKeep() {
+    public ProgramMethod getMethodToKeep() {
       return methodToKeep;
     }
 
-    public DexEncodedMethod getSingleTarget() {
+    public ProgramMethod getSingleTarget() {
       return singleTarget;
     }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index ac1dea1..1b76214 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2263,6 +2263,13 @@
         (field, info) -> field != info.getField() || info == MISSING_FIELD_ACCESS_INFO);
     assert fieldAccessInfoCollection.verifyMappingIsOneToOne();
 
+    for (ProgramMethod bridge : syntheticInterfaceMethodBridges.values()) {
+      appView.appInfo().invalidateTypeCacheFor(bridge.holder.type);
+      bridge.holder.appendVirtualMethod(bridge.method);
+      targetedMethods.add(bridge.method, graphReporter.fakeReportShouldNotBeUsed());
+      liveMethods.add(bridge.holder, bridge.method, graphReporter.fakeReportShouldNotBeUsed());
+    }
+
     AppInfoWithLiveness appInfoWithLiveness =
         new AppInfoWithLiveness(
             appInfo,
@@ -2460,25 +2467,31 @@
     return builder.buildConsequentRootSet();
   }
 
+  private Map<DexMethod, ProgramMethod> syntheticInterfaceMethodBridges = new IdentityHashMap<>();
+
   private void handleInterfaceMethodSyntheticBridgeAction(
       InterfaceMethodSyntheticBridgeAction action, RootSetBuilder builder) {
-    if (rootSet.noShrinking.containsKey(action.getSingleTarget().method)) {
+    ProgramMethod methodToKeep = action.getMethodToKeep();
+    ProgramMethod singleTarget = action.getSingleTarget();
+    if (rootSet.noShrinking.containsKey(singleTarget.method.method)) {
       return;
     }
-    DexEncodedMethod methodToKeep = action.getMethodToKeep();
-    DexEncodedMethod singleTarget = action.getSingleTarget();
-    DexClass clazz = getProgramClassOrNull(methodToKeep.method.holder);
     if (methodToKeep != singleTarget) {
-      // Insert a bridge method.
-      if (appView.definitionFor(methodToKeep.method) == null) {
-        clazz.appendVirtualMethod(methodToKeep);
-        if (singleTarget.isLibraryMethodOverride().isTrue()) {
-          methodToKeep.setLibraryMethodOverride(OptionalBool.TRUE);
+      assert null == methodToKeep.holder.lookupMethod(methodToKeep.method.method);
+      ProgramMethod old =
+          syntheticInterfaceMethodBridges.put(methodToKeep.method.method, methodToKeep);
+      if (old == null) {
+        if (singleTarget.method.isLibraryMethodOverride().isTrue()) {
+          methodToKeep.method.setLibraryMethodOverride(OptionalBool.TRUE);
         }
-        appView.appInfo().invalidateTypeCacheFor(methodToKeep.method.holder);
-        // The addition of a bridge method can lead to a change of resolution, thus the cached
-        // resolution targets are invalid.
-        virtualTargetsMarkedAsReachable.remove(methodToKeep.method);
+        assert singleTarget.holder.isInterface();
+        markVirtualMethodAsReachable(
+            singleTarget.method.method,
+            singleTarget.holder.isInterface(),
+            null,
+            graphReporter.fakeReportShouldNotBeUsed());
+        enqueueMarkMethodLiveAction(
+            singleTarget.holder, singleTarget.method, graphReporter.fakeReportShouldNotBeUsed());
       }
     }
     action.getAction().accept(builder);
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index cf820bc..c172b52 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -67,6 +67,10 @@
     }
   }
 
+  public KeepReasonWitness fakeReportShouldNotBeUsed() {
+    return KeepReasonWitness.INSTANCE;
+  }
+
   public boolean verifyRootedPath(DexProgramClass liveType) {
     assert verificationGraphConsumer != null;
     ClassGraphNode node = getClassGraphNode(liveType.type);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index b842680..8555b93 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -23,7 +23,8 @@
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
@@ -457,7 +458,9 @@
     }
     // TODO(b/143643942): Generalize the below approach to also work for subtyping hierarchies in
     //  fullmode.
-    if (clazz.isProgramClass()) {
+    if (clazz.isProgramClass()
+        && rule.isProguardKeepRule()
+        && !rule.asProguardKeepRule().getModifiers().allowsShrinking) {
       new SynthesizeMissingInterfaceMethodsForMemberRules(
               clazz.asProgramClass(), memberKeepRules, rule, preconditionSupplier, ifRule)
           .run();
@@ -484,6 +487,8 @@
         ProguardConfigurationRule context,
         Map<Predicate<DexDefinition>, DexDefinition> preconditionSupplier,
         ProguardIfRule ifRule) {
+      assert context.isProguardKeepRule();
+      assert !context.asProguardKeepRule().getModifiers().allowsShrinking;
       this.originalClazz = originalClazz;
       this.memberKeepRules = memberKeepRules;
       this.context = context;
@@ -525,49 +530,46 @@
     }
 
     private void tryAndKeepMethodOnClass(DexEncodedMethod method, ProguardMemberRule rule) {
-      boolean shouldKeepMethod =
-          context.isProguardKeepRule()
-              && !context.asProguardKeepRule().getModifiers().allowsShrinking;
-      if (!shouldKeepMethod) {
+      SingleResolutionResult resolutionResult =
+          appView.appInfo().resolveMethod(originalClazz, method.method).asSingleResolution();
+      if (resolutionResult == null || !resolutionResult.isVirtualTarget()) {
         return;
       }
-      ResolutionResult resolutionResult =
-          appView.appInfo().resolveMethod(originalClazz, method.method);
-      if (!resolutionResult.isVirtualTarget() || !resolutionResult.isSingleResolution()) {
+      if (resolutionResult.getResolvedHolder() == originalClazz
+          || resolutionResult.getResolvedHolder().isNotProgramClass()) {
         return;
       }
-      DexEncodedMethod methodToKeep = resolutionResult.getSingleTarget();
-      if (methodToKeep.method.holder == originalClazz.type) {
-        return;
-      }
-      DexClass holder = appView.definitionFor(methodToKeep.method.holder);
-      if (holder.isNotProgramClass()) {
-        return;
-      }
-      if (!holder.isInterface()) {
+      if (!resolutionResult.getResolvedHolder().isInterface()) {
         // TODO(b/143643942): For fullmode, this check should probably be removed.
         return;
       }
-      if (canInsertForwardingMethod(originalClazz, methodToKeep)) {
-        methodToKeep = methodToKeep.toForwardingMethod(originalClazz, appView);
-      }
-      final DexEncodedMethod finalKeepMethod = methodToKeep;
+      ProgramMethod resolutionMethod =
+          new ProgramMethod(
+              resolutionResult.getResolvedHolder().asProgramClass(),
+              resolutionResult.getResolvedMethod());
+      ProgramMethod methodToKeep =
+          canInsertForwardingMethod(originalClazz, resolutionMethod.method)
+              ? new ProgramMethod(
+                  originalClazz, resolutionMethod.method.toForwardingMethod(originalClazz, appView))
+              : resolutionMethod;
+
       delayedRootSetActionItems.add(
           new InterfaceMethodSyntheticBridgeAction(
               methodToKeep,
-              resolutionResult.getSingleTarget(),
+              resolutionMethod,
               (rootSetBuilder) -> {
                 if (Log.ENABLED) {
                   Log.verbose(
                       getClass(),
                       "Marking method `%s` due to `%s { %s }`.",
-                      finalKeepMethod,
+                      methodToKeep,
                       context,
                       rule);
                 }
                 DexDefinition precondition =
-                    testAndGetPrecondition(finalKeepMethod, preconditionSupplier);
-                rootSetBuilder.addItemToSets(finalKeepMethod, context, rule, precondition, ifRule);
+                    testAndGetPrecondition(methodToKeep.method, preconditionSupplier);
+                rootSetBuilder.addItemToSets(
+                    methodToKeep.method, context, rule, precondition, ifRule);
               }));
     }
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index c518937..ae8eb7e 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1214,7 +1214,8 @@
               DexAnnotationSet.empty(),
               ParameterAnnotationsList.empty(),
               code,
-              method.hasClassFileVersion() ? method.getClassFileVersion() : -1);
+              method.hasClassFileVersion() ? method.getClassFileVersion() : -1,
+              true);
       if (method.accessFlags.isPromotedToPublic()) {
         // The bridge is now the public method serving the role of the original method, and should
         // reflect that this method was publicized.
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 1425bc0..9ed931c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -213,6 +213,8 @@
   public boolean enablePropagationOfDynamicTypesAtCallSites = true;
   // TODO(b/69963623): enable if everything is ready, including signature rewriting at call sites.
   public boolean enablePropagationOfConstantsAtCallSites = false;
+  // TODO(b/70169921): enable after branching.
+  public boolean enableKotlinMetadataRewriting = false;
   public boolean encodeChecksums = false;
   public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
   public boolean enableCfInterfaceMethodDesugaring = false;
@@ -242,7 +244,8 @@
   // TODO(b/125282093): Enable member value propagation for instance fields.
   public boolean enableValuePropagationForInstanceFields = false;
   public boolean enableUninstantiatedTypeOptimization = true;
-  public boolean enableUninstantiatedTypeOptimizationForInterfaces = true;
+  // Currently disabled, see b/146957343.
+  public boolean enableUninstantiatedTypeOptimizationForInterfaces = false;
   // TODO(b/138917494): Disable until we have numbers on potential performance penalties.
   public boolean enableRedundantConstNumberOptimization = false;
 
@@ -1075,6 +1078,13 @@
     enablePropagationOfConstantsAtCallSites = true;
   }
 
+  // TODO(b/70169921): Remove this once enabled.
+  @VisibleForTesting
+  public void enableKotlinMetadataRewriting() {
+    assert !enableKotlinMetadataRewriting;
+    enableKotlinMetadataRewriting = true;
+  }
+
   private boolean hasMinApi(AndroidApiLevel level) {
     assert isGeneratingDex();
     return minApiLevel >= level.getLevel();
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 933f4fb..c7324da 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -303,6 +303,10 @@
           // deterministic behaviour: the algorithm will assign new line numbers in this order.
           // Methods with different names can share the same line numbers, that's why they don't
           // need to be sorted.
+          // If we are compiling to DEX we will try to not generate overloaded names. This saves
+          // space by allowing more debug-information to be canonicalized. If we have overloaded
+          // methods, we either did not rename them, we renamed them according to a supplied map or
+          // they may be bridges for interface methods with covariant return types.
           sortMethods(methods);
         }
 
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index db0baa3..fbacef6 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -13,6 +13,8 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.sdklib.AndroidVersion;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
 import com.android.tools.r8.D8CommandParser.OrderedClassFileResourceProvider;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.dex.Marker;
@@ -519,6 +521,51 @@
     assertEquals(0, new ZipFile(emptyZip.toFile(), StandardCharsets.UTF_8).size());
   }
 
+  private void checkSingleForceAllAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(1, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+  }
+
+  private void checkSingleForceClassAndPackageAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(2, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+    assertEquals("ClassName", entries.get(0).getValue());
+    assertEquals(transformation, entries.get(1).getTransformation());
+    assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+    assertEquals("PackageName", entries.get(1).getValue());
+  }
+
+  @Test
+  public void forceAssertionOption() throws Exception {
+    checkSingleForceAllAssertion(
+        parse("--force-enable-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceAllAssertion(
+        parse("--force-disable-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceAllAssertion(
+        parse("--force-passthrough-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+    checkSingleForceClassAndPackageAssertion(
+        parse("--force-enable-assertions:ClassName", "--force-enable-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse("--force-disable-assertions:ClassName", "--force-disable-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-passthrough-assertions:ClassName",
+                "--force-passthrough-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+  }
+
   @Test(expected = CompilationFailedException.class)
   public void missingParameterForLastOption() throws CompilationFailedException {
     DiagnosticsChecker.checkErrorsContains(
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 558ba0d..060bdbb 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -8,6 +8,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
@@ -182,11 +184,79 @@
 
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
-    L8Command l8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+    L8Command l8Command =
+        parse("--desugared-lib", ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString());
     assertFalse(
         l8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
   }
 
+  private void checkSingleForceAllAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(1, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+  }
+
+  private void checkSingleForceClassAndPackageAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(2, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+    assertEquals("ClassName", entries.get(0).getValue());
+    assertEquals(transformation, entries.get(1).getTransformation());
+    assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+    assertEquals("PackageName", entries.get(1).getValue());
+  }
+
+  @Test
+  public void forceAssertionOption() throws Exception {
+    checkSingleForceAllAssertion(
+        parse(
+                "--force-enable-assertions",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceAllAssertion(
+        parse(
+                "--force-disable-assertions",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceAllAssertion(
+        parse(
+                "--force-passthrough-assertions",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-enable-assertions:ClassName",
+                "--force-enable-assertions:PackageName...",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-disable-assertions:ClassName",
+                "--force-disable-assertions:PackageName...",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-passthrough-assertions:ClassName",
+                "--force-passthrough-assertions:PackageName...",
+                "--desugared-lib",
+                ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+            .getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+  }
+
   private L8Command parse(String... args) throws CompilationFailedException {
     return L8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
   }
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 165b510..cc92bf3 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -10,6 +10,8 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.dex.Marker;
@@ -632,6 +634,51 @@
     runCustomResourceProcessing(false, false, 0);
   }
 
+  private void checkSingleForceAllAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(1, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+  }
+
+  private void checkSingleForceClassAndPackageAssertion(
+      List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+    assertEquals(2, entries.size());
+    assertEquals(transformation, entries.get(0).getTransformation());
+    assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+    assertEquals("ClassName", entries.get(0).getValue());
+    assertEquals(transformation, entries.get(1).getTransformation());
+    assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+    assertEquals("PackageName", entries.get(1).getValue());
+  }
+
+  @Test
+  public void forceAssertionOption() throws Exception {
+    checkSingleForceAllAssertion(
+        parse("--force-enable-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceAllAssertion(
+        parse("--force-disable-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceAllAssertion(
+        parse("--force-passthrough-assertions").getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+    checkSingleForceClassAndPackageAssertion(
+        parse("--force-enable-assertions:ClassName", "--force-enable-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.ENABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse("--force-disable-assertions:ClassName", "--force-disable-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.DISABLE);
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-passthrough-assertions:ClassName",
+                "--force-passthrough-assertions:PackageName...")
+            .getAssertionsConfiguration(),
+        AssertionTransformation.PASSTHROUGH);
+  }
+
   @Test(expected = CompilationFailedException.class)
   public void missingParameterForLastOption() throws CompilationFailedException {
     DiagnosticsChecker.checkErrorsContains(
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index e1ebfa2..bbc9d4e 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -129,9 +129,14 @@
     return new CodeInspector(app);
   }
 
-  public RR inspect(Consumer<CodeInspector> consumer)
+  public RR inspect(ThrowingConsumer<CodeInspector, NoSuchMethodException> consumer)
       throws IOException, ExecutionException {
-    consumer.accept(inspector());
+    CodeInspector inspector = inspector();
+    try {
+      consumer.accept(inspector);
+    } catch (NoSuchMethodException exception) {
+      throw new RuntimeException(exception);
+    }
     return self();
   }
 
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index 0ba8510..ddb0608 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -64,7 +64,7 @@
   private static Pair<Path, Path> r8R8Release;
 
   private final TestParameters parameters;
-  private static boolean testExternal = true;
+  private static boolean testExternal = false;
 
   @ClassRule public static TemporaryFolder testFolder = new TemporaryFolder();
 
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
index 8322b11..9d01c91 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
@@ -12,6 +12,8 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -58,19 +60,30 @@
     int methodNumBridges = parameters.isCfRuntime() ? 0 : 2;
     ClassSubject methodMainClass = inspector.clazz(getMainClass("methods"));
     assertEquals(
-        methodNumBridges, methodMainClass.allMethods(FoundMethodSubject::isSynthetic).size());
+        methodNumBridges, methodMainClass.allMethods(this::isNestBridge).size());
 
     // Two bridges for method and staticMethod.
     int constructorNumBridges = parameters.isCfRuntime() ? 0 : 1;
     ClassSubject constructorMainClass = inspector.clazz(getMainClass("constructors"));
     assertEquals(
         constructorNumBridges,
-        constructorMainClass.allMethods(FoundMethodSubject::isSynthetic).size());
+        constructorMainClass.allMethods(this::isNestBridge).size());
 
     // Four bridges for field and staticField, both get & set.
     int fieldNumBridges = parameters.isCfRuntime() ? 0 : 4;
     ClassSubject fieldMainClass = inspector.clazz(getMainClass("fields"));
     assertEquals(
-        fieldNumBridges, fieldMainClass.allMethods(FoundMethodSubject::isSynthetic).size());
+        fieldNumBridges, fieldMainClass.allMethods(this::isNestBridge).size());
+  }
+
+  private boolean isNestBridge(FoundMethodSubject methodSubject) {
+    DexEncodedMethod method = methodSubject.getMethod();
+    if (method.isInstanceInitializer()) {
+      return method.method.proto.parameters.size() > 0 && method.method.proto.parameters.values[
+          method.method.proto.parameters.size() - 1].toSourceString()
+          .contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME);
+    }
+    return method.method.name.toString()
+        .startsWith(NestBasedAccessDesugaring.NEST_ACCESS_NAME_PREFIX);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
index d5297d1..36ac4fd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
@@ -72,6 +73,7 @@
             // to be called with Kotlin syntax from other kotlin code.
             .addKeepRules("-keep class **.ImplKt { <methods>; }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String implClassName = pkg + ".classpath_lib_ext.Impl";
@@ -127,6 +129,7 @@
             // to be called with Kotlin syntax from other kotlin code.
             .addKeepRules("-keep class **.ImplKt { <methods>; }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String implClassName = pkg + ".classpath_lib_ext.Impl";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
index e7692bc..2f583fa 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
@@ -65,6 +66,7 @@
             // to be called with Kotlin syntax from other kotlin code.
             .addKeepRules("-keep class **.BKt { <methods>; }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String superClassName = pkg + ".extension_lib.Super";
@@ -115,6 +117,7 @@
             // to be called with Kotlin syntax from other kotlin code.
             .addKeepRules("-keep class **.BKt { <methods>; }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String superClassName = pkg + ".extension_lib.Super";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
index 9280756..4adeb3f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -65,6 +66,7 @@
             .addKeepRules("-keep class **.UtilKt")
             .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String utilClassName = pkg + ".multifileclass_lib.UtilKt";
@@ -125,6 +127,7 @@
             .addKeepRules(
                 "-keepclassmembers,allowobfuscation class * { ** joinOf*(...); }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String utilClassName = pkg + ".multifileclass_lib.UtilKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
index b470bb6..e493ba2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
@@ -63,6 +64,7 @@
             // Keep Itf, but allow minification.
             .addKeepRules("-keep,allowobfuscation class **.Itf")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String itfClassName = pkg + ".parametertype_lib.Itf";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
index 2e92333..2c51bff 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
@@ -63,6 +64,7 @@
             // Keep non-private members of Impl
             .addKeepRules("-keep public class **.Impl { !private *; }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String itfClassName = pkg + ".propertytype_lib.Itf";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
index bcf2181..1dc6c14 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
@@ -62,6 +63,7 @@
             // Keep Itf, but allow minification.
             .addKeepRules("-keep,allowobfuscation class **.Itf")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
             .compile();
     String pkg = getClass().getPackage().getName();
     final String itfClassName = pkg + ".returntype_lib.Itf";
diff --git a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java
index 39747f0..8c50aa7 100644
--- a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java
@@ -6,119 +6,106 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
-import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.VmTestRunner;
-import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.nio.file.Path;
-import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
-interface SuperInterface {
-  Super foo();
-}
-
-interface SubInterface extends SuperInterface {
-  @Override
-  Sub foo();
-}
-
-class Super {
-  protected int bar() {
-    return 0;
-  }
-}
-
-class Sub extends Super {
-  @Override
-  protected int bar() {
-    return 1;
-  }
-}
-
-class SuperImplementer implements SuperInterface {
-  @Override
-  public Super foo() {
-    return new Sub();
-  }
-}
-
-class SubImplementer extends SuperImplementer implements SubInterface {
-  @Override
-  public Sub foo() {
-    return (Sub) super.foo();
-  }
-}
-
-class TestMain {
-  public static void main(String[] args) {
-    SubImplementer subImplementer = new SubImplementer();
-    Super sup = subImplementer.foo();
-    System.out.print(sup.bar());
-  }
-}
-
-@RunWith(VmTestRunner.class)
+@RunWith(Parameterized.class)
 public class CovariantReturnTypeInSubInterfaceTest extends TestBase {
 
+  interface SuperInterface {
+    Super foo();
+  }
+
+  interface SubInterface extends SuperInterface {
+    @Override
+    Sub foo();
+  }
+
+  static class Super {
+    protected int bar() {
+      return 0;
+    }
+  }
+
+  static class Sub extends Super {
+    @Override
+    protected int bar() {
+      return 1;
+    }
+  }
+
+  static class SuperImplementer implements SuperInterface {
+    @Override
+    public Super foo() {
+      return new Sub();
+    }
+  }
+
+  static class SubImplementer extends SuperImplementer implements SubInterface {
+    @Override
+    public Sub foo() {
+      return (Sub) super.foo();
+    }
+  }
+
+  static class TestMain {
+    public static void main(String[] args) {
+      SubImplementer subImplementer = new SubImplementer();
+      Super sup = subImplementer.foo();
+      System.out.print(sup.bar());
+    }
+  }
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public CovariantReturnTypeInSubInterfaceTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
   private void test(boolean overloadAggressively) throws Exception {
-    String mainName = TestMain.class.getCanonicalName();
-    String aggressive =
-        overloadAggressively ? "-overloadaggressively" : "# Not overload aggressively";
-    List<String> config = ImmutableList.of(
-        "-printmapping",
-        aggressive,
-        "-keep class " + mainName + " {",
-        "  public void main(...);",
-        "}",
-        "-keep,allowobfuscation class **.Super* {",
-        "  <methods>;",
-        "}",
-        "-keep,allowobfuscation class **.Sub* {",
-        "  <methods>;",
-        "}"
-    );
-    AndroidApp app = readClasses(
-        SuperInterface.class,
-        SubInterface.class,
-        Super.class,
-        Sub.class,
-        SuperImplementer.class,
-        SubImplementer.class,
-        TestMain.class
-    );
-    AndroidApp processedApp =
-        compileWithR8(app, String.join(System.lineSeparator(), config), options -> {
-          options.enableInlining = false;
-        });
-    CodeInspector inspector = new CodeInspector(processedApp);
+    testForR8(parameters.getBackend())
+        .addInnerClasses(CovariantReturnTypeInSubInterfaceTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(TestMain.class)
+        .addKeepRules(
+            "-keep,allowobfuscation class **.*$Super* { <methods>; }",
+            "-keep,allowobfuscation class **.*$Sub* { <methods>; }",
+            overloadAggressively ? "-overloadaggressively" : "# Not overload aggressively")
+        .addOptionsModification(internalOptions -> internalOptions.enableInlining = false)
+        .run(parameters.getRuntime(), TestMain.class)
+        .inspect(inspector -> inspect(inspector, overloadAggressively));
+  }
+
+  private void inspect(CodeInspector inspector, boolean overloadAggressively)
+      throws NoSuchMethodException {
     ClassSubject superInterface = inspector.clazz(SuperInterface.class);
     assertThat(superInterface, isRenamed());
-    MethodSubject foo1 = superInterface.method(
-        Super.class.getCanonicalName(), "foo", ImmutableList.of());
+    MethodSubject foo1 = superInterface.uniqueMethodWithName("foo");
     assertThat(foo1, isRenamed());
     ClassSubject subInterface = inspector.clazz(SubInterface.class);
     assertThat(subInterface, isRenamed());
-    MethodSubject foo2 = subInterface.method(
-        Sub.class.getCanonicalName(), "foo", ImmutableList.of());
+    MethodSubject foo2 = subInterface.method(SubInterface.class.getDeclaredMethod("foo"));
     assertThat(foo2, isRenamed());
-    assertEquals(foo1.getFinalName(), foo2.getFinalName());
-
-    ProcessResult javaResult = ToolHelper.runJava(ToolHelper.getClassPathForTests(), mainName);
-    assertEquals(0, javaResult.exitCode);
-    Path outDex = temp.getRoot().toPath().resolve("dex.zip");
-    processedApp.writeToZip(outDex, OutputMode.DexIndexed);
-    ProcessResult artResult = ToolHelper.runArtNoVerificationErrorsRaw(outDex.toString(), mainName);
-    assertEquals(0, artResult.exitCode);
-    assertEquals(javaResult.stdout, artResult.stdout);
+    if (overloadAggressively && parameters.isDexRuntime()) {
+      assertNotEquals(foo1.getFinalName(), foo2.getFinalName());
+    } else {
+      assertEquals(foo1.getFinalName(), foo2.getFinalName());
+    }
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
index 1303232..6cb9837 100644
--- a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
@@ -87,6 +87,6 @@
         minifiedMethodNames.add(methodSubject.getFinalName());
       }
     }
-    assertEquals(3, minifiedMethodNames.size());
+    assertEquals(9, minifiedMethodNames.size());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/MethodNameMinificationPrivateNamedLastTest.java b/src/test/java/com/android/tools/r8/naming/MethodNameMinificationPrivateNamedLastTest.java
deleted file mode 100644
index 301fe07..0000000
--- a/src/test/java/com/android/tools/r8/naming/MethodNameMinificationPrivateNamedLastTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.naming;
-
-import static junit.framework.TestCase.assertEquals;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.AnyOf.anyOf;
-import static org.hamcrest.core.Is.is;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NeverMerge;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
-import org.hamcrest.core.AnyOf;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-/**
- * MethodNameMinificationPrivateNamedLastTest tests that private methods are named after public
- * methods. Private methods can be named freely and should not influence how public methods are
- * named.
- */
-@RunWith(Parameterized.class)
-public class MethodNameMinificationPrivateNamedLastTest extends TestBase {
-
-  public static final String EXPECTED_OUTPUT =
-      StringUtils.lines("A.m1", "A.m2", "B.m1", "B.m2", "B.m3", "C.m1", "C.m2", "C.m3", "C.m4");
-
-  @NeverMerge
-  public static class A {
-
-    @NeverInline
-    public void m1() {
-      System.out.println("A.m1");
-      m2();
-    }
-
-    @NeverInline
-    private void m2() {
-      System.out.println("A.m2");
-    }
-  }
-
-  @NeverMerge
-  public static class B extends A {
-
-    @NeverInline
-    public void m1() {
-      System.out.println("B.m1");
-      m2();
-    }
-
-    @NeverInline
-    private void m2() {
-      System.out.println("B.m2");
-    }
-
-    @NeverInline
-    public void m3() {
-      System.out.println("B.m3");
-    }
-  }
-
-  @NeverMerge
-  public static class C extends B {
-
-    @NeverInline
-    public void m1() {
-      System.out.println("C.m1");
-      m2();
-    }
-
-    @NeverInline
-    private void m2() {
-      System.out.println("C.m2");
-    }
-
-    @NeverInline
-    public void m3() {
-      System.out.println("C.m3");
-      m4();
-    }
-
-    @NeverInline
-    private void m4() {
-      System.out.println("C.m4");
-    }
-  }
-
-  public static class Runner {
-
-    public static void main(String[] args) {
-      A a = new A();
-      a.m1();
-      B b = new B();
-      b.m1();
-      b.m3();
-      C c = new C();
-      c.m1();
-      c.m3();
-    }
-  }
-
-  private TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
-  }
-
-  public MethodNameMinificationPrivateNamedLastTest(TestParameters parameters) {
-    this.parameters = parameters;
-  }
-
-  @Test
-  public void testInheritedNamingState()
-      throws IOException, CompilationFailedException, ExecutionException {
-    testForR8(parameters.getBackend())
-        .addInnerClasses(MethodNameMinificationPrivateNamedLastTest.class)
-        .enableMergeAnnotations()
-        .enableInliningAnnotations()
-        .addKeepMainRule(Runner.class)
-        .setMinApi(parameters.getRuntime())
-        .compile()
-        .run(parameters.getRuntime(), Runner.class)
-        .assertSuccessWithOutput(EXPECTED_OUTPUT)
-        .inspect(
-            inspector -> {
-              assertEquals("a", inspector.clazz(A.class).uniqueMethodWithName("m1").getFinalName());
-              assertEquals("b", inspector.clazz(A.class).uniqueMethodWithName("m2").getFinalName());
-              assertEquals("a", inspector.clazz(B.class).uniqueMethodWithName("m1").getFinalName());
-              assertEquals("c", inspector.clazz(B.class).uniqueMethodWithName("m2").getFinalName());
-              assertEquals("b", inspector.clazz(B.class).uniqueMethodWithName("m3").getFinalName());
-              ClassSubject cSubject = inspector.clazz(C.class);
-              assertEquals("a", cSubject.uniqueMethodWithName("m1").getFinalName());
-              assertEquals("b", cSubject.uniqueMethodWithName("m3").getFinalName());
-              AnyOf<String> cPrivateNamesP = anyOf(is("c"), is("d"));
-              assertThat(cSubject.uniqueMethodWithName("m2").getFinalName(), cPrivateNamesP);
-              assertThat(cSubject.uniqueMethodWithName("m4").getFinalName(), cPrivateNamesP);
-            });
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/naming/OverloadUniqueNameTest.java b/src/test/java/com/android/tools/r8/naming/OverloadUniqueNameTest.java
new file mode 100644
index 0000000..6d9f975
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/OverloadUniqueNameTest.java
@@ -0,0 +1,205 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.transformers.ClassTransformer;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.MethodVisitor;
+
+@RunWith(Parameterized.class)
+public class OverloadUniqueNameTest extends TestBase {
+
+  public static class A {
+    public void foo(int i) {}
+
+    public void foo(Object o) {}
+  }
+
+  public static class B extends A {
+
+    // This is here to ensure that foo is not visited first when iterating sorted methods.
+    public void baz(int i) {}
+
+    // This is an override, so it needs to have the same name as A.foo(int).
+    @Override
+    public void foo(int i) {}
+
+    public void foo(boolean b) {}
+
+    public void foo(int i, int j) {}
+
+    private void foo(char c) {}
+
+    private static void foo(String s) {}
+  }
+
+  private interface I1 {
+
+    void foo(long l);
+  }
+
+  private interface I2 {
+
+    void bar(long l);
+
+    void foo(long l);
+  }
+
+  public static class C extends B implements I1, I2 {
+
+    @Override
+    public void bar(long l) {}
+
+    @Override
+    // This method should be named according to I1.foo() and I2.foo().
+    public void foo(long l) {}
+
+    @Override
+    public void foo(int i) {}
+  }
+
+  private interface I3 {
+    void foo(long l);
+  }
+
+  private interface I4 extends I3 {
+    @Override
+    void foo(long l);
+  }
+
+  public static class LambdaTest {
+
+    public static void lambdaInterface() {
+      I1 lambda = ((I1 & I3) l -> {});
+    }
+  }
+
+  public static class ReturnType {
+
+    int foo() { // <-- changed to int foo() to test overloading on return type
+      System.out.println("int foo();");
+      return 0;
+    }
+
+    void rewrite(int dummy) {
+      System.out.println("void foo();");
+    }
+  }
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public OverloadUniqueNameTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws CompilationFailedException, IOException, ExecutionException {
+    testForR8(Backend.DEX)
+        .addProgramClasses(
+            A.class, B.class, I1.class, I2.class, C.class, I3.class, I4.class, LambdaTest.class)
+        .addKeepAllClassesRuleWithAllowObfuscation()
+        .compile()
+        .inspect(
+            codeInspector -> {
+              inspectUniqueMethodsInClass(codeInspector, I1.class);
+              inspectUniqueMethodsInClass(codeInspector, I2.class);
+              inspectUniqueMethodsInClass(codeInspector, I3.class);
+              inspectUniqueMethodsInClass(codeInspector, I4.class);
+              inspectUniqueMethodsInClass(codeInspector, A.class);
+              inspectUniqueMethodsInClass(codeInspector, B.class);
+              inspectUniqueMethodsInClass(codeInspector, C.class);
+
+              // Ensure that virtual overrides in the class hierarchy has the same name.
+              final MethodSubject aFoo = codeInspector.clazz(A.class).method("void", "foo", "int");
+              final MethodSubject bFoo = codeInspector.clazz(B.class).method("void", "foo", "int");
+              assertEquals(aFoo.getFinalName(), bFoo.getFinalName());
+              final MethodSubject cFoo = codeInspector.clazz(C.class).method("void", "foo", "int");
+              assertEquals(aFoo.getFinalName(), cFoo.getFinalName());
+
+              // Ensure that all SAM interfaces has same method name.
+              final MethodSubject i1Foo = codeInspector.clazz(I1.class).uniqueMethodWithName("foo");
+              final MethodSubject i2Foo = codeInspector.clazz(I2.class).uniqueMethodWithName("foo");
+              assertEquals(i1Foo.getFinalName(), i2Foo.getFinalName());
+              final MethodSubject i3Foo = codeInspector.clazz(I3.class).uniqueMethodWithName("foo");
+              assertEquals(i1Foo.getFinalName(), i3Foo.getFinalName());
+
+              // Ensure C has the correct name for the interface method.
+              final MethodSubject cIFoo =
+                  codeInspector.clazz(C.class).method("void", "foo", "long");
+              assertEquals(cIFoo.getFinalName(), i1Foo.getFinalName());
+
+              // Ensure that I4.foo(int) has the same name as I3.foo(int).
+              final MethodSubject i4Foo = codeInspector.clazz(I4.class).uniqueMethodWithName("foo");
+              assertEquals(i3Foo.getFinalName(), i4Foo.getFinalName());
+            });
+  }
+
+  @Test
+  public void testReturnType() throws IOException, CompilationFailedException, ExecutionException {
+    testForR8(Backend.DEX)
+        .addProgramClassFileData(
+            transformer(ReturnType.class)
+                .addClassTransformer(
+                    new ClassTransformer() {
+                      @Override
+                      public MethodVisitor visitMethod(
+                          int access,
+                          String name,
+                          String descriptor,
+                          String signature,
+                          String[] exceptions) {
+                        if (name.equals("rewrite")) {
+                          return super.visitMethod(access, "foo", "()V", signature, exceptions);
+                        }
+                        return super.visitMethod(access, name, descriptor, signature, exceptions);
+                      }
+                    })
+                .transform())
+        .addKeepAllClassesRuleWithAllowObfuscation()
+        .compile()
+        .inspect(
+            codeInspector -> {
+              Set<String> seenMethodNames = new HashSet<>();
+              for (FoundMethodSubject method : codeInspector.clazz(ReturnType.class).allMethods()) {
+                assertTrue(seenMethodNames.add(method.getFinalName()));
+              }
+            });
+  }
+
+  private void inspectUniqueMethodsInClass(CodeInspector inspector, Class<?> clazz) {
+    Set<String> newNames = new HashSet<>();
+    for (Method method : clazz.getDeclaredMethods()) {
+      final MethodSubject methodSubject = inspector.method(method);
+      assertThat(methodSubject, isPresent());
+      assertTrue(methodSubject.isRenamed());
+      assertTrue(newNames.add(methodSubject.getFinalName()));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
index e034df6..ff61668 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
@@ -214,6 +215,7 @@
 
   @Test
   public void testMethodResolution_aggressively() throws Exception {
+    assumeTrue(backend == Backend.CF);
     methodResolution(true);
   }
 
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
index eade0a5..3d9645d 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
@@ -93,7 +93,8 @@
                 "  invokevirtual java/lang/Class/getDeclaredFields()[Ljava/lang/reflect/Field;"),
             ImmutableList.of(
                 "  aconst_null",
-                "  invokevirtual java/lang/reflect/Field/get(Ljava/lang/Object;)Ljava/lang/Object;")));
+                "  invokevirtual"
+                    + " java/lang/reflect/Field/get(Ljava/lang/Object;)Ljava/lang/Object;")));
     return builder;
   }
 
@@ -266,7 +267,7 @@
     MethodSubject m2 = clazz.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
     assertTrue(m2.isPresent());
     assertTrue(m2.isRenamed());
-    assertEquals(m1.getFinalName(), m2.getFinalName());
+    assertNotEquals(m1.getFinalName(), m2.getFinalName());
 
     ProcessResult output = runRaw(app, CLASS_NAME);
     assertEquals(0, output.exitCode);
@@ -296,7 +297,11 @@
     MethodSubject m2 = clazz.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
     assertTrue(m2.isPresent());
     assertTrue(m2.isRenamed());
-    assertEquals(m1.getFinalName(), m2.getFinalName());
+    if (backend == Backend.DEX) {
+      assertNotEquals(m1.getFinalName(), m2.getFinalName());
+    } else {
+      assertEquals(m1.getFinalName(), m2.getFinalName());
+    }
 
     ProcessResult output = runRaw(app, CLASS_NAME);
     assertEquals(0, output.exitCode);
@@ -410,7 +415,7 @@
     MethodSubject m2 = sup.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
     assertTrue(m2.isPresent());
     assertTrue(m2.isRenamed());
-    assertEquals(m1.getFinalName(), m2.getFinalName());
+    assertNotEquals(m1.getFinalName(), m2.getFinalName());
 
     ClassSubject sub = codeInspector.clazz(ANOTHER_CLASS);
     assertTrue(sub.isPresent());
@@ -420,7 +425,7 @@
     MethodSubject subM2 = sub.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
     assertTrue(subM2.isPresent());
     assertTrue(subM2.isRenamed());
-    assertEquals(subM1.getFinalName(), subM2.getFinalName());
+    assertNotEquals(subM1.getFinalName(), subM2.getFinalName());
 
     // No matter what, overloading methods should be renamed to the same name.
     assertEquals(m1.getFinalName(), subM1.getFinalName());
@@ -455,7 +460,11 @@
     MethodSubject m2 = sup.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
     assertTrue(m2.isPresent());
     assertTrue(m2.isRenamed());
-    assertEquals(m1.getFinalName(), m2.getFinalName());
+    if (backend == Backend.DEX) {
+      assertNotEquals(m1.getFinalName(), m2.getFinalName());
+    } else {
+      assertEquals(m1.getFinalName(), m2.getFinalName());
+    }
 
     ClassSubject sub = codeInspector.clazz(ANOTHER_CLASS);
     assertTrue(sub.isPresent());
@@ -465,7 +474,11 @@
     MethodSubject subM2 = sub.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
     assertTrue(subM2.isPresent());
     assertTrue(subM2.isRenamed());
-    assertEquals(subM1.getFinalName(), subM2.getFinalName());
+    if (backend == Backend.DEX) {
+      assertNotEquals(subM1.getFinalName(), subM2.getFinalName());
+    } else {
+      assertEquals(subM1.getFinalName(), subM2.getFinalName());
+    }
 
     // No matter what, overloading methods should be renamed to the same name.
     assertEquals(m1.getFinalName(), subM1.getFinalName());
diff --git a/src/test/java/com/android/tools/r8/reflection/TestClassForNameWhenSplit.java b/src/test/java/com/android/tools/r8/reflection/TestClassForNameWhenSplit.java
new file mode 100644
index 0000000..50305fd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/reflection/TestClassForNameWhenSplit.java
@@ -0,0 +1,97 @@
+// 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.reflection;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.dexsplitter.SplitterTestBase;
+import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+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 TestClassForNameWhenSplit extends TestBase {
+
+  private final TestParameters parameters;
+  private final String EXPECTED = "caught";
+
+  @Parameters(name="{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public TestClassForNameWhenSplit(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testClassNotFound() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testClassForNameIsKeept() throws Exception {
+    Path featurePath = temp.newFile("feature1.zip").toPath();
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .addFeatureSplit(
+            builder ->
+                SplitterTestBase.simpleSplitProvider(
+                    builder, featurePath, temp, Foobar.class))
+        .addKeepMainRule(Main.class)
+        .addKeepClassRules(Foobar.class)
+        .compile()
+        .disassemble()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      try {
+        foo();
+      } catch (ClassNotFoundException e) {
+        System.out.println("caught");
+      } catch (IllegalAccessException e) {
+        System.out.println("illegal access");
+      } catch (InstantiationException e) {
+        System.out.println("instantiation exception");
+      }
+    }
+
+    private static void foo()
+        throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+      try {
+        // Ensure cl init has been assumed triggered
+        new Foobar();
+      } catch (NoClassDefFoundError e) { }
+      // It is not valid to replace this with just classForName, even if we see that there is only
+      // a trivial clinit or the fact that we have already triggered it above.
+      Class<?> foobar =
+          Class.forName("com.android.tools.r8.reflection.TestClassForNameWhenSplit$Foobar");
+      foobar.newInstance();
+    }
+  }
+
+  public static class Foobar {
+    public Foobar() {
+      if (System.currentTimeMillis() < 2) {
+        System.out.println("A long time ago, in a galaxy far far away");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
index b951b4f..b42be41 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -40,7 +40,8 @@
     D8,
     JAVAC,
     PROGUARD,
-    R8
+    R8,
+    R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
   }
 
   private enum Mode {
@@ -207,6 +208,27 @@
             .setMinApi(parameters.getRuntime())
             .run(parameters.getRuntime(), mainClass.name);
     checkTestRunResult(r8Result, Compiler.R8);
+
+    R8TestRunResult r8ResultWithUninstantiatedTypeOptimizationForInterfaces =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(inputJar)
+            .addKeepMainRule(mainClass.name)
+            .addKeepRules(
+                "-keep class TestClass { public static I g; }",
+                "-neverinline class TestClass { public static void m(); }")
+            .enableProguardTestOptions()
+            .addOptionsModification(
+                options -> {
+                  if (mode == Mode.INVOKE_UNVERIFIABLE_METHOD) {
+                    options.testing.allowTypeErrors = true;
+                  }
+                  options.enableUninstantiatedTypeOptimizationForInterfaces = true;
+                })
+            .setMinApi(parameters.getRuntime())
+            .run(parameters.getRuntime(), mainClass.name);
+    checkTestRunResult(
+        r8ResultWithUninstantiatedTypeOptimizationForInterfaces,
+        Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES);
   }
 
   private void checkTestRunResult(TestRunResult<?> result, Compiler compiler) {
@@ -219,7 +241,9 @@
         if (useInterface) {
           result.assertSuccessWithOutput(getExpectedOutput(compiler));
         } else {
-          if (compiler == Compiler.R8 || compiler == Compiler.PROGUARD) {
+          if (compiler == Compiler.R8
+              || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
+              || compiler == Compiler.PROGUARD) {
             result.assertSuccessWithOutput(getExpectedOutput(compiler));
           } else {
             result
@@ -233,7 +257,8 @@
         if (useInterface) {
           result.assertSuccessWithOutput(getExpectedOutput(compiler));
         } else {
-          if (compiler == Compiler.R8) {
+          if (compiler == Compiler.R8
+              || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES) {
             result
                 .assertFailureWithOutput(getExpectedOutput(compiler))
                 .assertFailureWithErrorThatMatches(
@@ -261,7 +286,9 @@
       if (useInterface) {
         return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
       } else {
-        if (compiler == Compiler.R8 || compiler == Compiler.PROGUARD) {
+        if (compiler == Compiler.R8
+            || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
+            || compiler == Compiler.PROGUARD) {
           // The unverifiable method has been removed as a result of tree shaking, so the code does
           // not fail with a verification error when trying to load class `UnverifiableClass`.
           return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
@@ -274,14 +301,14 @@
     }
     assert mode == Mode.INVOKE_UNVERIFIABLE_METHOD;
     if (useInterface) {
-      if (compiler == Compiler.R8) {
+      if (compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES) {
         return StringUtils.joinLines(
             "Hello!",
             "Unexpected outcome of getstatic",
             "Unexpected outcome of checkcast",
             "Goodbye!",
             "");
-      } else if (compiler == Compiler.PROGUARD) {
+      } else if (compiler == Compiler.R8 || compiler == Compiler.PROGUARD) {
         return StringUtils.joinLines("Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
       } else if (compiler == Compiler.DX || compiler == Compiler.D8) {
         if (ToolHelper.getDexVm().getVersion() == Version.V4_0_4
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
index 74f4321..5c6a305 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
@@ -207,9 +207,9 @@
     assertThat(clazz, isPresent());
     assertEquals(minify, clazz.isRenamed());
     MethodSubject f1 = clazz.uniqueMethodWithName("f1");
-    assertThat(f1, not(isPresent()));
+    assertThat(f1, isPresent());
     MethodSubject f2 = clazz.uniqueMethodWithName("f2");
-    assertThat(f2, not(isPresent()));
+    assertThat(f2, isPresent());
     MethodSubject f3 = clazz.uniqueMethodWithName("f3");
     assertThat(f3, not(isPresent()));
     MethodSubject f4 = clazz.uniqueMethodWithName("f4");
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 705337c..d7f09cb 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -74,6 +74,10 @@
     return method(returnType, name, ImmutableList.of());
   }
 
+  public MethodSubject method(String returnType, String name, String... parameters) {
+    return method(returnType, name, Arrays.asList(parameters));
+  }
+
   public abstract MethodSubject method(String returnType, String name, List<String> parameters);
 
   public abstract MethodSubject uniqueMethodWithName(String name);