Merge commit 'be9a163a9bdd72ec036d1a048cc8cc2b2fecbadd' into dev-release
diff --git a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
new file mode 100644
index 0000000..f77bbce
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import com.android.tools.r8.utils.StringDiagnostic;
+
+@Keep
+public class AssertionsConfiguration {
+
+  /** The possible transformations of the javac generated assertion code during compilation. */
+  public enum AssertionTransformation {
+    /** Unconditionally enable the javac generated assertion code. */
+    ENABLE,
+    /**
+     * Unconditionally disable the javac generated assertion code. This will most likely remove the
+     * javac generated assertion code completely.
+     */
+    DISABLE,
+    /** Passthrough of the javac generated assertion code. */
+    PASSTHROUGH
+  }
+
+  private AssertionTransformation transformation;
+
+  private AssertionsConfiguration(AssertionTransformation transformation) {
+    this.transformation = transformation;
+  }
+
+  static Builder builder(DiagnosticsHandler handler) {
+    return new Builder(handler);
+  }
+
+  /**
+   * Builder for constructing a <code>{@link AssertionsConfiguration}</code>.
+   *
+   * <p>A builder is obtained by calling {@link
+   * BaseCompilerCommand.Builder#addAssertionsConfiguration}.
+   */
+  public AssertionTransformation getTransformation() {
+    return transformation;
+  }
+
+  @Keep
+  public static class Builder {
+    private AssertionTransformation transformation = null;
+
+    private final DiagnosticsHandler handler;
+
+    private Builder(DiagnosticsHandler handler) {
+      this.handler = handler;
+    }
+
+    /** Set how to handle javac generated assertion code. */
+    public AssertionsConfiguration.Builder setTransformation(
+        AssertionTransformation transformation) {
+      this.transformation = transformation;
+      return this;
+    }
+
+    /**
+     * Unconditionally enable javac generated assertion code in all packages and classes. This
+     * corresponds to passing <code>-enableassertions</code> or <code>-ea</code> to the java CLI.
+     */
+    public AssertionsConfiguration.Builder enable() {
+      setTransformation(AssertionTransformation.ENABLE);
+      return this;
+    }
+
+    /**
+     * Disable the javac generated assertion code in all packages and classes. This corresponds to
+     * passing <code>-disableassertions</code> or <code>-da</code> to the java CLI.
+     */
+    public AssertionsConfiguration.Builder disable() {
+      setTransformation(AssertionTransformation.DISABLE);
+      return this;
+    }
+
+    /** Passthrough of the javac generated assertion code in all packages and classes. */
+    public AssertionsConfiguration.Builder passthrough() {
+      setTransformation(AssertionTransformation.PASSTHROUGH);
+      return this;
+    }
+
+    /** Set how to handle javac generated assertion code in package and all subpackages. */
+    public AssertionsConfiguration.Builder setTransformationForPackage(
+        String packageName, AssertionTransformation transformation) {
+      handler.error(new StringDiagnostic("Unsupported"));
+      return this;
+    }
+
+    /**
+     * Unconditionally enable javac generated assertion code in package <code>packageName</code> and
+     * all subpackages. This corresponds to passing <code>-enableassertions:packageName...</code> or
+     * <code>-ea:packageName...</code> to the java CLI.
+     *
+     * <p>If <code>packageName</code> is the empty string, assertions are enabled in the unnamed
+     * package, which corresponds to passing <code>-enableassertions:...</code> or <code>-ea:...
+     * </code> to the java CLI.
+     */
+    public AssertionsConfiguration.Builder enableForPackage(String packageName) {
+      return setTransformationForPackage(packageName, AssertionTransformation.ENABLE);
+    }
+
+    /**
+     * Disable the javac generated assertion code in package <code>packageName</code> and all
+     * subpackages. This corresponds to passing <code>-disableassertions:packageName...</code> or
+     * <code>-da:packageName...</code> to the java CLI.
+     *
+     * <p>If <code>packageName</code> is the empty string assertions are disabled in the unnamed
+     * package, which corresponds to passing <code>-disableassertions:...</code> or <code>-da:...
+     * </code> to the java CLI.
+     */
+    public AssertionsConfiguration.Builder disableForPackage(String packageName) {
+      return setTransformationForPackage(packageName, AssertionTransformation.DISABLE);
+    }
+
+    public AssertionsConfiguration.Builder passthroughForPackage(String packageName) {
+      return setTransformationForPackage(packageName, AssertionTransformation.PASSTHROUGH);
+    }
+
+    /** Set how to handle javac generated assertion code in class. */
+    public AssertionsConfiguration.Builder setTransformationForClass(
+        String className, AssertionTransformation transformation) {
+      handler.error(new StringDiagnostic("Unsupported"));
+      return this;
+    }
+
+    /**
+     * Unconditionally enable javac generated assertion in class <code>className</code>. This
+     * corresponds to passing <code> -enableassertions:className</code> or <code>-ea:className
+     * </code> to the java CLI.
+     */
+    public AssertionsConfiguration.Builder enableForClass(String className) {
+      return setTransformationForClass(className, AssertionTransformation.ENABLE);
+    }
+
+    /**
+     * Disable the javac generated assertion code in class <code>className</code>. This corresponds
+     * to passing <code> -disableassertions:className</code> or <code>-da:className</code> to the
+     * java CLI.
+     */
+    public AssertionsConfiguration.Builder disableForClass(String className) {
+      return setTransformationForClass(className, AssertionTransformation.DISABLE);
+    }
+
+    public AssertionsConfiguration.Builder passthroughForClass(String className) {
+      return setTransformationForClass(className, AssertionTransformation.PASSTHROUGH);
+    }
+
+    /** Build and return the {@link AssertionsConfiguration}. */
+    public AssertionsConfiguration build() {
+      return new AssertionsConfiguration(transformation);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index b0d5055..ce60661 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -17,6 +17,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BiPredicate;
+import java.util.function.Function;
 
 /**
  * Base class for commands and command builders for compiler applications/tools which besides an
@@ -38,6 +39,7 @@
   private final boolean includeClassesChecksum;
   private final boolean optimizeMultidexForLinearAlloc;
   private final BiPredicate<String, Long> dexClassChecksumFilter;
+  private final AssertionsConfiguration assertionsConfiguration;
 
   BaseCompilerCommand(boolean printHelp, boolean printVersion) {
     super(printHelp, printVersion);
@@ -50,6 +52,7 @@
     includeClassesChecksum = false;
     optimizeMultidexForLinearAlloc = false;
     dexClassChecksumFilter = (name, checksum) -> true;
+    assertionsConfiguration = null;
   }
 
   BaseCompilerCommand(
@@ -62,7 +65,8 @@
       boolean enableDesugaring,
       boolean optimizeMultidexForLinearAlloc,
       boolean includeClassesChecksum,
-      BiPredicate<String, Long> dexClassChecksumFilter) {
+      BiPredicate<String, Long> dexClassChecksumFilter,
+      AssertionsConfiguration assertionsConfiguration) {
     super(app);
     assert minApiLevel > 0;
     assert mode != null;
@@ -75,6 +79,7 @@
     this.optimizeMultidexForLinearAlloc = optimizeMultidexForLinearAlloc;
     this.includeClassesChecksum = includeClassesChecksum;
     this.dexClassChecksumFilter = dexClassChecksumFilter;
+    this.assertionsConfiguration = assertionsConfiguration;
   }
 
   /**
@@ -129,6 +134,10 @@
     return optimizeMultidexForLinearAlloc;
   }
 
+  public AssertionsConfiguration getAssertionsConfiguration() {
+    return assertionsConfiguration;
+  }
+
   Reporter getReporter() {
     return reporter;
   }
@@ -158,6 +167,7 @@
     private boolean lookupLibraryBeforeProgram = true;
     private boolean optimizeMultidexForLinearAlloc = false;
     private BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
+    private AssertionsConfiguration assertionsConfiguration;
 
     abstract CompilationMode defaultCompilationMode();
 
@@ -476,6 +486,19 @@
       return includeClassesChecksum;
     }
 
+    /** Configure compile time assertion enabling through a {@link AssertionsConfiguration}. */
+    public B addAssertionsConfiguration(
+        Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
+            assertionsConfigurationGenerator) {
+      assertionsConfiguration =
+          assertionsConfigurationGenerator.apply(AssertionsConfiguration.builder(getReporter()));
+      return self();
+    }
+
+    public AssertionsConfiguration getAssertionsConfiguration() {
+      return assertionsConfiguration;
+    }
+
     @Override
     void validate() {
       Reporter reporter = getReporter();
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index ebebbf1..2214e2d 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.D8Command.USAGE_MESSAGE;
 import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.dex.Marker;
@@ -25,7 +26,6 @@
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
@@ -159,7 +159,7 @@
 
       final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
 
-      if (options.assertionProcessing != AssertionProcessing.LEAVE) {
+      if (options.assertionTransformation != AssertionTransformation.PASSTHROUGH) {
         // Run analysis to mark all <clinit> methods having the javac generated assertion
         // enabling code.
         ClassInitializerAssertionEnablingAnalysis analysis =
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index b72548b..6bdc35c 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.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.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
@@ -218,6 +219,7 @@
           getDesugarGraphConsumer(),
           desugaredLibraryKeepRuleConsumer,
           libraryConfiguration,
+          getAssertionsConfiguration(),
           factory);
     }
   }
@@ -285,6 +287,7 @@
       DesugarGraphConsumer desugarGraphConsumer,
       StringConsumer desugaredLibraryKeepRuleConsumer,
       DesugaredLibraryConfiguration libraryConfiguration,
+      AssertionsConfiguration assertionsConfiguration,
       DexItemFactory factory) {
     super(
         inputApp,
@@ -296,7 +299,8 @@
         enableDesugaring,
         optimizeMultidexForLinearAlloc,
         encodeChecksum,
-        dexClassChecksumFilter);
+        dexClassChecksumFilter,
+        assertionsConfiguration);
     this.intermediate = intermediate;
     this.desugarGraphConsumer = desugarGraphConsumer;
     this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
@@ -357,6 +361,16 @@
     internal.desugaredLibraryConfiguration = libraryConfiguration;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
 
+    assert internal.assertionTransformation == null;
+    if (getAssertionsConfiguration() != null) {
+      internal.assertionTransformation = getAssertionsConfiguration().getTransformation();
+    }
+    if (internal.assertionTransformation == null) {
+      // Default, when no configuration is provided, is to disable all javac generated assertion
+      // code when generating dex.
+      internal.assertionTransformation = AssertionTransformation.DISABLE;
+    }
+
     return internal;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index d96a5a1..ff64d62 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -61,7 +61,8 @@
         true,
         false,
         false,
-        (name, checksum) -> true);
+        (name, checksum) -> true,
+        AssertionsConfiguration.builder(null).build());
     this.d8Command = d8Command;
     this.r8Command = r8Command;
     this.libraryConfiguration = libraryConfiguration;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b4ae1c8..03235a4 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.R8Command.USAGE_MESSAGE;
 import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.dex.Marker;
@@ -81,7 +82,6 @@
 import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
 import com.android.tools.r8.utils.LineNumberOptimizer;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.SelfRetraceTest;
@@ -813,7 +813,7 @@
     if (appView.options().enableInitializedClassesInInstanceMethodsAnalysis) {
       enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView));
     }
-    if (appView.options().assertionProcessing != AssertionProcessing.LEAVE) {
+    if (appView.options().assertionTransformation != AssertionTransformation.PASSTHROUGH) {
       enqueuer.registerAnalysis(
           new ClassInitializerAssertionEnablingAnalysis(
               appView.dexItemFactory(), OptimizationFeedbackSimple.getInstance()));
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 8c63d06..45f9e84 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.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.ProgramResource.Kind;
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
@@ -23,7 +24,6 @@
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -562,7 +562,8 @@
               getDexClassChecksumFilter(),
               desugaredLibraryKeepRuleConsumer,
               libraryConfiguration,
-              featureSplitConfiguration);
+              featureSplitConfiguration,
+              getAssertionsConfiguration());
 
       return command;
     }
@@ -718,7 +719,8 @@
       BiPredicate<String, Long> dexClassChecksumFilter,
       StringConsumer desugaredLibraryKeepRuleConsumer,
       DesugaredLibraryConfiguration libraryConfiguration,
-      FeatureSplitConfiguration featureSplitConfiguration) {
+      FeatureSplitConfiguration featureSplitConfiguration,
+      AssertionsConfiguration assertionsConfiguration) {
     super(
         inputApp,
         mode,
@@ -729,7 +731,8 @@
         enableDesugaring,
         optimizeMultidexForLinearAlloc,
         encodeChecksum,
-        dexClassChecksumFilter);
+        dexClassChecksumFilter,
+        assertionsConfiguration);
     assert proguardConfiguration != null;
     assert mainDexKeepRules != null;
     this.mainDexKeepRules = mainDexKeepRules;
@@ -869,9 +872,16 @@
 
     // Default is to remove Java assertion code as Dalvik and Art does not reliable support
     // Java assertions. When generation class file output always keep the Java assertions code.
-    assert internal.assertionProcessing == AssertionProcessing.REMOVE;
-    if (internal.isGeneratingClassFiles()) {
-      internal.assertionProcessing = AssertionProcessing.LEAVE;
+    assert internal.assertionTransformation == null;
+    if (getAssertionsConfiguration() == null) {
+      // Default, when no configuration is provided, is to disable all javac generated assertion
+      // code when generating dex and leave it when generating class files.
+      internal.assertionTransformation =
+          internal.isGeneratingClassFiles()
+              ? AssertionTransformation.PASSTHROUGH
+              : AssertionTransformation.DISABLE;
+    } else {
+      internal.assertionTransformation = getAssertionsConfiguration().getTransformation();
     }
 
     // When generating class files the build is "intermediate" and we cannot pollute the namespace
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 5daaccc..7ba04b5 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -71,6 +71,7 @@
   public final GraphLense graphLense;
   public final NamingLens namingLens;
   public final InternalOptions options;
+  private final CodeToKeep desugaredLibraryCodeToKeep;
   public List<Marker> markers;
   public List<DexString> markerStrings;
 
@@ -167,6 +168,7 @@
     this.appView = appView;
     assert options != null;
     this.options = options;
+    this.desugaredLibraryCodeToKeep = CodeToKeep.createCodeToKeep(options, namingLens);
     this.markers = markers;
     this.graphLense = graphLense;
     this.namingLens = namingLens;
@@ -310,6 +312,11 @@
       }
       // Wait for all files to be processed before moving on.
       ThreadUtils.awaitFutures(dexDataFutures);
+      // A consumer can manage the generated keep rules.
+      if (options.desugaredLibraryKeepRuleConsumer != null && !desugaredLibraryCodeToKeep.isNop()) {
+        assert !options.isDesugaredLibraryCompilation();
+        desugaredLibraryCodeToKeep.generateKeepRules(options);
+      }
       // Fail if there are pending errors, e.g., the program consumers may have reported errors.
       options.reporter.failIfPendingErrors();
       // Supply info to all additional resource consumers.
@@ -576,7 +583,14 @@
       MethodToCodeObjectMapping codeMapping,
       ByteBufferProvider provider) {
     FileWriter fileWriter =
-        new FileWriter(provider, objectMapping, codeMapping, application, options, namingLens);
+        new FileWriter(
+            provider,
+            objectMapping,
+            codeMapping,
+            application,
+            options,
+            namingLens,
+            desugaredLibraryCodeToKeep);
     // Collect the non-fixed sections.
     fileWriter.collect();
     // Generate and write the bytes.
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index fa50d92..5cbd975 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -101,7 +101,8 @@
       MethodToCodeObjectMapping codeMapping,
       DexApplication application,
       InternalOptions options,
-      NamingLens namingLens) {
+      NamingLens namingLens,
+      CodeToKeep desugaredLibraryCodeToKeep) {
     this.mapping = mapping;
     this.codeMapping = codeMapping;
     this.application = application;
@@ -109,7 +110,7 @@
     this.namingLens = namingLens;
     this.dest = new DexOutputBuffer(provider);
     this.mixedSectionOffsets = new MixedSectionOffsets(options, codeMapping);
-    this.desugaredLibraryCodeToKeep = CodeToKeep.createCodeToKeep(options, namingLens);
+    this.desugaredLibraryCodeToKeep = desugaredLibraryCodeToKeep;
   }
 
   public static void writeEncodedAnnotation(
@@ -223,12 +224,6 @@
     writeSignature(layout);
     writeChecksum(layout);
 
-    // A consumer can manage the generated keep rules.
-    if (options.desugaredLibraryKeepRuleConsumer != null && !desugaredLibraryCodeToKeep.isNop()) {
-      assert !options.isDesugaredLibraryCompilation();
-      desugaredLibraryCodeToKeep.generateKeepRules(options);
-    }
-
     // Wrap backing buffer with actual length.
     return new ByteBufferResult(dest.stealByteBuffer(), layout.getEndOfFile());
   }
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 98760b6..61def68 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -3,14 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static kotlinx.metadata.FlagsKt.flagsOf;
+
 import com.android.tools.r8.dex.Constants;
 import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BooleanSupplier;
+import kotlinx.metadata.Flag;
 
 /** Access flags common to classes, methods and fields. */
 public abstract class AccessFlags<T extends AccessFlags<T>> {
 
+  protected Flag[] EMPTY_FLAG = {};
+
   protected static final int BASE_FLAGS
       = Constants.ACC_PUBLIC
       | Constants.ACC_PRIVATE
@@ -65,6 +71,26 @@
 
   public abstract int getAsDexAccessFlags();
 
+  public int getAsKotlinFlags() {
+    List<Flag> flags = new ArrayList<>();
+    if (isPrivate()) {
+      flags.add(Flag.IS_PRIVATE);
+    }
+    if (isProtected()) {
+      flags.add(Flag.IS_PROTECTED);
+    }
+    if (isPublic()) {
+      flags.add(Flag.IS_PUBLIC);
+    }
+    if (isFinal()) {
+      flags.add(Flag.IS_FINAL);
+    }
+    if (isOpen()) {
+      flags.add(Flag.IS_OPEN);
+    }
+    return flagsOf(flags.toArray(EMPTY_FLAG));
+  }
+
   public final int getOriginalAccessFlags() {
     return originalFlags;
   }
@@ -171,6 +197,10 @@
     set(Constants.ACC_STATIC);
   }
 
+  public boolean isOpen() {
+    return !isFinal();
+  }
+
   public boolean isFinal() {
     return isSet(Constants.ACC_FINAL);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index b9e8757..2753232 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -3,6 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static com.android.tools.r8.graph.DexCode.FAKE_THIS_PREFIX;
+import static com.android.tools.r8.graph.DexCode.FAKE_THIS_SUFFIX;
+
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.cf.code.CfFrame;
 import com.android.tools.r8.cf.code.CfIinc;
@@ -26,6 +29,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.base.Strings;
 import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -518,4 +522,32 @@
     }
     return constraint;
   }
+
+  void addFakeThisParameter(DexItemFactory factory) {
+    if (localVariables == null || localVariables.isEmpty()) {
+      // We have no debugging info in the code.
+      return;
+    }
+    int largestPrefix = 1;
+    int existingThisIndex = -1;
+    for (int i = 0; i < localVariables.size(); i++) {
+      LocalVariableInfo localVariable = localVariables.get(i);
+      largestPrefix =
+          Math.max(largestPrefix, DexCode.getLargestPrefix(factory, localVariable.local.name));
+      if (localVariable.local.name.toString().equals("this")) {
+        existingThisIndex = i;
+      }
+    }
+    if (existingThisIndex < 0) {
+      return;
+    }
+    String fakeThisName = Strings.repeat(FAKE_THIS_PREFIX, largestPrefix + 1) + FAKE_THIS_SUFFIX;
+    DebugLocalInfo debugLocalInfo =
+        new DebugLocalInfo(factory.createString(fakeThisName), this.originalHolder, null);
+    LocalVariableInfo thisLocalInfo = localVariables.get(existingThisIndex);
+    this.localVariables.set(
+        existingThisIndex,
+        new LocalVariableInfo(
+            thisLocalInfo.index, debugLocalInfo, thisLocalInfo.start, thisLocalInfo.end));
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index 0c96120..030497d 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -3,10 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static kotlinx.metadata.FlagsKt.flagsOf;
+
 import com.android.tools.r8.dex.Constants;
 import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BooleanSupplier;
+import kotlinx.metadata.Flag;
 
 public class ClassAccessFlags extends AccessFlags<ClassAccessFlags> {
 
@@ -83,6 +87,11 @@
   }
 
   @Override
+  public int getAsCfAccessFlags() {
+    return materialize();
+  }
+
+  @Override
   public int getAsDexAccessFlags() {
     // We unset the super flag here, as it is meaningless in DEX. Furthermore, we add missing
     // abstract to interfaces to work around a javac bug when generating package-info classes.
@@ -94,8 +103,25 @@
   }
 
   @Override
-  public int getAsCfAccessFlags() {
-    return materialize();
+  public int getAsKotlinFlags() {
+    int flag = super.getAsKotlinFlags();
+    List<Flag> flags = new ArrayList<>();
+    if (isAbstract()) {
+      flags.add(Flag.IS_ABSTRACT);
+    }
+    if (isClass()) {
+      flags.add(Flag.Class.IS_CLASS);
+    }
+    if (isInterface()) {
+      flags.add(Flag.Class.IS_INTERFACE);
+    }
+    if (isAnnotation()) {
+      flags.add(Flag.Class.IS_ANNOTATION_CLASS);
+    }
+    if (isEnum()) {
+      flags.add(Flag.Class.IS_ENUM_CLASS);
+    }
+    return flag | flagsOf(flags.toArray(EMPTY_FLAG));
   }
 
   /**
@@ -121,6 +147,10 @@
     }
   }
 
+  private boolean isClass() {
+    return !isInterface() && !isAnnotation() && !isEnum();
+  }
+
   public boolean isInterface() {
     return isSet(Constants.ACC_INTERFACE);
   }
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 358af04..2a87607 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -16,6 +16,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Sets;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -26,6 +27,7 @@
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
+import kotlinx.metadata.KmProperty;
 
 public abstract class DexClass extends DexDefinition {
 
@@ -208,6 +210,16 @@
     return Arrays.asList(virtualMethods);
   }
 
+  public List<DexEncodedMethod> kotlinFunctions(List<KmProperty> kmProperties) {
+    List<DexEncodedMethod> functions = new ArrayList<>();
+    for (DexEncodedMethod method : virtualMethods) {
+      if (method.isKotlinFunction(kmProperties)) {
+        functions.add(method);
+      }
+    }
+    return functions;
+  }
+
   public void appendVirtualMethod(DexEncodedMethod method) {
     DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length + 1];
     System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index a5b1097..d32abdf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -32,8 +32,8 @@
 // DexCode corresponds to code item in dalvik/dex-format.html
 public class DexCode extends Code {
 
-  private static final String FAKE_THIS_PREFIX = "_";
-  private static final String FAKE_THIS_SUFFIX = "this";
+  static final String FAKE_THIS_PREFIX = "_";
+  static final String FAKE_THIS_SUFFIX = "this";
 
   public final int registerSize;
   public final int incomingRegisterSize;
@@ -132,7 +132,7 @@
     return new DexDebugInfo(debugInfo.startLine, newParameters, debugInfo.events);
   }
 
-  private static int getLargestPrefix(DexItemFactory factory, DexString name) {
+  public static int getLargestPrefix(DexItemFactory factory, DexString name) {
     if (name != null && name.endsWith(factory.thisName)) {
       String string = name.toString();
       for (int i = 0; i < string.length(); i++) {
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 7fe4555..e4fff90 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -73,6 +73,7 @@
 import java.util.Map;
 import java.util.function.Consumer;
 import java.util.function.IntPredicate;
+import kotlinx.metadata.KmProperty;
 import org.objectweb.asm.Opcodes;
 
 public class DexEncodedMethod extends KeyedDexItem<DexMethod> {
@@ -348,6 +349,41 @@
     return accessFlags.isSynthetic();
   }
 
+  boolean isKotlinFunction(List<KmProperty> properties) {
+    return !isStaticMember() && !isKotlinProperty(properties);
+  }
+
+  // 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;
+    }
+    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;
+      }
+    }
+    return false;
+  }
+
   public boolean isOnlyInlinedIntoNestMembers() {
     return compilationState == PROCESSED_INLINING_CANDIDATE_SAME_NEST;
   }
@@ -722,8 +758,11 @@
       assert (dexCode.getDebugInfo() == null)
           || (arity == dexCode.getDebugInfo().parameters.length);
     } else {
-      // TODO(b/134732760): Patch Cf debug info.
-      assert appView.options().isDesugaredLibraryCompilation();
+      assert appView.options().isDesugaredLibraryCompilation()
+          || appView.options().enableCfInterfaceMethodDesugaring;
+      assert code.isCfCode();
+      CfCode cfCode = code.asCfCode();
+      cfCode.addFakeThisParameter(appView.dexItemFactory());
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
index bb39765..ca16abe 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
 import java.util.function.BooleanSupplier;
@@ -77,6 +78,11 @@
     return materialize();
   }
 
+  @Override
+  public int getAsKotlinFlags() {
+    throw new Unreachable("Kotlin property is not directly mapped to JVM field.");
+  }
+
   public boolean isVolatile() {
     return isSet(Constants.ACC_VOLATILE);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
index 2173ed0..1b8e3a0 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
@@ -3,10 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static kotlinx.metadata.FlagsKt.flagsOf;
+
 import com.android.tools.r8.dex.Constants;
 import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BooleanSupplier;
+import kotlinx.metadata.Flag;
 
 public class MethodAccessFlags extends AccessFlags<MethodAccessFlags> {
 
@@ -92,6 +96,11 @@
   }
 
   @Override
+  public int getAsCfAccessFlags() {
+    return materialize() & ~Constants.ACC_CONSTRUCTOR;
+  }
+
+  @Override
   public int getAsDexAccessFlags() {
     MethodAccessFlags copy = copy();
     if (copy.isSynchronized() && !copy.isNative()) {
@@ -102,8 +111,13 @@
   }
 
   @Override
-  public int getAsCfAccessFlags() {
-    return materialize() & ~Constants.ACC_CONSTRUCTOR;
+  public int getAsKotlinFlags() {
+    int flag = super.getAsKotlinFlags();
+    List<Flag> flags = new ArrayList<>();
+    if (isAbstract()) {
+      flags.add(Flag.IS_ABSTRACT);
+    }
+    return flag | flagsOf(flags.toArray(EMPTY_FLAG));
   }
 
   public boolean isSynchronized() {
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 5041e1d..32b7cbe 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
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
@@ -86,11 +87,9 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
 import com.android.tools.r8.shaking.MainDexClasses;
-import com.android.tools.r8.utils.Action;
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
 import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -171,7 +170,7 @@
       OptimizationFeedbackSimple.getInstance();
   private DexString highestSortingString;
 
-  private List<Action> onWaveDoneActions = null;
+  private List<com.android.tools.r8.utils.Action> onWaveDoneActions = null;
 
   private final List<DexString> neverMergePrefixes;
   boolean seenNotNeverMergePrefix = false;
@@ -819,11 +818,11 @@
 
   private void waveDone() {
     delayedOptimizationFeedback.updateVisibleOptimizationInfo();
-    onWaveDoneActions.forEach(Action::execute);
+    onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute);
     onWaveDoneActions = null;
   }
 
-  public void addWaveDoneAction(Action action) {
+  public void addWaveDoneAction(com.android.tools.r8.utils.Action action) {
     if (!appView.enableWholeProgramOptimizations()) {
       throw new Unreachable("addWaveDoneAction() should never be used in D8.");
     }
@@ -1177,7 +1176,7 @@
       assert appView.enableWholeProgramOptimizations();
       codeRewriter.removeSwitchMaps(code);
     }
-    if (options.assertionProcessing != AssertionProcessing.LEAVE) {
+    if (options.assertionTransformation != AssertionTransformation.PASSTHROUGH) {
       codeRewriter.processAssertions(appView, method, code, feedback);
     }
 
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 bdc98f0..fa68de4 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
@@ -1417,6 +1417,49 @@
       addProvider(
           new MethodGenerator(
               method, BackportedMethods::CharacterMethods_toStringCodepoint, "toStringCodepoint"));
+
+      // String
+      type = factory.stringType;
+
+      // String String.repeat(int)
+      name = factory.createString("repeat");
+      proto = factory.createProto(factory.stringType, factory.intType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(
+          new StatifyingMethodGenerator(
+              method, BackportedMethods::StringMethods_repeat, "repeat", type));
+
+      // boolean String.isBlank()
+      name = factory.createString("isBlank");
+      proto = factory.createProto(factory.booleanType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(
+          new StatifyingMethodGenerator(
+              method, BackportedMethods::StringMethods_isBlank, "isBlank", type));
+
+      // String String.strip()
+      name = factory.createString("strip");
+      proto = factory.createProto(factory.stringType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(
+          new StatifyingMethodGenerator(
+              method, BackportedMethods::StringMethods_strip, "strip", type));
+
+      // String String.stripLeading()
+      name = factory.createString("stripLeading");
+      proto = factory.createProto(factory.stringType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(
+          new StatifyingMethodGenerator(
+              method, BackportedMethods::StringMethods_stripLeading, "stripLeading", type));
+
+      // String String.stripTrailing()
+      name = factory.createString("stripTrailing");
+      proto = factory.createProto(factory.stringType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(
+          new StatifyingMethodGenerator(
+              method, BackportedMethods::StringMethods_stripTrailing, "stripTrailing", type));
     }
 
     private void initializeJava9OptionalMethodProviders(DexItemFactory factory) {
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 e7ba0b5..ce77849 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
@@ -29,6 +29,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.base.Suppliers;
 import com.google.common.primitives.Longs;
 import java.nio.ByteBuffer;
@@ -494,7 +495,8 @@
     }
 
     boolean holderIsInterface() {
-      if (!rewriter.converter.appView.options().isGeneratingClassFiles()) {
+      InternalOptions options = rewriter.converter.appView.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,
         // so we cannot set the flag and just return false.
@@ -503,14 +505,11 @@
       // The only case where we do Lambda desugaring with Cf to Cf is in L8.
       // If the compilation is not coreLibraryCompilation, then the assertion
       // implMethodHolder != null may fail, hence the assertion.
-      assert rewriter.converter.appView.options().isDesugaredLibraryCompilation();
+      assert options.isDesugaredLibraryCompilation() || options.enableCfInterfaceMethodDesugaring;
       DexMethod implMethod = descriptor.implHandle.asMethod();
       DexClass implMethodHolder = definitionFor(implMethod.holder);
       if (implMethodHolder == null) {
-        assert rewriter
-            .converter
-            .appView
-            .options()
+        assert options
             .desugaredLibraryConfiguration
             .getBackportCoreLibraryMember()
             .containsKey(implMethod.holder);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index 8440fc2..744b10c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -5695,6 +5695,88 @@
         ImmutableList.of());
   }
 
+  public static CfCode StringMethods_isBlank(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        2,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 1),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("I")),
+                    options.itemFactory.createString("length")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label2,
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(If.Type.GE, ValueType.INT, label8),
+            label3,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("I"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("codePointAt")),
+                false),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfLoad(ValueType.INT, 3),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Z"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("isWhitespace")),
+                false),
+            new CfIf(If.Type.NE, ValueType.INT, label6),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label6,
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("I"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("charCount")),
+                false),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfStore(ValueType.INT, 1),
+            label7,
+            new CfGoto(label2),
+            label8,
+            new CfConstNumber(1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label9),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode StringMethods_joinArray(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
@@ -5960,4 +6042,484 @@
         ImmutableList.of(),
         ImmutableList.of());
   }
+
+  public static CfCode StringMethods_repeat(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        5,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.INT, 1),
+            new CfIf(If.Type.GE, ValueType.INT, label2),
+            label1,
+            new CfNew(options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfNew(options.itemFactory.createType("Ljava/lang/StringBuilder;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfInvoke(
+                183,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("V")),
+                    options.itemFactory.createString("<init>")),
+                false),
+            new CfConstString(options.itemFactory.createString("count is negative: ")),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+                        options.itemFactory.createType("Ljava/lang/String;")),
+                    options.itemFactory.createString("append")),
+                false),
+            new CfLoad(ValueType.INT, 1),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+                        options.itemFactory.createType("I")),
+                    options.itemFactory.createString("append")),
+                false),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/String;")),
+                    options.itemFactory.createString("toString")),
+                false),
+            new CfInvoke(
+                183,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/IllegalArgumentException;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("V"),
+                        options.itemFactory.createType("Ljava/lang/String;")),
+                    options.itemFactory.createString("<init>")),
+                false),
+            new CfThrow(),
+            label2,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("I")),
+                    options.itemFactory.createString("length")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label3,
+            new CfLoad(ValueType.INT, 1),
+            new CfIf(If.Type.EQ, ValueType.INT, label4),
+            new CfLoad(ValueType.INT, 2),
+            new CfIf(If.Type.NE, ValueType.INT, label5),
+            label4,
+            new CfConstString(options.itemFactory.createString("")),
+            new CfReturn(ValueType.OBJECT),
+            label5,
+            new CfLoad(ValueType.INT, 1),
+            new CfConstNumber(1, ValueType.INT),
+            new CfIfCmp(If.Type.NE, ValueType.INT, label7),
+            label6,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfReturn(ValueType.OBJECT),
+            label7,
+            new CfNew(options.itemFactory.createType("Ljava/lang/StringBuilder;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Mul, NumericType.INT),
+            new CfInvoke(
+                183,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("V"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("<init>")),
+                false),
+            new CfStore(ValueType.OBJECT, 3),
+            label8,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 4),
+            label9,
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 1),
+            new CfIfCmp(If.Type.GE, ValueType.INT, label12),
+            label10,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+                        options.itemFactory.createType("Ljava/lang/String;")),
+                    options.itemFactory.createString("append")),
+                false),
+            new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+            label11,
+            new CfIinc(4, 1),
+            new CfGoto(label9),
+            label12,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/String;")),
+                    options.itemFactory.createString("toString")),
+                false),
+            new CfReturn(ValueType.OBJECT),
+            label13),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode StringMethods_strip(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    CfLabel label14 = new CfLabel();
+    CfLabel label15 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 1),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("I")),
+                    options.itemFactory.createString("length")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label2,
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(If.Type.GE, ValueType.INT, label8),
+            label3,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("I"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("codePointAt")),
+                false),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfLoad(ValueType.INT, 3),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Z"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("isWhitespace")),
+                false),
+            new CfIf(If.Type.NE, ValueType.INT, label6),
+            label5,
+            new CfGoto(label8),
+            label6,
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("I"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("charCount")),
+                false),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfStore(ValueType.INT, 1),
+            label7,
+            new CfGoto(label2),
+            label8,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfIfCmp(If.Type.LE, ValueType.INT, label14),
+            label9,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("I"),
+                        options.itemFactory.createType("Ljava/lang/CharSequence;"),
+                        options.itemFactory.createType("I")),
+                    options.itemFactory.createString("codePointBefore")),
+                false),
+            new CfStore(ValueType.INT, 3),
+            label10,
+            new CfLoad(ValueType.INT, 3),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Z"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("isWhitespace")),
+                false),
+            new CfIf(If.Type.NE, ValueType.INT, label12),
+            label11,
+            new CfGoto(label14),
+            label12,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 3),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("I"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("charCount")),
+                false),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 2),
+            label13,
+            new CfGoto(label8),
+            label14,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/String;"),
+                        options.itemFactory.createType("I"),
+                        options.itemFactory.createType("I")),
+                    options.itemFactory.createString("substring")),
+                false),
+            new CfReturn(ValueType.OBJECT),
+            label15),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode StringMethods_stripLeading(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 1),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("I")),
+                    options.itemFactory.createString("length")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label2,
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(If.Type.GE, ValueType.INT, label8),
+            label3,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("I"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("codePointAt")),
+                false),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfLoad(ValueType.INT, 3),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Z"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("isWhitespace")),
+                false),
+            new CfIf(If.Type.NE, ValueType.INT, label6),
+            label5,
+            new CfGoto(label8),
+            label6,
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("I"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("charCount")),
+                false),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfStore(ValueType.INT, 1),
+            label7,
+            new CfGoto(label2),
+            label8,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/String;"),
+                        options.itemFactory.createType("I"),
+                        options.itemFactory.createType("I")),
+                    options.itemFactory.createString("substring")),
+                false),
+            new CfReturn(ValueType.OBJECT),
+            label9),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode StringMethods_stripTrailing(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        3,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(options.itemFactory.createType("I")),
+                    options.itemFactory.createString("length")),
+                false),
+            new CfStore(ValueType.INT, 1),
+            label1,
+            new CfLoad(ValueType.INT, 1),
+            new CfIf(If.Type.LE, ValueType.INT, label7),
+            label2,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("I"),
+                        options.itemFactory.createType("Ljava/lang/CharSequence;"),
+                        options.itemFactory.createType("I")),
+                    options.itemFactory.createString("codePointBefore")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label3,
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Z"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("isWhitespace")),
+                false),
+            new CfIf(If.Type.NE, ValueType.INT, label5),
+            label4,
+            new CfGoto(label7),
+            label5,
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Character;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("I"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("charCount")),
+                false),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 1),
+            label6,
+            new CfGoto(label1),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfConstNumber(0, ValueType.INT),
+            new CfLoad(ValueType.INT, 1),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/String;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/String;"),
+                        options.itemFactory.createType("I"),
+                        options.itemFactory.createType("I")),
+                    options.itemFactory.createString("substring")),
+                false),
+            new CfReturn(ValueType.OBJECT),
+            label8),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 04dd590..527874a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -9,6 +9,7 @@
 import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isTypeVisibleFromContext;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
@@ -83,7 +84,6 @@
 import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
 import com.android.tools.r8.shaking.AppInfoWithLiveness.EnumValueInfo;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
 import com.android.tools.r8.utils.InternalOutputMode;
 import com.android.tools.r8.utils.LongInterval;
 import com.android.tools.r8.utils.SetUtils;
@@ -1320,7 +1320,7 @@
    */
   public void processAssertions(
       AppView<?> appView, DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
-    assert appView.options().assertionProcessing != AssertionProcessing.LEAVE;
+    assert appView.options().assertionTransformation != AssertionTransformation.PASSTHROUGH;
     DexEncodedMethod clinit;
     // If the <clinit> of this class did not have have code to turn on assertions don't try to
     // remove assertion code from the method (including <clinit> itself.
@@ -1356,7 +1356,9 @@
         if (staticGet.getField().name == dexItemFactory.assertionsDisabled) {
           iterator.replaceCurrentInstruction(
               code.createIntConstant(
-                  appView.options().assertionProcessing == AssertionProcessing.REMOVE ? 1 : 0));
+                  appView.options().assertionTransformation == AssertionTransformation.DISABLE
+                      ? 1
+                      : 0));
         }
       }
     }
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 6aee26c..1600057 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -113,7 +113,9 @@
     }
     String markerString = marker.toString();
     for (DexProgramClass clazz : application.classes()) {
-      if (clazz.getSynthesizedFrom().isEmpty() || options.isDesugaredLibraryCompilation()) {
+      if (clazz.getSynthesizedFrom().isEmpty()
+          || options.isDesugaredLibraryCompilation()
+          || options.enableCfInterfaceMethodDesugaring) {
         writeClass(clazz, consumer, markerString);
       } else {
         throw new Unimplemented("No support for synthetics in the Java bytecode backend.");
@@ -201,8 +203,11 @@
       // In this case bridges have been introduced for the Cf back-end,
       // which do not have class file version.
       assert options.testing.enableForceNestBasedAccessDesugaringForTest
-          || options.isDesugaredLibraryCompilation();
-      return 0;
+          || options.isDesugaredLibraryCompilation()
+          || options.enableCfInterfaceMethodDesugaring;
+      // TODO(b/146424042): We may call static methods on interface classes so we have to go for
+      //  version 52.
+      return options.enableCfInterfaceMethodDesugaring ? 52 : 0;
     }
     return method.getClassFileVersion();
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index 73ab96a..1b34adf 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -11,8 +11,10 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import java.util.Map;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -37,12 +39,30 @@
   public final Intrinsics intrinsics;
   public final Metadata metadata;
 
+  final Map<DexType, DexType> knownTypeConversion;
+
   public Kotlin(DexItemFactory factory) {
     this.factory = factory;
 
     this.functional = new Functional();
     this.intrinsics = new Intrinsics();
     this.metadata = new Metadata();
+
+    this.knownTypeConversion =
+        ImmutableMap.<DexType, DexType>builder()
+            // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/index.html
+            .put(factory.booleanType, factory.createType(addKotlinPrefix("Boolean;")))
+            .put(factory.byteType, factory.createType(addKotlinPrefix("Byte;")))
+            .put(factory.charType, factory.createType(addKotlinPrefix("Character;")))
+            .put(factory.shortType, factory.createType(addKotlinPrefix("Short;")))
+            .put(factory.intType, factory.createType(addKotlinPrefix("Int;")))
+            .put(factory.longType, factory.createType(addKotlinPrefix("Long;")))
+            .put(factory.floatType, factory.createType(addKotlinPrefix("Float;")))
+            .put(factory.doubleType, factory.createType(addKotlinPrefix("Double;")))
+            .put(factory.voidType, factory.createType(addKotlinPrefix("Unit;")))
+            .put(factory.stringType, factory.createType(addKotlinPrefix("String;")))
+            // TODO(b/70169921): Collections?
+            .build();
   }
 
   public final class Functional {
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 9f77f7a..e76bbcf 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -4,15 +4,21 @@
 
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toKmType;
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunction;
 import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmType;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.List;
 import kotlinx.metadata.KmClass;
+import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmProperty;
 import kotlinx.metadata.KmType;
 import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -50,9 +56,23 @@
       }
     }
     assert clazz.superType != null;
-    KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, appView, lens);
-    if (kmTypeForSupertype != null) {
-      superTypes.add(kmTypeForSupertype);
+    if (clazz.superType != appView.dexItemFactory().objectType) {
+      KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, appView, lens);
+      if (kmTypeForSupertype != null) {
+        superTypes.add(kmTypeForSupertype);
+      }
+    } else if (clazz.isInterface()) {
+      superTypes.add(toKmType(addKotlinPrefix("Any;")));
+    }
+
+    List<KmFunction> functions = kmClass.getFunctions();
+    functions.clear();
+    List<KmProperty> properties = kmClass.getProperties();
+    for (DexEncodedMethod method : clazz.kotlinFunctions(properties)) {
+      KmFunction kmFunction = toRenamedKmFunction(method.method, appView, lens);
+      if (kmFunction != null) {
+        functions.add(kmFunction);
+      }
     }
   }
 
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 4400273..0e195e7 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -3,17 +3,44 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
+import static com.android.tools.r8.utils.DescriptorUtils.descriptorToInternalName;
+import static kotlinx.metadata.FlagsKt.flagsOf;
+
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexClass;
+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.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.List;
+import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmValueParameter;
 
 class KotlinMetadataSynthesizer {
+  static KmType toKmType(String descriptor) {
+    KmType kmType = new KmType(flagsOf());
+    kmType.visitClass(descriptorToInternalName(descriptor));
+    return kmType;
+  }
+
   static KmType toRenamedKmType(
       DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+    // E.g., [Ljava/lang/String; -> Lkotlin/Array;
+    if (type.isArrayType()) {
+      return toKmType(addKotlinPrefix("Array;"));
+    }
+    // E.g., void -> Lkotlin/Unit;
+    if (appView.dexItemFactory().kotlin.knownTypeConversion.containsKey(type)) {
+      KmType kmType = new KmType(flagsOf());
+      DexType convertedType = appView.dexItemFactory().kotlin.knownTypeConversion.get(type);
+      assert convertedType != null;
+      kmType.visitClass(descriptorToInternalName(convertedType.toDescriptorString()));
+      return kmType;
+    }
     DexClass clazz = appView.definitionFor(type);
     if (clazz == null) {
       return null;
@@ -27,9 +54,48 @@
     // For library or classpath class, we should not have renamed it.
     assert clazz.isProgramClass() || renamedType == type
         : type.toSourceString() + " -> " + renamedType.toSourceString();
-    // TODO(b/70169921): Consult kotlinx.metadata.Flag for kotlin-specific flags (e.g., sealed).
-    KmType kmType = new KmType(clazz.accessFlags.getAsCfAccessFlags());
-    kmType.visitClass(DescriptorUtils.descriptorToInternalName(renamedType.toDescriptorString()));
+    // TODO(b/70169921): Mysterious, why attempts to properly set flags bothers kotlinc?
+    //   and/or why wiping out flags works for KmType but not KmFunction?!
+    KmType kmType = new KmType(flagsOf());
+    kmType.visitClass(descriptorToInternalName(renamedType.toDescriptorString()));
     return kmType;
   }
+
+  static KmFunction toRenamedKmFunction(
+      DexMethod method, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+    DexEncodedMethod encodedMethod = appView.definitionFor(method);
+    if (encodedMethod == null) {
+      return null;
+    }
+    // For library overrides, synthesize @Metadata always.
+    // For regular methods, make sure it is live.
+    if (!encodedMethod.isLibraryMethodOverride().isTrue()
+        && !appView.appInfo().liveMethods.contains(method)) {
+      return null;
+    }
+    DexMethod renamedMethod = lens.lookupMethod(method, appView.dexItemFactory());
+    // For a library method override, we should not have renamed it.
+    assert !encodedMethod.isLibraryMethodOverride().isTrue() || renamedMethod == method
+        : method.toSourceString() + " -> " + renamedMethod.toSourceString();
+    // TODO(b/70169921): Consult kotlinx.metadata.Flag.Function for kind (e.g., suspend).
+    KmFunction kmFunction =
+        new KmFunction(encodedMethod.accessFlags.getAsKotlinFlags(), renamedMethod.name.toString());
+    KmType kmReturnType = toRenamedKmType(method.proto.returnType, appView, lens);
+    assert kmReturnType != null;
+    kmFunction.setReturnType(kmReturnType);
+    List<KmValueParameter> parameters = kmFunction.getValueParameters();
+    for (int i = 0; i < method.proto.parameters.values.length; i++) {
+      DexType paramType = method.proto.parameters.values[i];
+      DebugLocalInfo debugLocalInfo = encodedMethod.getParameterInfo().get(i);
+      String parameterName =
+          debugLocalInfo != null ? debugLocalInfo.name.toString() : ("p" + i);
+      // TODO(b/70169921): Consult kotlinx.metadata.Flag.ValueParameter.
+      KmValueParameter kmValueParameter = new KmValueParameter(flagsOf(), parameterName);
+      KmType kmParamType = toRenamedKmType(paramType, appView, lens);
+      assert kmParamType != null;
+      kmValueParameter.setType(kmParamType);
+      parameters.add(kmValueParameter);
+    }
+    return kmFunction;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 4778c35..ab9e92f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -214,8 +214,14 @@
       do {
         skipWhitespace();
       } while (parseOption());
+      // This may be unknown, but we want to always ensure that we don't attribute lines to the
+      // wrong configuration.
+      configurationBuilder.addParsedConfiguration(
+          "# The proguard configuration file for the following section is " + origin.toString());
+
       // Collect the parsed configuration.
       configurationBuilder.addParsedConfiguration(contents.substring(positionAfterInclude));
+      configurationBuilder.addParsedConfiguration("# End of content from " + origin.toString());
     }
 
     private boolean parseOption() throws ProguardRuleParserException {
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 a5f672e..c4815ae 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -5,6 +5,7 @@
 
 import static com.google.common.base.Predicates.not;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.DataResourceConsumer;
@@ -74,15 +75,6 @@
     ON
   }
 
-  public enum AssertionProcessing {
-    /** Leave the conditional javac generated assertion code untouched. */
-    LEAVE,
-    /** Remove the javac generated assertion code completely. */
-    REMOVE,
-    /** Enable the javac generated assertion code unconditionally at compile time. */
-    ENABLE
-  }
-
   public static final int SUPPORTED_CF_MAJOR_VERSION = Opcodes.V11;
   public static final int SUPPORTED_DEX_VERSION =
       AndroidApiLevel.LATEST.getDexVersion().getIntValue();
@@ -225,6 +217,7 @@
   public boolean encodeChecksums = false;
   public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
   public boolean enableSourceDebugExtensionRewriter = false;
+  public boolean enableCfInterfaceMethodDesugaring = false;
 
   public int callGraphLikelySpuriousCallEdgeThreshold = 50;
 
@@ -442,7 +435,7 @@
   public boolean ignoreMissingClasses = false;
   // EXPERIMENTAL flag to get behaviour as close to Proguard as possible.
   public boolean forceProguardCompatibility = false;
-  public AssertionProcessing assertionProcessing = AssertionProcessing.REMOVE;
+  public AssertionTransformation assertionTransformation = null;
   public boolean configurationDebugging = false;
 
   // Don't convert Code objects to IRCode.
@@ -1132,7 +1125,7 @@
     }
     return enableDesugaring
         && interfaceMethodDesugaring == OffOrAuto.Auto
-        && !canUseDefaultAndStaticInterfaceMethods();
+        && (!canUseDefaultAndStaticInterfaceMethods() || enableCfInterfaceMethodDesugaring);
   }
 
   public boolean isStringSwitchConversionEnabled() {
diff --git a/src/main/java/com/android/tools/r8/utils/SegmentTree.java b/src/main/java/com/android/tools/r8/utils/SegmentTree.java
new file mode 100644
index 0000000..1fc95b0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/SegmentTree.java
@@ -0,0 +1,82 @@
+// 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.utils;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Implementation of a discrete segment tree where intervals are specified by their start end and
+ * point. Both points are considered part of the interval.
+ */
+public class SegmentTree<V> {
+
+  private final TreeMap<Integer, V> internalTree = new TreeMap<>();
+  private final boolean allowIntervalOverwrites;
+
+  private int size = 0;
+
+  public SegmentTree(boolean allowIntervalOverwrites) {
+    this.allowIntervalOverwrites = allowIntervalOverwrites;
+  }
+
+  public V find(int point) {
+    Map.Entry<Integer, V> entry = findEntry(point);
+    return entry != null ? entry.getValue() : null;
+  }
+
+  public Map.Entry<Integer, V> findEntry(Integer point) {
+    Map.Entry<Integer, V> kvEntry = internalTree.floorEntry(point);
+    return (kvEntry == null || kvEntry.getValue() == null) ? null : kvEntry;
+  }
+
+  public SegmentTree<V> add(int start, int end, V value) {
+    Map.Entry<Integer, V> existingEndRange = findEntry(end);
+    Box<Integer> removedIntervals = new Box<>(0);
+    boolean removedKeys =
+        internalTree
+            .navigableKeySet()
+            .removeIf(
+                key -> {
+                  if (start < key && key <= end) {
+                    assert allowIntervalOverwrites;
+                    if (internalTree.get(key) != null) {
+                      removedIntervals.set(removedIntervals.get() + 1);
+                    }
+                    return true;
+                  }
+                  return false;
+                });
+    if (existingEndRange != null) {
+      assert allowIntervalOverwrites;
+      if (removedKeys) {
+        // We have counted a removed interval where it was actually just shortened:
+        // I1      |----------------------------------|
+        // I2   |--------|
+        // R    |--------||---------------------------|
+        // I1.start has been removed and counted, but it is actually just moved, so we decrease the
+        // removed counter by 1.
+        removedIntervals.set(removedIntervals.get() - 1);
+      }
+    }
+    internalTree.put(start, value);
+    if (!internalTree.containsKey(end + 1)) {
+      internalTree.put(end + 1, existingEndRange == null ? null : existingEndRange.getValue());
+    }
+    // We only count unique intervals, thus the following is two and not three intervals when
+    // adding I2:
+    // I1      |-------------------------------|
+    // I2              |------------|
+    // R       |------||------------||---------|
+    // However, if the order was reversed, we should remove one from the size count since I1
+    // completely shadows I2.
+    size = (size - removedIntervals.get()) + 1;
+    return this;
+  }
+
+  public int size() {
+    return size;
+  }
+}
diff --git a/src/test/examplesJava11/backport/StringBackportJava11Main.java b/src/test/examplesJava11/backport/StringBackportJava11Main.java
new file mode 100644
index 0000000..6af33fa
--- /dev/null
+++ b/src/test/examplesJava11/backport/StringBackportJava11Main.java
@@ -0,0 +1,131 @@
+package backport;
+
+public final class StringBackportJava11Main {
+  public static void main(String[] args) {
+    testRepeat();
+    testIsBlank();
+    testStrip();
+    testStripLeading();
+    testStripTrailing();
+  }
+
+  private static void testRepeat() {
+    try {
+      throw new AssertionError("hey".repeat(-1));
+    } catch (IllegalArgumentException e) {
+      assertEquals("count is negative: -1", e.getMessage());
+    }
+
+    assertEquals("", "".repeat(0));
+    assertEquals("", "".repeat(1));
+    assertEquals("", "".repeat(2));
+
+    assertEquals("", "hey".repeat(0));
+    assertEquals("hey", "hey".repeat(1));
+    assertEquals("heyhey", "hey".repeat(2));
+    assertEquals("heyheyhey", "hey".repeat(3));
+    assertEquals("heyheyheyhey", "hey".repeat(4));
+  }
+
+  /** Per {@link Character#isWhitespace(int)} */
+  private static final String WHITESPACE = ""
+      // Unicode "Zs" category:
+      + "\u0020"
+      + "\u1680"
+      //+ "\u00A0" Exception per Javadoc
+      + "\u1680"
+      + "\u2000"
+      + "\u2001"
+      + "\u2002"
+      + "\u2003"
+      + "\u2004"
+      + "\u2005"
+      + "\u2006"
+      //+ "\u2007" Exception per Javadoc
+      + "\u2008"
+      + "\u2009"
+      + "\u200A"
+      //+ "\u200F" Exception per Javadoc
+      //+ "\u205F" Not honored on Android 4.0.4
+      + "\u3000"
+      // Unicode "Zl" category:
+      + "\u2028"
+      // Unicode "Zp" category:
+      + "\u2029"
+      // Others:
+      + "\t"
+      + "\n"
+      + "\u000B"
+      + "\f"
+      + "\r"
+      + "\u001C"
+      + "\u001D"
+      + "\u001E"
+      + "\u001F"
+      ;
+
+  public static void testIsBlank() {
+    assertEquals(true, "".isBlank());
+    assertEquals(true, WHITESPACE.isBlank());
+
+    // Android <=4.0.4 does not recognize this as whitespace. Just ensure local consistency.
+    assertEquals(Character.isWhitespace(0x205F), "\u205F".isBlank());
+
+    assertEquals(false, "a".isBlank());
+    assertEquals(false, "å".isBlank());
+    assertEquals(false, "a\u030A".isBlank());
+    assertEquals(false, "\uD83D\uDE00".isBlank());
+    assertEquals(false, (WHITESPACE + "a").isBlank());
+    assertEquals(false, ("a" + WHITESPACE).isBlank());
+  }
+
+  public static void testStrip() {
+    assertEquals("", "".strip());
+    assertEquals("", WHITESPACE.strip());
+    assertEquals("a", "a".strip());
+    assertEquals("a", (WHITESPACE + "a").strip());
+    assertEquals("a", ("a" + WHITESPACE).strip());
+    assertEquals("a", (WHITESPACE + "a" + WHITESPACE).strip());
+    assertEquals("a" + WHITESPACE + "a", ("a" + WHITESPACE + "a").strip());
+    assertEquals("a" + WHITESPACE + "a", (WHITESPACE + "a" + WHITESPACE + "a").strip());
+    assertEquals("a" + WHITESPACE + "a", ("a" + WHITESPACE + "a" + WHITESPACE).strip());
+    assertEquals("a" + WHITESPACE + "a",
+        (WHITESPACE + "a" + WHITESPACE + "a" + WHITESPACE).strip());
+  }
+
+  public static void testStripLeading() {
+    assertEquals("", "".stripLeading());
+    assertEquals("", WHITESPACE.stripLeading());
+    assertEquals("a", "a".stripLeading());
+    assertEquals("a", (WHITESPACE + "a").stripLeading());
+    assertEquals("a" + WHITESPACE, ("a" + WHITESPACE).stripLeading());
+    assertEquals("a" + WHITESPACE, (WHITESPACE + "a" + WHITESPACE).stripLeading());
+    assertEquals("a" + WHITESPACE + "a", ("a" + WHITESPACE + "a").stripLeading());
+    assertEquals("a" + WHITESPACE + "a", (WHITESPACE + "a" + WHITESPACE + "a").stripLeading());
+    assertEquals("a" + WHITESPACE + "a" + WHITESPACE,
+        ("a" + WHITESPACE + "a" + WHITESPACE).stripLeading());
+    assertEquals("a" + WHITESPACE + "a" + WHITESPACE,
+        (WHITESPACE + "a" + WHITESPACE + "a" + WHITESPACE).stripLeading());
+  }
+
+  public static void testStripTrailing() {
+    assertEquals("", "".stripTrailing());
+    assertEquals("", WHITESPACE.stripTrailing());
+    assertEquals("a", "a".stripTrailing());
+    assertEquals(WHITESPACE + "a", (WHITESPACE + "a").stripTrailing());
+    assertEquals("a", ("a" + WHITESPACE).stripTrailing());
+    assertEquals(WHITESPACE + "a", (WHITESPACE + "a" + WHITESPACE).stripTrailing());
+    assertEquals("a" + WHITESPACE + "a", ("a" + WHITESPACE + "a").stripTrailing());
+    assertEquals(WHITESPACE + "a" + WHITESPACE + "a",
+        (WHITESPACE + "a" + WHITESPACE + "a").stripTrailing());
+    assertEquals("a" + WHITESPACE + "a", ("a" + WHITESPACE + "a" + WHITESPACE).stripTrailing());
+    assertEquals(WHITESPACE + "a" + WHITESPACE + "a",
+        (WHITESPACE + "a" + WHITESPACE + "a" + WHITESPACE).stripTrailing());
+  }
+
+  private static void assertEquals(Object expected, Object actual) {
+    if (expected != actual && (expected == null || !expected.equals(actual))) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + '>');
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index e3f52a1..e04a2c1 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -204,6 +204,24 @@
     return self();
   }
 
+  public CR enableRuntimeAssertions(boolean enable) {
+    if (getBackend() == Backend.CF) {
+      if (enable) {
+        enableRuntimeAssertions();
+      }
+    } else {
+      // Assertions cannot be enabled on dex VMs.
+      assert !enable;
+    }
+
+    if (enable) {
+      if (!this.vmArguments.contains("-ea")) {
+        this.vmArguments.add("-ea");
+      }
+    }
+    return self();
+  }
+
   public Path writeToZip() throws IOException {
     Path file = state.getNewTempFolder().resolve("out.zip");
     writeToZip(file);
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 3b368b6..0f613d5 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -21,6 +21,7 @@
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Supplier;
 
 public abstract class TestCompilerBuilder<
@@ -42,7 +43,7 @@
 
   // Default initialized setup. Can be overwritten if needed.
   private boolean useDefaultRuntimeLibrary = true;
-  final List<Path> additionalRunClassPath = new ArrayList<>();
+  private final List<Path> additionalRunClassPath = new ArrayList<>();
   private ProgramConsumer programConsumer;
   private StringConsumer mainDexListConsumer;
   private AndroidApiLevel defaultMinApiLevel = ToolHelper.getMinApiLevelForDexVm();
@@ -279,12 +280,6 @@
     return self();
   }
 
-  @Deprecated
-  public T enableCoreLibraryDesugaring() {
-    // TODO(b/134732760): Use the other API instead.
-    return enableCoreLibraryDesugaring(AndroidApiLevel.B);
-  }
-
   public T enableCoreLibraryDesugaring(AndroidApiLevel minAPILevel) {
     return enableCoreLibraryDesugaring(minAPILevel, null);
   }
@@ -299,4 +294,11 @@
     additionalRunClassPath.addAll(files);
     return self();
   }
+
+  public T addAssertionsConfiguration(
+      Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
+          assertionsConfigurationGenerator) {
+    builder.addAssertionsConfiguration(assertionsConfigurationGenerator);
+    return self();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
index 10d74a8..feda70f 100644
--- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
+++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
@@ -3,8 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar;
 
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.JvmTestBuilder;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.debug.DebugTestBase;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
 import com.android.tools.r8.debug.DebugTestConfig;
@@ -17,25 +24,43 @@
 import java.util.Collections;
 import java.util.function.Function;
 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 DefaultLambdaWithUnderscoreThisTestRunner extends DebugTestBase {
 
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public DefaultLambdaWithUnderscoreThisTestRunner(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
   final Class<?> CLASS = DefaultLambdaWithUnderscoreThisTest.class;
 
   final String EXPECTED = StringUtils
       .lines("stateful(My _this variable foo Another ___this variable)");
 
-  private void runDebugger(DebugTestConfig config) throws Throwable {
+  private void runDebugger(DebugTestConfig config, boolean desugared) throws Throwable {
     MethodReference main = Reference.methodFromMethod(CLASS.getMethod("main", String[].class));
     MethodReference stateful = Reference.methodFromMethod(I.class.getMethod("stateful"));
-    Function<String, Command> checkThis = (String desugarThis) -> conditional((state) ->
-        state.isCfRuntime()
-            ? Collections.singletonList(checkLocal("this"))
-            : ImmutableList.of(
-                checkNoLocal("this"),
-                checkLocal(desugarThis)));
+    Function<String, Command> checkThis =
+        (String desugarThis) ->
+            conditional(
+                (state) ->
+                    !desugared
+                        ? Collections.singletonList(checkLocal("this"))
+                        : ImmutableList.of(checkNoLocal("this"), checkLocal(desugarThis)));
 
-    runDebugTest(config, CLASS,
+    runDebugTest(
+        config,
+        CLASS,
         breakpoint(main, 22),
         run(),
         checkLine(22),
@@ -55,27 +80,53 @@
         checkLine(15),
         // Desugaring will insert '____this' in place of 'this' here.
         checkThis.apply("____this"),
-        checkLocals("_this",  "___this"),
+        checkLocals("_this", "___this"),
         run());
   }
 
   @Test
   public void testJvm() throws Throwable {
+    assumeTrue(parameters.isCfRuntime());
     JvmTestBuilder builder = testForJvm().addTestClasspath();
-    builder.run(CLASS).assertSuccessWithOutput(EXPECTED);
-    runDebugger(builder.debugConfig());
+    builder.run(parameters.getRuntime(), CLASS).assertSuccessWithOutput(EXPECTED);
+    runDebugger(builder.debugConfig(), false);
   }
 
   @Test
   public void testD8() throws Throwable {
-    D8TestCompileResult compileResult = testForD8()
-        .addProgramClassesAndInnerClasses(CLASS)
-        .setMinApiThreshold(AndroidApiLevel.K)
-        .compile();
+    assumeTrue(parameters.isDexRuntime());
+    D8TestCompileResult compileResult =
+        testForD8()
+            .addProgramClassesAndInnerClasses(CLASS)
+            .setMinApiThreshold(AndroidApiLevel.K)
+            .compile();
     compileResult
         // TODO(b/123506120): Add .assertNoMessages()
-        .run(CLASS)
+        .run(parameters.getRuntime(), CLASS)
         .assertSuccessWithOutput(EXPECTED);
-    runDebugger(compileResult.debugConfig());
+    runDebugger(compileResult.debugConfig(), true);
+  }
+
+  @Test
+  public void testR8() throws Throwable {
+    R8FullTestBuilder r8FullTestBuilder =
+        testForR8(parameters.getBackend())
+            .addProgramClassesAndInnerClasses(CLASS)
+            .addKeepAllClassesRule()
+            .noMinification()
+            .setMode(CompilationMode.DEBUG)
+            .addOptionsModification(
+                internalOptions -> {
+                  if (parameters.isCfRuntime()) {
+                    internalOptions.enableDesugaring = true;
+                    internalOptions.enableCfInterfaceMethodDesugaring = true;
+                  }
+                });
+    if (parameters.isDexRuntime()) {
+      r8FullTestBuilder.setMinApiThreshold(AndroidApiLevel.K);
+    }
+    R8TestCompileResult compileResult = r8FullTestBuilder.compile();
+    compileResult.run(parameters.getRuntime(), CLASS).assertSuccessWithOutput(EXPECTED);
+    runDebugger(compileResult.debugConfig(), true);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
new file mode 100644
index 0000000..b7f4b6d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+@RunWith(Parameterized.class)
+public final class StringBackportJava11Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withAllApiLevels()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA11_JAR_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public StringBackportJava11Test(TestParameters parameters) {
+    super(parameters, String.class, TEST_JAR, "backport.StringBackportJava11Main");
+    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+    // an actual API level, migrate these tests to CharacterBackportTest.
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java
new file mode 100644
index 0000000..d4f08fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java
@@ -0,0 +1,264 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dexsplitter.SplitterTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import dalvik.system.PathClassLoader;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FeatureSplitTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+
+  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+  }
+
+  public FeatureSplitTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testTwoFeatures() throws CompilationFailedException, IOException, ExecutionException {
+    CompiledWithFeature compiledWithFeature = new CompiledWithFeature().invoke(this);
+    Path basePath = compiledWithFeature.getBasePath();
+    Path feature1Path = compiledWithFeature.getFeature1Path();
+    Path feature2Path = compiledWithFeature.getFeature2Path();
+    Path desugaredLibrary = compiledWithFeature.getDesugaredLibrary();
+
+    assertKeepThe3StreamMethods(compiledWithFeature.getKeepRules());
+
+    assertClassPresent(basePath, BaseClass.class);
+    assertClassPresent(feature1Path, FeatureClass.class);
+    assertClassPresent(feature2Path, FeatureClass2.class);
+
+    verifyRun(BaseClass.class, basePath, desugaredLibrary, null, "42");
+    verifyRun(FeatureClass.class, basePath, desugaredLibrary, feature1Path, "1");
+    verifyRun(FeatureClass2.class, basePath, desugaredLibrary, feature2Path, "7");
+  }
+
+  private void assertKeepThe3StreamMethods(String keepRules) {
+    // Stream desugaring is not needed >= N.
+    if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
+      return;
+    }
+    // Ensure count, toArray and forEach are kept.
+    assertTrue(
+        keepRules.contains(
+            "-keep class j$.lang.Iterable$-EL {\n"
+                + "    void forEach(java.lang.Iterable, j$.util.function.Consumer);"));
+    assertTrue(
+        keepRules.contains(
+            "-keep class j$.util.stream.Stream {\n"
+                + "    long count();\n"
+                + "    java.lang.Object[] toArray();"));
+  }
+
+  private void assertClassPresent(Path appPath, Class<?> present)
+      throws IOException, ExecutionException {
+    CodeInspector inspector = new CodeInspector(appPath);
+    assertTrue(inspector.clazz(present).isPresent());
+  }
+
+  private void verifyRun(
+      Class<?> toRun,
+      Path basePath,
+      Path desugaredLibrary,
+      Path splitterFeatureDexFile,
+      String expectedResult)
+      throws IOException {
+    ProcessResult result =
+        runFeatureOnArt(
+            toRun, desugaredLibrary, basePath, splitterFeatureDexFile, parameters.getRuntime());
+    assertEquals(result.exitCode, 0);
+    assertEquals(result.stdout, StringUtils.lines(expectedResult));
+  }
+
+  protected ProcessResult runFeatureOnArt(
+      Class toRun,
+      Path desugaredLibrary,
+      Path splitterBaseDexFile,
+      Path splitterFeatureDexFile,
+      TestRuntime runtime)
+      throws IOException {
+    assumeTrue(runtime.isDex());
+    ArtCommandBuilder commandBuilder = new ArtCommandBuilder(runtime.asDex().getVm());
+    commandBuilder.appendClasspath(splitterBaseDexFile.toString());
+    if (desugaredLibrary != null) {
+      commandBuilder.appendClasspath(desugaredLibrary.toString());
+    }
+    commandBuilder.appendProgramArgument(toRun.getName());
+    if (splitterFeatureDexFile != null) {
+      commandBuilder.appendProgramArgument(splitterFeatureDexFile.toString());
+    }
+    commandBuilder.setMainClass(SplitRunner.class.getName());
+    return ToolHelper.runArtRaw(commandBuilder);
+  }
+
+  public interface RunInterface {
+
+    void run();
+  }
+
+  // Base using ForEach.
+  public static class BaseClass implements RunInterface {
+
+    @Override
+    public void run() {
+      ArrayList<Integer> list = new ArrayList<>();
+      list.add(42);
+      list.forEach(System.out::println);
+    }
+  }
+
+  // Feature using count.
+  public static class FeatureClass implements RunInterface {
+
+    @SuppressWarnings("ReplaceInefficientStreamCount")
+    @Override
+    public void run() {
+      ArrayList<Object> list = new ArrayList<>();
+      list.add(new Object());
+      System.out.println(list.stream().count());
+    }
+  }
+
+  // Feature using toArray.
+  public static class FeatureClass2 implements RunInterface {
+
+    @SuppressWarnings("SimplifyStreamApiCallChains")
+    @Override
+    public void run() {
+      ArrayList<Integer> list = new ArrayList<>();
+      list.add(7);
+      System.out.println(list.stream().toArray()[0]);
+    }
+  }
+
+  static class SplitRunner {
+
+    /* We support two different modes:
+     *   - One argument to main:
+     *     Pass in the class to be loaded, must implement RunInterface, run will be called.
+     *   - Two arguments to main:
+     *     Pass in the class to be loaded, must implement RunInterface, run will be called.
+     *     Pass in the feature split that we class load.
+     */
+    public static void main(String[] args) {
+      if (args.length < 1 || args.length > 2) {
+        throw new RuntimeException("Unsupported number of arguments");
+      }
+      String classToRun = args[0];
+      ClassLoader loader = SplitRunner.class.getClassLoader();
+      // In the case where we simulate splits, we pass in the feature as the second argument
+      if (args.length == 2) {
+        try {
+          loader = new PathClassLoader(args[1], SplitRunner.class.getClassLoader());
+        } catch (MalformedURLException e) {
+          throw new RuntimeException("Failed reading input URL");
+        }
+      }
+
+      try {
+        Class<?> aClass = loader.loadClass(classToRun);
+        RunInterface b = (RunInterface) aClass.newInstance();
+        b.run();
+      } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
+        throw new RuntimeException("Failed loading class");
+      }
+    }
+  }
+
+  private class CompiledWithFeature {
+
+    private Path basePath;
+    private Path feature1Path;
+    private Path feature2Path;
+    private Path desugaredLibrary;
+    private String keepRules = "";
+
+    public Path getBasePath() {
+      return basePath;
+    }
+
+    public Path getFeature1Path() {
+      return feature1Path;
+    }
+
+    public Path getFeature2Path() {
+      return feature2Path;
+    }
+
+    public Path getDesugaredLibrary() {
+      return desugaredLibrary;
+    }
+
+    public String getKeepRules() {
+      return keepRules;
+    }
+
+    public CompiledWithFeature invoke(FeatureSplitTest tester)
+        throws IOException, CompilationFailedException {
+
+      basePath = temp.newFile("base.zip").toPath();
+      feature1Path = temp.newFile("feature1.zip").toPath();
+      feature2Path = temp.newFile("feature2.zip").toPath();
+
+      KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+      testForR8(parameters.getBackend())
+          .addProgramClasses(BaseClass.class, RunInterface.class, SplitRunner.class)
+          .setMinApi(parameters.getApiLevel())
+          .addFeatureSplit(
+              builder ->
+                  SplitterTestBase.simpleSplitProvider(
+                      builder, feature1Path, temp, FeatureClass.class))
+          .addFeatureSplit(
+              builder ->
+                  SplitterTestBase.simpleSplitProvider(
+                      builder, feature2Path, temp, FeatureClass2.class))
+          .addKeepAllClassesRule()
+          .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+          .compile()
+          .writeToZip(basePath);
+      // Stream desugaring is not needed >= N.
+      if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
+        return this;
+      }
+      desugaredLibrary =
+          tester.buildDesugaredLibrary(
+              parameters.getApiLevel(), keepRuleConsumer.get(), shrinkDesugaredLibrary);
+      keepRules = keepRuleConsumer.get();
+      return this;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
index 725bf3f..84917f1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
@@ -5,10 +5,12 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.time.chrono.Chronology;
+import java.util.List;
 import java.util.Map;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -17,27 +19,65 @@
 @RunWith(Parameterized.class)
 public class StaticInterfaceMethodTest extends DesugaredLibraryTestBase {
 
-  private final TestParameters parameters;
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("false", "java.util.HashSet");
 
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+
+  @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
   }
 
-  public StaticInterfaceMethodTest(TestParameters parameters) {
+  public StaticInterfaceMethodTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
 
   @Test
-  public void testStaticInterfaceMethods() throws Exception {
+  public void testStaticInterfaceMethodsD8() throws Exception {
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addInnerClasses(StaticInterfaceMethodTest.class)
+          .run(parameters.getRuntime(), Executor.class)
+          .assertSuccessWithOutput(EXPECTED_OUTPUT);
+      return;
+    }
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
         .addInnerClasses(StaticInterfaceMethodTest.class)
         .setMinApi(parameters.getApiLevel())
-        .enableCoreLibraryDesugaring(parameters.getApiLevel())
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
         .run(parameters.getRuntime(), Executor.class)
-        .assertSuccessWithOutput(StringUtils.lines("false", "java.util.HashSet"));
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testStaticInterfaceMethodsR8() throws Exception {
+    // Desugared library tests do not make sense in the Cf to Cf, and the JVM is already tested
+    // in the D8 test. Just return.
+    Assume.assumeFalse(parameters.isCfRuntime());
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForR8(parameters.getBackend())
+        .addKeepMainRule(Executor.class)
+        .addInnerClasses(StaticInterfaceMethodTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
   static class Executor {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/SynchronizedCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/SynchronizedCollectionTest.java
index a9df25b..2ad9b4f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/SynchronizedCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/SynchronizedCollectionTest.java
@@ -5,11 +5,14 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.List;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -18,26 +21,43 @@
 @RunWith(Parameterized.class)
 public class SynchronizedCollectionTest extends DesugaredLibraryTestBase {
 
+  private static final Path INPUT_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "desugaredlib.jar");
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines("[1]", "2", "[2, 3]", "true", "2", "2", "2");
+  private static final String MAIN_CLASS = "desugaredlib.SynchronizedCollectionMain";
+
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
 
-  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+  @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
   public static List<Object[]> data() {
     return buildParameters(
-        BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+        getTestParameters()
+            .withDexRuntimes()
+            .withAllApiLevels()
+            .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+            .build(),
+        BooleanUtils.values());
   }
 
-  public SynchronizedCollectionTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+  public SynchronizedCollectionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
     this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
 
   @Test
-  public void testExecution() throws Exception {
+  public void testExecutionD8() throws Exception {
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addProgramFiles(INPUT_JAR)
+          .run(parameters.getRuntime(), MAIN_CLASS)
+          .assertSuccessWithOutput(EXPECTED_OUTPUT);
+      return;
+    }
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
-        .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "desugaredlib.jar"))
-        .addInnerClasses(SynchronizedCollectionTest.class)
+        .addProgramFiles(INPUT_JAR)
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
@@ -46,7 +66,28 @@
             parameters.getApiLevel(),
             keepRuleConsumer.get(),
             shrinkDesugaredLibrary)
-        .run(parameters.getRuntime(), "desugaredlib.SynchronizedCollectionMain")
-        .assertSuccessWithOutput(StringUtils.lines("[1]", "2", "[2, 3]", "true", "2", "2", "2"));
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testExecutionR8() throws Exception {
+    // Desugared library tests do not make sense in the Cf to Cf, and the JVM is already tested
+    // in the D8 test. Just return.
+    Assume.assumeFalse(parameters.isCfRuntime());
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForR8(parameters.getBackend())
+        .addProgramFiles(INPUT_JAR)
+        .addKeepMainRule(MAIN_CLASS)
+        .setMinApi(parameters.getApiLevel())
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
index fea2bce..1e31022 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -41,12 +41,12 @@
 
 public class SplitterTestBase extends TestBase {
 
-  protected static FeatureSplit simpleSplitProvider(
+  public static FeatureSplit simpleSplitProvider(
       FeatureSplit.Builder builder, Path outputPath, TemporaryFolder temp, Class... classes) {
     return simpleSplitProvider(builder, outputPath, temp, Arrays.asList(classes));
   }
 
-  protected static FeatureSplit simpleSplitProvider(
+  public static FeatureSplit simpleSplitProvider(
       FeatureSplit.Builder builder,
       Path outputPath,
       TemporaryFolder temp,
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/StringMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/StringMethods.java
index 566f6d6..9a2fc52 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/StringMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/StringMethods.java
@@ -35,4 +35,78 @@
     }
     return builder.toString();
   }
+
+  public static String repeat(String receiver, int count) {
+    if (count < 0) {
+      throw new IllegalArgumentException("count is negative: " + count);
+    }
+    int length = receiver.length();
+    if (count == 0 || length == 0) {
+      return "";
+    }
+    if (count == 1) {
+      return receiver;
+    }
+    StringBuilder builder = new StringBuilder(length * count);
+    for (int i = 0; i < count; i++) {
+      builder.append(receiver);
+    }
+    return builder.toString();
+  }
+
+  public static boolean isBlank(String receiver) {
+    for (int i = 0, length = receiver.length(); i < length; ) {
+      int codePoint = receiver.codePointAt(i);
+      if (!Character.isWhitespace(codePoint)) {
+        return false;
+      }
+      i += Character.charCount(codePoint);
+    }
+    return true;
+  }
+
+  public static String strip(String receiver) {
+    int start = 0;
+    int end = receiver.length();
+    while (start < end) {
+      int codePoint = receiver.codePointAt(start);
+      if (!Character.isWhitespace(codePoint)) {
+        break;
+      }
+      start += Character.charCount(codePoint);
+    }
+    while (end > start) {
+      int codePoint = Character.codePointBefore(receiver, end);
+      if (!Character.isWhitespace(codePoint)) {
+        break;
+      }
+      end -= Character.charCount(codePoint);
+    }
+    return receiver.substring(start, end);
+  }
+
+  public static String stripLeading(String receiver) {
+    int start = 0;
+    int end = receiver.length();
+    while (start < end) {
+      int codePoint = receiver.codePointAt(start);
+      if (!Character.isWhitespace(codePoint)) {
+        break;
+      }
+      start += Character.charCount(codePoint);
+    }
+    return receiver.substring(start, end);
+  }
+
+  public static String stripTrailing(String receiver) {
+    int end = receiver.length();
+    while (end > 0) {
+      int codePoint = Character.codePointBefore(receiver, end);
+      if (!Character.isWhitespace(codePoint)) {
+        break;
+      }
+      end -= Character.charCount(codePoint);
+    }
+    return receiver.substring(0, end);
+  }
 }
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 3fedef1..b470bb6 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
@@ -6,16 +6,14 @@
 import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.KmClassSubject;
@@ -85,26 +83,25 @@
           supertype -> supertype.getFinalDescriptor().contains("Itf")));
       assertTrue(superTypes.stream().anyMatch(
           supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
-      // TODO(b/70169921): should not refer to Itf
       List<ClassSubject> parameterTypes = kmClass.getParameterTypesInFunctions();
       assertTrue(parameterTypes.stream().anyMatch(
-          parameterType -> parameterType.getOriginalDescriptor().contains("Itf")));
+          parameterType -> parameterType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
     });
 
     Path libJar = compileResult.writeToZip();
 
     String appFolder = PKG_PREFIX + "/parametertype_app";
-    ProcessResult processResult =
+    Path output =
         kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
             .addClasspathFiles(libJar)
             .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
             .setOutputPath(temp.newFolder().toPath())
-            // TODO(b/70169921): update to just .compile() once fixed.
-            .compileRaw();
-    // TODO(b/70169921): should be able to compile!
-    assertNotEquals(0, processResult.exitCode);
-    assertThat(
-        processResult.stderr,
-        containsString("cannot access class '" + pkg + ".parametertype_lib.Itf'"));
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), pkg + ".parametertype_app.MainKt")
+        .assertSuccessWithOutputLines("Impl::bar", "Program::bar");
   }
 }
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 e7efcae..bcf2181 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
@@ -6,16 +6,14 @@
 import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.KmClassSubject;
@@ -84,27 +82,26 @@
           supertype -> supertype.getFinalDescriptor().contains("Itf")));
       assertTrue(superTypes.stream().anyMatch(
           supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
-      // TODO(b/70169921): should not refer to Itf
       List<ClassSubject> functionReturnTypes = kmClass.getReturnTypesInFunctions();
       assertTrue(functionReturnTypes.stream().anyMatch(
-          returnType -> returnType.getOriginalDescriptor().contains("Itf")));
+          returnType -> returnType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
     });
 
     Path libJar = compileResult.writeToZip();
 
     String appFolder = PKG_PREFIX + "/returntype_app";
-    ProcessResult processResult =
+    Path output =
         kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
             .addClasspathFiles(libJar)
             .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
             .setOutputPath(temp.newFolder().toPath())
-            // TODO(b/70169921): update to just .compile() once fixed.
-            .compileRaw();
-    // TODO(b/70169921): should be able to compile!
-    assertNotEquals(0, processResult.exitCode);
-    assertThat(
-        processResult.stderr,
-        containsString("cannot access class '" + pkg + ".returntype_lib.Itf'"));
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), pkg + ".returntype_app.MainKt")
+        .assertSuccessWithOutputLines("Impl::foo", "Program::foo", "true");
   }
 }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/classpath_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/classpath_app/main.kt
index 3c1585a..83caed2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/classpath_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/classpath_app/main.kt
@@ -4,8 +4,8 @@
 package com.android.tools.r8.kotlin.metadata.classpath_app
 
 import com.android.tools.r8.kotlin.metadata.classpath_lib_ext.Extra
-import com.android.tools.r8.kotlin.metadata.classpath_lib_ext.extension
+import com.android.tools.r8.kotlin.metadata.classpath_lib_ext.fooExt
 
 fun main() {
-  Extra().extension()
-}
\ No newline at end of file
+  Extra().fooExt()
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/classpath_lib_ext/impl.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/classpath_lib_ext/impl.kt
index 975865b..501c583 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/classpath_lib_ext/impl.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/classpath_lib_ext/impl.kt
@@ -13,6 +13,6 @@
 
 class Extra : Impl()
 
-fun Extra.extension() {
+fun Extra.fooExt() {
   foo()
-}
\ No newline at end of file
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_app/main.kt
index 37d446e..fd4ce90 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_app/main.kt
@@ -4,10 +4,9 @@
 package com.android.tools.r8.kotlin.metadata.returntype_app
 
 import com.android.tools.r8.kotlin.metadata.returntype_lib.Impl
-import com.android.tools.r8.kotlin.metadata.returntype_lib.Itf
 
 class ProgramClass : Impl() {
-  override fun foo(): Itf {
+  override fun foo(): Impl {
     super.foo()
     println("Program::foo")
     return this
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java
new file mode 100644
index 0000000..af84f69
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java
@@ -0,0 +1,287 @@
+// 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.rewrite.assertions;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.AssertionsConfiguration;
+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.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.function.Function;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionsConfigurationTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public AssertionsConfigurationTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private void runD8Test(
+      Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
+          assertionsConfigurationBuilder,
+      ThrowingConsumer<CodeInspector, RuntimeException> inspector,
+      List<String> outputLines)
+      throws Exception {
+    testForD8()
+        .addProgramClasses(
+            TestClass.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.Class1.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.Class2.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class)
+        .setMinApi(parameters.getApiLevel())
+        .addAssertionsConfiguration(assertionsConfigurationBuilder)
+        .compile()
+        .inspect(inspector)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(StringUtils.lines(outputLines));
+  }
+
+  public void runR8Test(
+      Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
+          assertionsConfigurationBuilder,
+      ThrowingConsumer<CodeInspector, RuntimeException> inspector,
+      List<String> outputLines)
+      throws Exception {
+    runR8Test(assertionsConfigurationBuilder, inspector, outputLines, false);
+  }
+
+  public void runR8Test(
+      Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
+          assertionsConfigurationBuilder,
+      ThrowingConsumer<CodeInspector, RuntimeException> inspector,
+      List<String> outputLines,
+      boolean enableJvmAssertions)
+      throws Exception {
+
+    testForR8(parameters.getBackend())
+        .addProgramClasses(
+            TestClass.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.Class1.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.Class2.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class)
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassAndMembersRules(
+            com.android.tools.r8.rewrite.assertions.testclasses.Class1.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.Class2.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class,
+            com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class)
+        .setMinApi(parameters.getApiLevel())
+        .addAssertionsConfiguration(assertionsConfigurationBuilder)
+        .compile()
+        .inspect(inspector)
+        .enableRuntimeAssertions(enableJvmAssertions)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(StringUtils.lines(outputLines));
+  }
+
+  private AssertionsConfiguration enableAllAssertions(AssertionsConfiguration.Builder builder) {
+    return builder.enable().build();
+  }
+
+  private AssertionsConfiguration disableAllAssertions(AssertionsConfiguration.Builder builder) {
+    return builder.disable().build();
+  }
+
+  private AssertionsConfiguration leaveAllAssertions(AssertionsConfiguration.Builder builder) {
+    return builder.passthrough().build();
+  }
+
+  private List<String> allAssertionsExpectedLines() {
+    return ImmutableList.of(
+        "AssertionError in testclasses.Class1",
+        "AssertionError in testclasses.Class2",
+        "AssertionError in testclasses.subpackage.Class1",
+        "AssertionError in testclasses.subpackage.Class2",
+        "DONE");
+  }
+
+  private List<String> noAllAssertionsExpectedLines() {
+    return ImmutableList.of("DONE");
+  }
+
+  private void checkAssertionCodeRemoved(CodeInspector inspector, Class<?> clazz) {
+    ClassSubject subject = inspector.clazz(clazz);
+    assertThat(subject, isPresent());
+    // <clinit> is removed by R8 as it becomes empty.
+    if (subject.uniqueMethodWithName("<clinit>").isPresent()) {
+      assertFalse(
+          subject
+              .uniqueMethodWithName("<clinit>")
+              .streamInstructions()
+              .anyMatch(InstructionSubject::isStaticPut));
+    }
+    assertFalse(
+        subject
+            .uniqueMethodWithName("m")
+            .streamInstructions()
+            .anyMatch(InstructionSubject::isThrow));
+  }
+
+  private void checkAssertionCodeEnabled(CodeInspector inspector, Class<?> clazz) {
+    ClassSubject subject = inspector.clazz(clazz);
+    assertThat(subject, isPresent());
+    // <clinit> is removed by R8.
+    if (subject.uniqueMethodWithName("<clinit>").isPresent()) {
+      assertFalse(
+          subject
+              .uniqueMethodWithName("<clinit>")
+              .streamInstructions()
+              .anyMatch(InstructionSubject::isStaticPut));
+    }
+    assertTrue(
+        subject
+            .uniqueMethodWithName("m")
+            .streamInstructions()
+            .anyMatch(InstructionSubject::isThrow));
+  }
+
+  private void checkAssertionCodeLeft(CodeInspector inspector, Class<?> clazz) {
+    ClassSubject subject = inspector.clazz(clazz);
+    assertThat(subject, isPresent());
+    assertTrue(
+        subject
+            .uniqueMethodWithName("<clinit>")
+            .streamInstructions()
+            .anyMatch(InstructionSubject::isStaticPut));
+    assertTrue(
+        subject
+            .uniqueMethodWithName("m")
+            .streamInstructions()
+            .anyMatch(InstructionSubject::isThrow));
+  }
+
+  private void checkAssertionCodeRemoved(CodeInspector inspector) {
+    checkAssertionCodeRemoved(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class1.class);
+    checkAssertionCodeRemoved(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class2.class);
+    checkAssertionCodeRemoved(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class);
+    checkAssertionCodeRemoved(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class);
+  }
+
+  private void checkAssertionCodeEnabled(CodeInspector inspector) {
+    checkAssertionCodeEnabled(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class1.class);
+    checkAssertionCodeEnabled(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class2.class);
+    checkAssertionCodeEnabled(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class);
+    checkAssertionCodeEnabled(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class);
+  }
+
+  private void checkAssertionCodeLeft(CodeInspector inspector) {
+    checkAssertionCodeLeft(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class1.class);
+    checkAssertionCodeLeft(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class2.class);
+    checkAssertionCodeLeft(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class);
+    checkAssertionCodeLeft(
+        inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class);
+  }
+
+  @Test
+  public void testEnableAllAssertionsForDex() throws Exception {
+    Assume.assumeTrue(parameters.isDexRuntime());
+    // Leaving assertions in or disabling them on Dalvik/Art means no assertions.
+    runD8Test(
+        this::leaveAllAssertions, this::checkAssertionCodeLeft, noAllAssertionsExpectedLines());
+    runR8Test(
+        this::leaveAllAssertions, this::checkAssertionCodeLeft, noAllAssertionsExpectedLines());
+    runD8Test(
+        this::disableAllAssertions,
+        this::checkAssertionCodeRemoved,
+        noAllAssertionsExpectedLines());
+    runR8Test(
+        this::disableAllAssertions,
+        this::checkAssertionCodeRemoved,
+        noAllAssertionsExpectedLines());
+    // Compile time enabling assertions gives assertions on Dalvik/Art.
+    runD8Test(
+        this::enableAllAssertions, this::checkAssertionCodeEnabled, allAssertionsExpectedLines());
+    runR8Test(
+        this::enableAllAssertions, this::checkAssertionCodeEnabled, allAssertionsExpectedLines());
+  }
+
+  @Test
+  public void testAssertionsForCf() throws Exception {
+    Assume.assumeTrue(parameters.isCfRuntime());
+    // Leaving assertion code means assertions are controlled by the -ea flag.
+    runR8Test(
+        this::leaveAllAssertions, this::checkAssertionCodeLeft, noAllAssertionsExpectedLines());
+    runR8Test(
+        this::leaveAllAssertions, this::checkAssertionCodeLeft, allAssertionsExpectedLines(), true);
+    // Compile time enabling or disabling assertions means the -ea flag has no effect.
+    runR8Test(
+        this::enableAllAssertions, this::checkAssertionCodeEnabled, allAssertionsExpectedLines());
+    runR8Test(
+        this::enableAllAssertions,
+        this::checkAssertionCodeEnabled,
+        allAssertionsExpectedLines(),
+        true);
+    runR8Test(
+        this::disableAllAssertions,
+        this::checkAssertionCodeRemoved,
+        noAllAssertionsExpectedLines());
+    runR8Test(
+        this::disableAllAssertions,
+        this::checkAssertionCodeRemoved,
+        noAllAssertionsExpectedLines(),
+        true);
+  }
+
+  static class TestClass {
+    public static void main(String[] args) {
+      try {
+        com.android.tools.r8.rewrite.assertions.testclasses.Class1.m();
+      } catch (AssertionError e) {
+        System.out.println("AssertionError in testclasses.Class1");
+      }
+      try {
+        com.android.tools.r8.rewrite.assertions.testclasses.Class2.m();
+      } catch (AssertionError e) {
+        System.out.println("AssertionError in testclasses.Class2");
+      }
+      try {
+        com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.m();
+      } catch (AssertionError e) {
+        System.out.println("AssertionError in testclasses.subpackage.Class1");
+      }
+      try {
+        com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.m();
+      } catch (AssertionError e) {
+        System.out.println("AssertionError in testclasses.subpackage.Class2");
+      }
+      System.out.println("DONE");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
index 298134a..446821a 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.R8TestCompileResult;
@@ -18,7 +19,6 @@
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -192,14 +192,14 @@
         .compile();
   }
 
-  private static R8TestCompileResult compileCf(InternalOptions.AssertionProcessing assertionsState)
+  private static R8TestCompileResult compileCf(AssertionTransformation transformation)
       throws CompilationFailedException {
     return testForR8(staticTemp, Backend.CF)
         .addProgramClasses(ClassWithAssertions.class)
         .debug()
         .noTreeShaking()
         .noMinification()
-        .addOptionsModification(o -> o.assertionProcessing = assertionsState)
+        .addOptionsModification(o -> o.assertionTransformation = transformation)
         .compile();
   }
 
@@ -232,9 +232,9 @@
     if (backend == Backend.CF) {
       return new CompilationResults(
           withAccess,
-          compileCf(AssertionProcessing.LEAVE),
-          compileCf(AssertionProcessing.REMOVE),
-          compileCf(AssertionProcessing.ENABLE));
+          compileCf(AssertionTransformation.PASSTHROUGH),
+          compileCf(AssertionTransformation.DISABLE),
+          compileCf(AssertionTransformation.ENABLE));
     }
     return new CompilationResults(
         withAccess,
@@ -331,18 +331,18 @@
     checkResultWithChromiumAssertions(results.withAssertions);
   }
 
-  private D8TestCompileResult compileD8(InternalOptions.AssertionProcessing assertionsState)
+  private D8TestCompileResult compileD8(AssertionTransformation transformation)
       throws CompilationFailedException {
     return testForD8()
         .addProgramClasses(ClassWithAssertions.class)
         .debug()
         .setMinApi(AndroidApiLevel.B)
-        .addOptionsModification(o -> o.assertionProcessing = assertionsState)
+        .addOptionsModification(o -> o.assertionTransformation = transformation)
         .compile();
   }
 
-  private D8TestCompileResult compileR8FollowedByD8(
-      InternalOptions.AssertionProcessing assertionsState) throws Exception {
+  private D8TestCompileResult compileR8FollowedByD8(AssertionTransformation transformation)
+      throws Exception {
     Path program =
         testForR8(Backend.CF)
             .addProgramClasses(ClassWithAssertions.class)
@@ -357,16 +357,16 @@
         .addProgramFiles(program)
         .debug()
         .setMinApi(AndroidApiLevel.B)
-        .addOptionsModification(o -> o.assertionProcessing = assertionsState)
+        .addOptionsModification(o -> o.assertionTransformation = transformation)
         .compile();
   }
 
   @Test
   public void testD8() throws Exception {
     assumeTrue(parameters.isDexRuntime());
-    checkResultWithAssertionsInactive(compileD8(AssertionProcessing.REMOVE));
-    checkResultWithAssertionsInactive(compileD8(AssertionProcessing.LEAVE));
-    checkResultWithAssertionsEnabledAtCompileTime(compileD8(AssertionProcessing.ENABLE));
+    checkResultWithAssertionsInactive(compileD8(AssertionTransformation.DISABLE));
+    checkResultWithAssertionsInactive(compileD8(AssertionTransformation.PASSTHROUGH));
+    checkResultWithAssertionsEnabledAtCompileTime(compileD8(AssertionTransformation.ENABLE));
   }
 
   private D8TestCompileResult compileD8Regress110887293(Function<byte[], byte[]> rewriter)
@@ -390,9 +390,9 @@
   @Test
   public void testR8FollowedByD8() throws Exception {
     assumeTrue(parameters.isDexRuntime());
-    checkResultWithAssertionsInactive(compileR8FollowedByD8(AssertionProcessing.REMOVE));
-    checkResultWithAssertionsInactive(compileR8FollowedByD8(AssertionProcessing.LEAVE));
+    checkResultWithAssertionsInactive(compileR8FollowedByD8(AssertionTransformation.DISABLE));
+    checkResultWithAssertionsInactive(compileR8FollowedByD8(AssertionTransformation.PASSTHROUGH));
     checkResultWithAssertionsEnabledAtCompileTime(
-        compileR8FollowedByD8(AssertionProcessing.ENABLE));
+        compileR8FollowedByD8(AssertionTransformation.ENABLE));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class1.java b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class1.java
new file mode 100644
index 0000000..9de87c9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class1.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.assertions.testclasses;
+
+public class Class1 {
+  public static void m() {
+    assert false;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class2.java b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class2.java
new file mode 100644
index 0000000..93b24e7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class2.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.assertions.testclasses;
+
+public class Class2 {
+  public static void m() {
+    assert false;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class1.java b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class1.java
new file mode 100644
index 0000000..79d0949
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class1.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.assertions.testclasses.subpackage;
+
+public class Class1 {
+  public static void m() {
+    assert false;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class2.java b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class2.java
new file mode 100644
index 0000000..e001cf0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class2.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.assertions.testclasses.subpackage;
+
+public class Class2 {
+  public static void m() {
+    assert false;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java b/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java
index 4ea48cb..3fc3434 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java
@@ -17,6 +17,7 @@
 import com.google.common.base.Charsets;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
+import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -52,7 +53,22 @@
         .addProgramClasses(PrintConfigurationTestClass.class)
         .addKeepRules(proguardConfig)
         .compile();
-    assertEquals(proguardConfig, FileUtils.readTextFile(printConfigurationFile, Charsets.UTF_8));
+    assertEqualsStripOrigin(
+        proguardConfig, FileUtils.readTextFile(printConfigurationFile, Charsets.UTF_8));
+  }
+
+  private String removeOriginComments(String s) {
+    return StringUtils.lines(
+        StringUtils.splitLines(s).stream()
+            .filter(line -> !line.startsWith("# The proguard"))
+            .filter(line -> !line.startsWith("# End of content"))
+            .filter(line -> !line.equals(""))
+            .collect(Collectors.toList()));
+  }
+
+  private void assertEqualsStripOrigin(String a, String b) {
+    String expected = removeOriginComments(a);
+    assertEquals(expected, removeOriginComments(b));
   }
 
   @Test
@@ -72,7 +88,12 @@
         .addKeepRuleFiles(proguardConfigFile)
         .compile();
 
-    assertEquals(proguardConfig, FileUtils.readTextFile(proguardConfigOutFile, Charsets.UTF_8));
+    String outFileContent = FileUtils.readTextFile(proguardConfigOutFile, Charsets.UTF_8);
+    assertEqualsStripOrigin(proguardConfig, outFileContent);
+
+    // We should have added the proguard-config.txt file as the origin in the config output
+    String firstLine = StringUtils.splitLines(outFileContent).get(0);
+    assertThat(outFileContent, containsString(proguardConfigFile.toString()));
   }
 
   @Test
@@ -129,6 +150,7 @@
         "-printconfiguration " + printConfigurationFile.toString()
     ));
     compileWithR8(ImmutableList.of(mainClass), proguardConfig);
-    assertEquals(expected, FileUtils.readTextFile(printConfigurationFile, Charsets.UTF_8));
+    assertEqualsStripOrigin(
+        expected, FileUtils.readTextFile(printConfigurationFile, Charsets.UTF_8));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/SegmentTreeTests.java b/src/test/java/com/android/tools/r8/utils/SegmentTreeTests.java
new file mode 100644
index 0000000..bb2ea17
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/SegmentTreeTests.java
@@ -0,0 +1,106 @@
+// 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.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.util.Map;
+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 SegmentTreeTests extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public SegmentTreeTests(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private SegmentTree<Integer> tree(boolean allowOverrides) {
+    return new SegmentTree<>(allowOverrides);
+  }
+
+  @Test
+  public void testInsertSimpleRange() {
+    SegmentTree<Integer> tree = tree(false).add(1, 100, 3);
+    Map.Entry<Integer, Integer> entry = tree.findEntry(50);
+    assertEntry(tree, 50, 1, 3);
+    assertNull(tree.find(0));
+    assertNull(tree.find(101));
+  }
+
+  @Test
+  public void testInsertMultipleRanges() {
+    SegmentTree<Integer> tree = tree(false).add(1, 10, 3).add(20, 30, 7).add(31, 100, 9);
+    assertEntry(tree, 10, 1, 3);
+    assertNull(tree.findEntry(11));
+    assertEntry(tree, 20, 20, 7);
+    assertEquals(tree.findEntry(31), tree.findEntry(100));
+    assertNull(tree.find(101));
+  }
+
+  @Test(expected = AssertionError.class)
+  public void testAssertingNoOverrides() {
+    tree(false).add(1, 10, 3).add(7, 2, 4);
+  }
+
+  @Test
+  public void testOverrideLeft() {
+    SegmentTree<Integer> tree = tree(true).add(7, 9, 2).add(6, 7, 1);
+    assertNull(tree.findEntry(5));
+    assertEntry(tree, 7, 6, 1);
+    assertEntry(tree, 9, 8, 2);
+    assertNull(tree.findEntry(10));
+    assertEquals(2, tree.size());
+  }
+
+  @Test
+  public void testOverrideRight() {
+    SegmentTree<Integer> tree = tree(true).add(6, 7, 1).add(7, 9, 2);
+    assertNull(tree.findEntry(5));
+    assertEntry(tree, 6, 6, 1);
+    assertEntry(tree, 8, 7, 2);
+    assertNull(tree.findEntry(10));
+    assertEquals(2, tree.size());
+  }
+
+  @Test
+  public void testOverrideContained() {
+    SegmentTree<Integer> tree = tree(true).add(0, 100, 1).add(25, 75, 2);
+    assertEntry(tree, 24, 0, 1);
+    assertEntry(tree, 75, 25, 2);
+    assertEntry(tree, 100, 76, 1);
+    assertNull(tree.findEntry(101));
+    assertEquals(2, tree.size());
+  }
+
+  @Test
+  public void testOverrideContainer() {
+    SegmentTree<Integer> tree = tree(true).add(25, 75, 2).add(0, 100, 1);
+    assertEntry(tree, 24, 0, 1);
+    assertEntry(tree, 75, 0, 1);
+    assertEntry(tree, 100, 0, 1);
+    assertNull(tree.findEntry(101));
+    assertEquals(1, tree.size());
+  }
+
+  private void assertEntry(
+      SegmentTree<Integer> tree, int index, int expectedStart, int expectedValue) {
+    assertEquals(expectedStart, (int) tree.findEntry(index).getKey());
+    assertEquals(expectedValue, (int) tree.findEntry(index).getValue());
+  }
+}
diff --git a/tools/r8_release.py b/tools/r8_release.py
index bdb8242..a694ae0 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -518,6 +518,10 @@
   print '  --gcs_bucket_path=$BUCKET_PATH \\'
   print '  --port=1480'
   print
+  print 'The path for BUCKET_PATH has to be taken from the admrt info line:'
+  print '  INFO: Stage Available at: ...'
+  print '(without the /bigstore prefix).'
+  print
   print "When the 'redir' server is running use the following commands"
   print 'to retreive the artifact:'
   print
@@ -551,7 +555,7 @@
 
 def admrt(archives, action):
   cmd = [ADMRT, '--archives']
-  cmd.extend(archives)
+  cmd.append(','.join(archives))
   cmd.extend(['--action', action])
   subprocess.check_call(cmd)