Version 1.7.2-dev

Merge commit 'e727ab7eecb212a954e249f3ef138eb6d3581e65' into 1.7
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 9b6281b..0c95e98 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -56,9 +56,8 @@
   acl_sets: "default"
   gitiles: {
     repo: "https://r8.googlesource.com/r8"
-    refs: "regexp:refs/heads/d8.*"
-    refs: "regexp:refs/heads/1.*"
-    refs: "regexp:refs/heads/2.*"
+    # Version branches are named d8-x.y (up until d8-1.5) or just x.y (from 1.6)
+    refs: "regexp:refs/heads/(?:d8-)?[0-9]+\\.[0-9]+"
   }
   triggers: "archive_release"
   triggers: "linux-android-4.0.4_release"
diff --git a/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java b/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
index 4a07bf4..2f03241 100644
--- a/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
+++ b/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
@@ -126,6 +126,7 @@
 
   private static Map<String, String> buildPrefixRewritingForCoreLibCompilationAndroidNPlus() {
     return ImmutableMap.<String, String>builder()
+        .put("j$.time.", "java.time.")
         .put("java.time.", "j$.time.")
         .put("java.util.Desugar", "j$.util.Desugar")
         .build();
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
new file mode 100644
index 0000000..2c63a69
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -0,0 +1,335 @@
+// 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.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.jar.CfApplicationWriter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Sets;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BiPredicate;
+import java.util.function.Predicate;
+
+public class GenerateLintFiles {
+
+  private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
+
+  private final DexItemFactory factory = new DexItemFactory();
+  private final Reporter reporter = new Reporter();
+  private final InternalOptions options = new InternalOptions(factory, reporter);
+
+  private final DesugaredLibraryConfiguration desugaredLibraryConfiguration;
+  private final String outputDirectory;
+
+  private final Set<DexMethod> parallelMethods = Sets.newIdentityHashSet();
+
+  private GenerateLintFiles(String desugarConfigurationPath, String outputDirectory) {
+    this.desugaredLibraryConfiguration =
+        readDesugaredLibraryConfiguration(desugarConfigurationPath);
+    this.outputDirectory =
+        outputDirectory.endsWith("/") ? outputDirectory : outputDirectory + File.separator;
+
+    DexType streamType = factory.createType(factory.createString("Ljava/util/stream/Stream;"));
+    DexMethod parallelMethod =
+        factory.createMethod(
+            factory.collectionType,
+            factory.createProto(streamType),
+            factory.createString("parallelStream"));
+    parallelMethods.add(parallelMethod);
+    for (String typePrefix : new String[] {"Base", "Double", "Int", "Long"}) {
+      streamType =
+          factory.createType(factory.createString("Ljava/util/stream/" + typePrefix + "Stream;"));
+      parallelMethod =
+          factory.createMethod(
+              streamType, factory.createProto(streamType), factory.createString("parallel"));
+      parallelMethods.add(parallelMethod);
+    }
+  }
+
+  private static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
+    String jar = String.format(ANDROID_JAR_PATTERN, apiLevel.getLevel());
+    return Paths.get(jar);
+  }
+
+  private DesugaredLibraryConfiguration readDesugaredLibraryConfiguration(
+      String desugarConfigurationPath) {
+    return new DesugaredLibraryConfigurationParser(
+            factory, reporter, false, AndroidApiLevel.B.getLevel())
+        .parse(StringResource.fromFile(Paths.get(desugarConfigurationPath)));
+  }
+
+  private CfCode buildEmptyThrowingCfCode(DexMethod method) {
+    CfInstruction insn[] = {new CfConstNull(), new CfThrow()};
+    return new CfCode(
+        method.holder,
+        1,
+        method.proto.parameters.size() + 1,
+        Arrays.asList(insn),
+        Collections.emptyList(),
+        Collections.emptyList());
+  }
+
+  private void addMethodsToHeaderJar(
+      DexApplication.Builder builder, DexClass clazz, List<DexEncodedMethod> methods) {
+    if (methods.size() == 0) {
+      return;
+    }
+
+    List<DexEncodedMethod> directMethods = new ArrayList<>();
+    List<DexEncodedMethod> virtualMethods = new ArrayList<>();
+    for (DexEncodedMethod method : methods) {
+      assert method.method.holder == clazz.type;
+      CfCode code = null;
+      if (!method.accessFlags.isAbstract() /*&& !method.accessFlags.isNative()*/) {
+        code = buildEmptyThrowingCfCode(method.method);
+      }
+      DexEncodedMethod throwingMethod =
+          new DexEncodedMethod(
+              method.method,
+              method.accessFlags,
+              DexAnnotationSet.empty(),
+              ParameterAnnotationsList.empty(),
+              code,
+              50);
+      if (method.accessFlags.isStatic()) {
+        directMethods.add(throwingMethod);
+      } else {
+        virtualMethods.add(throwingMethod);
+      }
+    }
+
+    DexEncodedMethod[] directMethodsArray = new DexEncodedMethod[directMethods.size()];
+    DexEncodedMethod[] virtualMethodsArray = new DexEncodedMethod[virtualMethods.size()];
+    directMethods.toArray(directMethodsArray);
+    virtualMethods.toArray(virtualMethodsArray);
+    builder.addProgramClass(
+        new DexProgramClass(
+            clazz.type,
+            null,
+            Origin.unknown(),
+            clazz.accessFlags,
+            clazz.superType,
+            clazz.interfaces,
+            null,
+            null,
+            Collections.emptyList(),
+            null,
+            Collections.emptyList(),
+            DexAnnotationSet.empty(),
+            DexEncodedField.EMPTY_ARRAY,
+            DexEncodedField.EMPTY_ARRAY,
+            directMethodsArray,
+            virtualMethodsArray,
+            false));
+  }
+
+  private Map<DexClass, List<DexEncodedMethod>> collectSupportedMethods(
+      AndroidApiLevel compilationApiLevel, Predicate<DexEncodedMethod> supported)
+      throws IOException, ExecutionException {
+
+    // Read the android.jar for the compilation API level.
+    AndroidApp library =
+        AndroidApp.builder().addLibraryFiles(getAndroidJarPath(compilationApiLevel)).build();
+    DirectMappedDexApplication dexApplication =
+        new ApplicationReader(library, options, new Timing()).read().toDirect();
+
+    // collect all the methods that the library desugar configuration adds support for.
+    Map<DexClass, List<DexEncodedMethod>> supportedMethods = new LinkedHashMap<>();
+    for (DexLibraryClass clazz : dexApplication.libraryClasses()) {
+      String className = clazz.toSourceString();
+      // All the methods with the rewritten prefix are supported.
+      for (String prefix : desugaredLibraryConfiguration.getRewritePrefix().keySet()) {
+        if (clazz.accessFlags.isPublic() && className.startsWith(prefix)) {
+          for (DexEncodedMethod method : clazz.methods()) {
+            if (supported.test(method)) {
+              supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
+            }
+          }
+        }
+      }
+
+      // All retargeted methods are supported.
+      for (DexEncodedMethod method : clazz.methods()) {
+        if (desugaredLibraryConfiguration
+            .getRetargetCoreLibMember()
+            .keySet()
+            .contains(method.method.name)) {
+          if (desugaredLibraryConfiguration
+              .getRetargetCoreLibMember()
+              .get(method.method.name)
+              .containsKey(clazz.type)) {
+            if (supported.test(method)) {
+              supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
+            }
+          }
+        }
+      }
+      // All emulated interfaces methods are supported.
+      if (desugaredLibraryConfiguration.getEmulateLibraryInterface().containsKey(clazz.type)) {
+        for (DexEncodedMethod method : clazz.methods()) {
+          if (supported.test(method)) {
+            supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
+          }
+        }
+      }
+    }
+
+    return supportedMethods;
+  }
+
+  private String lintBaseFileName(
+      AndroidApiLevel compilationApiLevel, AndroidApiLevel minApiLevel) {
+    return "desugared_apis_" + compilationApiLevel.getLevel() + "_" + minApiLevel.getLevel();
+  }
+
+  private Path lintFile(
+      AndroidApiLevel compilationApiLevel, AndroidApiLevel minApiLevel, String extension)
+      throws Exception {
+    Path directory =
+        Paths.get(outputDirectory + "compile_api_level_" + compilationApiLevel.getLevel());
+    Files.createDirectories(directory);
+    return Paths.get(
+        directory
+            + File.separator
+            + lintBaseFileName(compilationApiLevel, minApiLevel)
+            + extension);
+  }
+
+  private void writeLintFiles(
+      AndroidApiLevel compilationApiLevel,
+      AndroidApiLevel minApiLevel,
+      Map<DexClass, List<DexEncodedMethod>> supportedMethods)
+      throws Exception {
+    // Build a plain text file with the desugared APIs.
+    List<String> desugaredApisSignatures = new ArrayList<>();
+
+    DexApplication.Builder builder = DexApplication.builder(options, new Timing());
+    supportedMethods.forEach(
+        (clazz, methods) -> {
+          String classBinaryName =
+              DescriptorUtils.getClassBinaryNameFromDescriptor(clazz.type.descriptor.toString());
+          for (DexEncodedMethod method : methods) {
+            desugaredApisSignatures.add(
+                classBinaryName
+                    + '/'
+                    + method.method.name
+                    + method.method.proto.toDescriptorString());
+          }
+
+          addMethodsToHeaderJar(builder, clazz, methods);
+        });
+    DexApplication app = builder.build();
+
+    // Write a plain text file with the desugared APIs.
+    FileUtils.writeTextFile(
+        lintFile(compilationApiLevel, minApiLevel, ".txt"), desugaredApisSignatures);
+
+    // Write a header jar with the desugared APIs.
+    AppInfo appInfo = new AppInfo(app);
+    AppView<?> appView = AppView.createForD8(appInfo, options);
+    CfApplicationWriter writer =
+        new CfApplicationWriter(
+            builder.build(),
+            appView,
+            options,
+            options.getMarker(Tool.L8),
+            GraphLense.getIdentityLense(),
+            NamingLens.getIdentityLens(),
+            null);
+    ClassFileConsumer consumer =
+        new ClassFileConsumer.ArchiveConsumer(
+            lintFile(compilationApiLevel, minApiLevel, FileUtils.JAR_EXTENSION));
+    writer.write(consumer, ThreadUtils.getExecutorService(options));
+    consumer.finished(options.reporter);
+  }
+
+  private void generateLintFiles(
+      AndroidApiLevel compilationApiLevel,
+      Predicate<AndroidApiLevel> generateForThisMinApiLevel,
+      BiPredicate<AndroidApiLevel, DexEncodedMethod> supportedForMinApiLevel)
+      throws Exception {
+    for (AndroidApiLevel value : AndroidApiLevel.values()) {
+      if (!generateForThisMinApiLevel.test(value)) {
+        continue;
+      }
+
+      Map<DexClass, List<DexEncodedMethod>> supportedMethods =
+          collectSupportedMethods(
+              compilationApiLevel, (method -> supportedForMinApiLevel.test(value, method)));
+      writeLintFiles(compilationApiLevel, value, supportedMethods);
+    }
+  }
+
+  private void run() throws Exception {
+    // Run over all the API levels that the desugared library can be compiled with.
+    for (int apiLevel = AndroidApiLevel.Q.getLevel();
+        apiLevel >= desugaredLibraryConfiguration.getRequiredCompilationApiLevel().getLevel();
+        apiLevel--) {
+      System.out.println("Generating lint files for compile API " + apiLevel);
+      generateLintFiles(
+          AndroidApiLevel.getAndroidApiLevel(apiLevel),
+          minApiLevel -> minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B,
+          (minApiLevel, method) -> {
+            assert minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B;
+            if (minApiLevel == AndroidApiLevel.L) {
+              return true;
+            }
+            assert minApiLevel == AndroidApiLevel.B;
+            return !parallelMethods.contains(method.method);
+          });
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    if (args.length != 2) {
+      System.out.println("Usage: GenerateLineFiles <desuage configuration> <output directory>");
+      System.exit(1);
+    }
+    new GenerateLintFiles(args[0], args[1]).run();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
index 946ac89..94633a0 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -113,7 +113,7 @@
             .append(r8Type("InternalOptions", "utils"))
             .append(" options, ")
             .append(r8Type("DexMethod", "graph"))
-            .append(" method, String name) {");
+            .append(" method) {");
 
     for (CfInstruction instruction : code.getInstructions()) {
       if (instruction instanceof CfLabel) {
@@ -166,7 +166,7 @@
   }
 
   private String longValue(long value) {
-    return (value < Integer.MIN_VALUE || Integer.MAX_VALUE < value) ? (value + "l") : ("" + value);
+    return (value < Integer.MIN_VALUE || Integer.MAX_VALUE < value) ? (value + "L") : ("" + value);
   }
 
   // Ensure a type import for a given type.
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 7b18fd8..02a6129 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -309,17 +309,21 @@
       mainDexFile = new VirtualFile(0, writer.namingLens);
       assert virtualFiles.isEmpty();
       virtualFiles.add(mainDexFile);
-      if (writer.markerStrings != null && !writer.markerStrings.isEmpty()) {
-        for (DexString markerString : writer.markerStrings) {
-          mainDexFile.transaction.addString(markerString);
-        }
-        mainDexFile.commitTransaction();
-      }
+      addMarkers(mainDexFile);
 
       classes = Sets.newHashSet(application.classes());
       originalNames = computeOriginalNameMapping(classes, application.getProguardMap());
     }
 
+    private void addMarkers(VirtualFile virtualFile) {
+      if (writer.markerStrings != null && !writer.markerStrings.isEmpty()) {
+        for (DexString markerString : writer.markerStrings) {
+          virtualFile.transaction.addString(markerString);
+        }
+        virtualFile.commitTransaction();
+      }
+    }
+
     protected void fillForMainDexList(Set<DexProgramClass> classes) {
       if (!application.mainDexList.isEmpty()) {
         VirtualFile mainDexFile = virtualFiles.get(0);
@@ -418,7 +422,10 @@
       for (Map.Entry<FeatureSplit, Set<DexProgramClass>> featureSplitSetEntry :
           featureSplitClasses.entrySet()) {
         // Add a new virtual file, start from index 0 again
-        virtualFiles.add(new VirtualFile(0, writer.namingLens, featureSplitSetEntry.getKey()));
+        VirtualFile featureFile =
+            new VirtualFile(0, writer.namingLens, featureSplitSetEntry.getKey());
+        virtualFiles.add(featureFile);
+        addMarkers(featureFile);
         Set<DexProgramClass> featureClasses =
             sortClassesByPackage(featureSplitSetEntry.getValue(), originalNames);
         filesForDistribution = virtualFiles.subList(virtualFiles.size() - 1, virtualFiles.size());
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
index b7f6158..22e819e 100644
--- a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
@@ -59,7 +59,12 @@
     return result;
   }
 
-  public boolean inSameFeatureOrBase(DexMethod a, DexMethod b) {
+  public boolean isInFeature(DexProgramClass clazz) {
+    return javaTypeToFeatureSplitMapping.containsKey(
+        DescriptorUtils.descriptorToJavaType(clazz.type.toDescriptorString()));
+  }
+
+  public boolean inSameFeatureOrBase(DexMethod a, DexMethod b){
     return inSameFeatureOrBase(a.holder, b.holder);
   }
 
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 3e3ed2b..bc4f6f0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1098,6 +1098,10 @@
       return false;
     }
 
+    public TrivialClassInitializer asTrivialClassInitializer() {
+      return null;
+    }
+
     // Defines instance trivial initialized, see details in comments
     // to CodeRewriter::computeInstanceInitializerInfo(...)
     public static final class TrivialInstanceInitializer extends TrivialInitializer {
@@ -1122,6 +1126,11 @@
       public TrivialClassInitializer(DexField field) {
         this.field = field;
       }
+
+      @Override
+      public TrivialClassInitializer asTrivialClassInitializer() {
+        return this;
+      }
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 36e4cc8..87d7ceb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -161,6 +161,8 @@
   public final DexString toStringMethodName = createString("toString");
   public final DexString internMethodName = createString("intern");
 
+  public final DexString convertMethodName = createString("convert");
+
   public final DexString getClassMethodName = createString("getClass");
   public final DexString finalizeMethodName = createString("finalize");
   public final DexString ordinalMethodName = createString("ordinal");
@@ -214,6 +216,10 @@
   public final DexString proxyDescriptor = createString("Ljava/lang/reflect/Proxy;");
   public final DexString serviceLoaderDescriptor = createString("Ljava/util/ServiceLoader;");
   public final DexString listDescriptor = createString("Ljava/util/List;");
+  public final DexString setDescriptor = createString("Ljava/util/Set;");
+  public final DexString mapDescriptor = createString("Ljava/util/Map;");
+  public final DexString mapEntryDescriptor = createString("Ljava/util/Map$Entry;");
+  public final DexString collectionDescriptor = createString("Ljava/util/Collection;");
   public final DexString comparatorDescriptor = createString("Ljava/util/Comparator;");
   public final DexString callableDescriptor = createString("Ljava/util/concurrent/Callable;");
   public final DexString supplierDescriptor = createString("Ljava/util/function/Supplier;");
@@ -302,6 +308,10 @@
   public final DexType proxyType = createType(proxyDescriptor);
   public final DexType serviceLoaderType = createType(serviceLoaderDescriptor);
   public final DexType listType = createType(listDescriptor);
+  public final DexType setType = createType(setDescriptor);
+  public final DexType mapType = createType(mapDescriptor);
+  public final DexType mapEntryType = createType(mapEntryDescriptor);
+  public final DexType collectionType = createType(collectionDescriptor);
   public final DexType comparatorType = createType(comparatorDescriptor);
   public final DexType callableType = createType(callableDescriptor);
   public final DexType supplierType = createType(supplierDescriptor);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
new file mode 100644
index 0000000..90320b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+import com.android.tools.r8.graph.DexEncodedField;
+
+/**
+ * Implements a lifted subset lattice for fields.
+ *
+ * <p>An element can either be:
+ *
+ * <ol>
+ *   <li>bottom, represented by {@link EmptyFieldSet},
+ *   <li>a (possibly empty) set of fields, represented by {@link ConcreteMutableFieldSet}, or
+ *   <li>top, represented by {@link UnknownFieldSet}.
+ * </ol>
+ *
+ * <p>Note that this class currently does not contain a {@code join()} method. Instead, the {@link
+ * ConcreteMutableFieldSet} has an {@link ConcreteMutableFieldSet#add(DexEncodedField)} and {@link
+ * ConcreteMutableFieldSet#addAll(ConcreteMutableFieldSet)} method. This is intentional, since the
+ * use of {@code join()} could lead to an excessive amount of set copying under the hood.
+ */
+public abstract class AbstractFieldSet {
+
+  public boolean isConcreteFieldSet() {
+    return false;
+  }
+
+  public ConcreteMutableFieldSet asConcreteFieldSet() {
+    return null;
+  }
+
+  public boolean isKnownFieldSet() {
+    return false;
+  }
+
+  public KnownFieldSet asKnownFieldSet() {
+    return null;
+  }
+
+  public abstract boolean contains(DexEncodedField field);
+
+  public boolean isBottom() {
+    return false;
+  }
+
+  public boolean isTop() {
+    return false;
+  }
+
+  public final boolean lessThanOrEqual(AbstractFieldSet other) {
+    if (isBottom() || other.isTop()) {
+      return true;
+    }
+    if (isTop() || other.isBottom()) {
+      return false;
+    }
+    assert isConcreteFieldSet();
+    assert other.isConcreteFieldSet();
+    return other.asConcreteFieldSet().getFields().containsAll(asConcreteFieldSet().getFields());
+  }
+
+  public final boolean strictlyLessThan(AbstractFieldSet other) {
+    return lessThanOrEqual(other) && !equals(other);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
new file mode 100644
index 0000000..0ac6de7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SetUtils;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Set;
+
+public class ConcreteMutableFieldSet extends AbstractFieldSet implements KnownFieldSet {
+
+  private final Set<DexEncodedField> fields;
+
+  public ConcreteMutableFieldSet() {
+    fields = Sets.newIdentityHashSet();
+  }
+
+  public ConcreteMutableFieldSet(DexEncodedField field) {
+    fields = SetUtils.newIdentityHashSet(field);
+  }
+
+  public void add(DexEncodedField field) {
+    fields.add(field);
+  }
+
+  public void addAll(ConcreteMutableFieldSet other) {
+    fields.addAll(other.fields);
+  }
+
+  Set<DexEncodedField> getFields() {
+    if (InternalOptions.assertionsEnabled()) {
+      return Collections.unmodifiableSet(fields);
+    }
+    return fields;
+  }
+
+  @Override
+  public boolean isConcreteFieldSet() {
+    return true;
+  }
+
+  @Override
+  public ConcreteMutableFieldSet asConcreteFieldSet() {
+    return this;
+  }
+
+  @Override
+  public boolean isKnownFieldSet() {
+    return true;
+  }
+
+  @Override
+  public ConcreteMutableFieldSet asKnownFieldSet() {
+    return this;
+  }
+
+  @Override
+  public boolean contains(DexEncodedField field) {
+    return fields.contains(field);
+  }
+
+  @Override
+  public int size() {
+    return fields.size();
+  }
+
+  @Override
+  public int hashCode() {
+    return fields.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other == null) {
+      return false;
+    }
+    if (other.getClass() != getClass()) {
+      return false;
+    }
+    ConcreteMutableFieldSet concreteFieldSet = (ConcreteMutableFieldSet) other;
+    return fields.equals(concreteFieldSet.fields);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
new file mode 100644
index 0000000..c50ded8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+import com.android.tools.r8.graph.DexEncodedField;
+
+public class EmptyFieldSet extends AbstractFieldSet implements KnownFieldSet {
+
+  private static final EmptyFieldSet INSTANCE = new EmptyFieldSet();
+
+  private EmptyFieldSet() {}
+
+  public static EmptyFieldSet getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isKnownFieldSet() {
+    return true;
+  }
+
+  @Override
+  public EmptyFieldSet asKnownFieldSet() {
+    return this;
+  }
+
+  @Override
+  public boolean contains(DexEncodedField field) {
+    return false;
+  }
+
+  @Override
+  public boolean isBottom() {
+    return true;
+  }
+
+  @Override
+  public int size() {
+    return 0;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
new file mode 100644
index 0000000..2d67908
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -0,0 +1,253 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.DominatorTree;
+import com.android.tools.r8.ir.code.DominatorTree.Assumption;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DequeUtils;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class FieldValueAnalysis {
+
+  private final AppView<AppInfoWithLiveness> appView;
+  private final IRCode code;
+  private final OptimizationFeedback feedback;
+  private final DexEncodedMethod method;
+
+  private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
+
+  private FieldValueAnalysis(
+      AppView<AppInfoWithLiveness> appView,
+      IRCode code,
+      OptimizationFeedback feedback,
+      DexEncodedMethod method) {
+    this.appView = appView;
+    this.code = code;
+    this.feedback = feedback;
+    this.method = method;
+  }
+
+  public static void run(
+      AppView<?> appView, IRCode code, OptimizationFeedback feedback, DexEncodedMethod method) {
+    if (appView.enableWholeProgramOptimizations() && method.isClassInitializer()) {
+      assert appView.appInfo().hasLiveness();
+      new FieldValueAnalysis(appView.withLiveness(), code, feedback, method)
+          .computeFieldOptimizationInfo();
+    }
+  }
+
+  private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
+    if (fieldsMaybeReadBeforeBlockInclusiveCache == null) {
+      fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive();
+    }
+    return fieldsMaybeReadBeforeBlockInclusiveCache;
+  }
+
+  /** This method analyzes initializers with the purpose of computing field optimization info. */
+  private void computeFieldOptimizationInfo() {
+    AppInfoWithLiveness appInfo = appView.appInfo();
+    DominatorTree dominatorTree = null;
+
+    if (method.isClassInitializer()) {
+      DexType context = method.method.holder;
+
+      // Find all the static-put instructions that assign a field in the enclosing class which is
+      // guaranteed to be assigned only in the current initializer.
+      boolean isStraightLineCode = true;
+      Map<DexEncodedField, LinkedList<StaticPut>> staticPutsPerField = new IdentityHashMap<>();
+      for (Instruction instruction : code.instructions()) {
+        if (instruction.isStaticPut()) {
+          StaticPut staticPut = instruction.asStaticPut();
+          DexField field = staticPut.getField();
+          DexEncodedField encodedField = appInfo.resolveField(field);
+          if (encodedField != null
+              && encodedField.field.holder == context
+              && appInfo.isStaticFieldWrittenOnlyInEnclosingStaticInitializer(encodedField)) {
+            staticPutsPerField
+                .computeIfAbsent(encodedField, ignore -> new LinkedList<>())
+                .add(staticPut);
+          }
+        }
+        if (instruction.isJumpInstruction()) {
+          if (!instruction.isGoto() && !instruction.isReturn()) {
+            isStraightLineCode = false;
+          }
+        }
+      }
+
+      List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
+      for (Entry<DexEncodedField, LinkedList<StaticPut>> entry : staticPutsPerField.entrySet()) {
+        DexEncodedField encodedField = entry.getKey();
+        LinkedList<StaticPut> staticPuts = entry.getValue();
+        if (staticPuts.size() > 1) {
+          continue;
+        }
+        StaticPut staticPut = staticPuts.getFirst();
+        if (!isStraightLineCode) {
+          if (dominatorTree == null) {
+            dominatorTree = new DominatorTree(code, Assumption.NO_UNREACHABLE_BLOCKS);
+          }
+          if (!dominatorTree.dominatesAllOf(staticPut.getBlock(), normalExitBlocks)) {
+            continue;
+          }
+        }
+        if (fieldMaybeReadBeforeInstruction(encodedField, staticPut)) {
+          continue;
+        }
+        updateFieldOptimizationInfo(encodedField, staticPut.value());
+      }
+    }
+  }
+
+  private boolean fieldMaybeReadBeforeInstruction(
+      DexEncodedField encodedField, Instruction instruction) {
+    BasicBlock block = instruction.getBlock();
+
+    // First check if the field may be read in any of the (transitive) predecessor blocks.
+    if (fieldMaybeReadBeforeBlock(encodedField, block)) {
+      return true;
+    }
+
+    // Then check if any of the instructions that precede the given instruction in the current block
+    // may read the field.
+    DexType context = method.method.holder;
+    InstructionIterator instructionIterator = block.iterator();
+    while (instructionIterator.hasNext()) {
+      Instruction current = instructionIterator.next();
+      if (current == instruction) {
+        break;
+      }
+      if (current.readSet(appView, context).contains(encodedField)) {
+        return true;
+      }
+    }
+
+    // Otherwise, the field is not read prior to the given instruction.
+    return false;
+  }
+
+  private boolean fieldMaybeReadBeforeBlock(DexEncodedField encodedField, BasicBlock block) {
+    for (BasicBlock predecessor : block.getPredecessors()) {
+      if (fieldMaybeReadBeforeBlockInclusive(encodedField, predecessor)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean fieldMaybeReadBeforeBlockInclusive(
+      DexEncodedField encodedField, BasicBlock block) {
+    return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(encodedField);
+  }
+
+  /**
+   * Eagerly creates a mapping from each block to the set of fields that may be read in that block
+   * and its transitive predecessors.
+   */
+  private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
+    DexType context = method.method.holder;
+    Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
+    Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
+    while (!worklist.isEmpty()) {
+      BasicBlock block = worklist.removeFirst();
+      boolean seenBefore = result.containsKey(block);
+      AbstractFieldSet readSet =
+          result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance());
+      if (readSet.isTop()) {
+        // We already have unknown information for this block.
+        continue;
+      }
+
+      assert readSet.isKnownFieldSet();
+      KnownFieldSet knownReadSet = readSet.asKnownFieldSet();
+      int oldSize = seenBefore ? knownReadSet.size() : -1;
+
+      // Everything that is read in the predecessor blocks should also be included in the read set
+      // for the current block, so here we join the information from the predecessor blocks into the
+      // current read set.
+      boolean blockOrPredecessorMaybeReadAnyField = false;
+      for (BasicBlock predecessor : block.getPredecessors()) {
+        AbstractFieldSet predecessorReadSet =
+            result.getOrDefault(predecessor, EmptyFieldSet.getInstance());
+        if (predecessorReadSet.isBottom()) {
+          continue;
+        }
+        if (predecessorReadSet.isTop()) {
+          blockOrPredecessorMaybeReadAnyField = true;
+          break;
+        }
+        assert predecessorReadSet.isConcreteFieldSet();
+        if (!knownReadSet.isConcreteFieldSet()) {
+          knownReadSet = new ConcreteMutableFieldSet();
+        }
+        knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet());
+      }
+
+      if (!blockOrPredecessorMaybeReadAnyField) {
+        // Finally, we update the read set with the fields that are read by the instructions in the
+        // current block.
+        for (Instruction instruction : block.getInstructions()) {
+          AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
+          if (instructionReadSet.isBottom()) {
+            continue;
+          }
+          if (instructionReadSet.isTop()) {
+            blockOrPredecessorMaybeReadAnyField = true;
+            break;
+          }
+          if (!knownReadSet.isConcreteFieldSet()) {
+            knownReadSet = new ConcreteMutableFieldSet();
+          }
+          knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet());
+        }
+      }
+
+      boolean changed = false;
+      if (blockOrPredecessorMaybeReadAnyField) {
+        // Record that this block reads all fields.
+        result.put(block, UnknownFieldSet.getInstance());
+        changed = true;
+      } else if (knownReadSet.size() != oldSize) {
+        assert knownReadSet.size() > oldSize;
+        changed = true;
+      }
+
+      if (changed) {
+        // Rerun the analysis for all successors because the state of the current block changed.
+        worklist.addAll(block.getSuccessors());
+      }
+    }
+    return result;
+  }
+
+  private void updateFieldOptimizationInfo(DexEncodedField field, Value value) {
+    TypeLatticeElement fieldType =
+        TypeLatticeElement.fromDexType(field.field.type, Nullability.maybeNull(), appView);
+    TypeLatticeElement valueType = value.getTypeLattice();
+    if (valueType.strictlyLessThan(fieldType, appView)) {
+      feedback.markFieldHasDynamicType(field, valueType);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/KnownFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/KnownFieldSet.java
new file mode 100644
index 0000000..b96918e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/KnownFieldSet.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+public interface KnownFieldSet {
+
+  default boolean isConcreteFieldSet() {
+    return false;
+  }
+
+  default ConcreteMutableFieldSet asConcreteFieldSet() {
+    return null;
+  }
+
+  int size();
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
new file mode 100644
index 0000000..9df87cc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+import com.android.tools.r8.graph.DexEncodedField;
+
+public class UnknownFieldSet extends AbstractFieldSet {
+
+  private static final UnknownFieldSet INSTANCE = new UnknownFieldSet();
+
+  private UnknownFieldSet() {}
+
+  public static UnknownFieldSet getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean contains(DexEncodedField field) {
+    return true;
+  }
+
+  @Override
+  public boolean isTop() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java b/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
new file mode 100644
index 0000000..57f8d2e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.modeling;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import java.util.function.Predicate;
+
+/** Models if a given library method may cause a program field to be read. */
+public class LibraryMethodReadSetModeling {
+
+  public static AbstractFieldSet getModeledReadSetOrUnknown(
+      InvokeMethod invoke, DexItemFactory dexItemFactory) {
+    DexMethod invokedMethod = invoke.getInvokedMethod();
+
+    // Check if it is a library method that does not have side effects. In that case it is safe to
+    // assume that the method does not read any fields, since even if it did, it would not be able
+    // to do anything with the values it read (since we will remove such invocations without side
+    // effects).
+    Predicate<InvokeMethod> noSideEffectsPredicate =
+        dexItemFactory.libraryMethodsWithoutSideEffects.get(invokedMethod);
+    if (noSideEffectsPredicate != null && noSideEffectsPredicate.test(invoke)) {
+      return EmptyFieldSet.getInstance();
+    }
+
+    // Already handled above.
+    assert !dexItemFactory.classMethods.isReflectiveNameLookup(invokedMethod);
+
+    // Modeling of other library methods.
+    DexType holder = invokedMethod.holder;
+    if (holder == dexItemFactory.objectType) {
+      if (invokedMethod == dexItemFactory.objectMethods.constructor) {
+        return EmptyFieldSet.getInstance();
+      }
+    }
+    return UnknownFieldSet.getInstance();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index c4dfbc4..18ba962 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -153,6 +153,10 @@
     return self;
   }
 
+  public boolean mayAffectStaticType() {
+    return isAssumeNonNull();
+  }
+
   @Override
   public boolean couldIntroduceAnAlias(AppView<?> appView, Value root) {
     assert root != null && root.getTypeLattice().isReference();
diff --git a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
index 72c64a0..120a2d4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
@@ -95,9 +95,9 @@
   /**
    * Check if one basic block is dominated by another basic block.
    *
-   * @param subject subject to check for domination by {@code dominator}
+   * @param subject subject to check for domination by {@param dominator}
    * @param dominator dominator to check against
-   * @return wether {@code subject} is dominated by {@code dominator}
+   * @return whether {@param subject} is dominated by {@param dominator}
    */
   public boolean dominatedBy(BasicBlock subject, BasicBlock dominator) {
     assert !obsolete;
@@ -108,11 +108,27 @@
   }
 
   /**
+   * Checks if one basic block dominates a collection of other basic blocks.
+   *
+   * @param dominator dominator to check against
+   * @param subjects subjects to check for domination by {@param dominator}
+   * @return whether {@param subjects} are all dominated by {@param dominator}
+   */
+  public boolean dominatesAllOf(BasicBlock dominator, Iterable<BasicBlock> subjects) {
+    for (BasicBlock subject : subjects) {
+      if (!dominatedBy(subject, dominator)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
    * Check if one basic block is strictly dominated by another basic block.
    *
-   * @param subject subject to check for domination by {@code dominator}
+   * @param subject subject to check for domination by {@param dominator}
    * @param dominator dominator to check against
-   * @return wether {@code subject} is strictly dominated by {@code dominator}
+   * @return whether {@param subject} is strictly dominated by {@param dominator}
    */
   public boolean strictlyDominatedBy(BasicBlock subject, BasicBlock dominator) {
     assert !obsolete;
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index e041d5a..b95376e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -11,6 +11,10 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.AbstractError;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Collections;
 import java.util.List;
@@ -131,4 +135,32 @@
     // TODO(jsjeon): what if the target field is known to be non-null?
     return true;
   }
+
+  @Override
+  public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+    if (instructionMayTriggerMethodInvocation(appView, context)) {
+      // This may trigger class initialization, which could potentially read any field.
+      return UnknownFieldSet.getInstance();
+    }
+
+    if (isFieldGet()) {
+      DexField field = getField();
+      DexEncodedField encodedField = null;
+      if (appView.enableWholeProgramOptimizations()) {
+        encodedField = appView.appInfo().resolveField(field);
+      } else {
+        DexClass clazz = appView.definitionFor(field.holder);
+        if (clazz != null) {
+          encodedField = clazz.lookupField(field);
+        }
+      }
+      if (encodedField != null) {
+        return new ConcreteMutableFieldSet(encodedField);
+      }
+      return UnknownFieldSet.getInstance();
+    }
+
+    assert isFieldPut();
+    return EmptyFieldSet.getInstance();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index b924c4a..cb32015 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -262,7 +262,6 @@
   }
 
   public boolean controlFlowMayDependOnEnvironment(AppView<?> appView) {
-    DexType context = method.method.holder;
     for (BasicBlock block : blocks) {
       if (block.hasCatchHandlers()) {
         // Whether an instruction throws may generally depend on the environment.
@@ -1009,14 +1008,22 @@
   }
 
   public void removeAllTrivialPhis() {
-    removeAllTrivialPhis(null);
+    removeAllTrivialPhis(null, null);
   }
 
   public void removeAllTrivialPhis(IRBuilder builder) {
+    removeAllTrivialPhis(builder, null);
+  }
+
+  public void removeAllTrivialPhis(Set<Value> affectedValues) {
+    removeAllTrivialPhis(null, affectedValues);
+  }
+
+  public void removeAllTrivialPhis(IRBuilder builder, Set<Value> affectedValues) {
     for (BasicBlock block : blocks) {
       List<Phi> phis = new ArrayList<>(block.getPhis());
       for (Phi phi : phis) {
-        phi.removeTrivialPhi(builder);
+        phi.removeTrivialPhi(builder, affectedValues);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 03421cc..1a17a8b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -17,6 +17,9 @@
 import com.android.tools.r8.ir.analysis.constant.Bottom;
 import com.android.tools.r8.ir.analysis.constant.ConstRangeLatticeElement;
 import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption;
 import com.android.tools.r8.ir.code.Assume.NoAssumption;
@@ -540,6 +543,18 @@
   }
 
   /**
+   * Returns an abstraction of the set of fields that may possibly be read as a result of executing
+   * this instruction.
+   */
+  public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+    if (instructionMayTriggerMethodInvocation(appView, context)
+        && instructionMayHaveSideEffects(appView, context)) {
+      return UnknownFieldSet.getInstance();
+    }
+    return EmptyFieldSet.getInstance();
+  }
+
+  /**
    * Returns true if this instruction need this value in a register.
    */
   public boolean needsValueInRegister(Value value) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 9381fd1..48baef4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -12,11 +12,15 @@
 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.DexEncodedMethod.TrivialInitializer;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -277,4 +281,22 @@
 
     return true;
   }
+
+  @Override
+  public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+    DexMethod invokedMethod = getInvokedMethod();
+
+    // Trivial instance initializers do not read any fields.
+    if (appView.dexItemFactory().isConstructor(invokedMethod)) {
+      DexEncodedMethod singleTarget = lookupSingleTarget(appView, context);
+      if (singleTarget != null) {
+        TrivialInitializer info = singleTarget.getOptimizationInfo().getTrivialInitializerInfo();
+        if (info != null && info.isTrivialInstanceInitializer()) {
+          return EmptyFieldSet.getInstance();
+        }
+      }
+    }
+
+    return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(this, appView.dexItemFactory());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index c3db7ea..178da3f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -11,6 +11,8 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
 import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
 import com.android.tools.r8.ir.optimize.InliningOracle;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
@@ -119,4 +121,9 @@
   public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
     return true;
   }
+
+  @Override
+  public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+    return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(this, appView.dexItemFactory());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index e2f9cf3..7cd8ca2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -123,7 +123,7 @@
       builder.constrainType(operand, readConstraint);
       appendOperand(operand);
     }
-    removeTrivialPhi(builder);
+    removeTrivialPhi(builder, null);
   }
 
   public void addOperands(List<Value> operands) {
@@ -224,10 +224,10 @@
   }
 
   public void removeTrivialPhi() {
-    removeTrivialPhi(null);
+    removeTrivialPhi(null, null);
   }
 
-  public void removeTrivialPhi(IRBuilder builder) {
+  void removeTrivialPhi(IRBuilder builder, Set<Value> affectedValues) {
     Value same = null;
     for (Value op : operands) {
       if (op == same || op == this) {
@@ -252,6 +252,9 @@
     if (builder != null && typeLattice.isPreciseType() && !typeLattice.isBottom()) {
       builder.constrainType(same, ValueTypeConstraint.fromTypeLattice(typeLattice));
     }
+    if (affectedValues != null) {
+      affectedValues.addAll(this.affectedValues());
+    }
     // Removing this phi, so get rid of it as a phi user from all of the operands to avoid
     // recursively getting back here with the same phi. If the phi has itself as an operand
     // that also removes the self-reference.
@@ -277,7 +280,7 @@
       replaceUsers(same);
       // Try to simplify phi users that might now have become trivial.
       for (Phi user : phiUsersToSimplify) {
-        user.removeTrivialPhi(builder);
+        user.removeTrivialPhi(builder, affectedValues);
       }
     }
     // Get rid of the phi itself.
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 2881de7..6648777 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.utils.LongInterval;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableSet;
@@ -424,6 +425,22 @@
     return users.getFirst();
   }
 
+  public Set<Instruction> aliasedUsers() {
+    Set<Instruction> users = SetUtils.newIdentityHashSet(uniqueUsers());
+    collectAliasedUsersViaAssume(uniqueUsers(), users);
+    return users;
+  }
+
+  private static void collectAliasedUsersViaAssume(
+      Set<Instruction> usersToTest, Set<Instruction> collectedUsers) {
+    for (Instruction user : usersToTest) {
+      if (user.isAssume()) {
+        collectedUsers.addAll(user.outValue().uniqueUsers());
+        collectAliasedUsersViaAssume(user.outValue().uniqueUsers(), collectedUsers);
+      }
+    }
+  }
+
   public Phi firstPhiUser() {
     assert !phiUsers.isEmpty();
     return phiUsers.getFirst();
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 e370192..a75ce9e 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
@@ -32,6 +32,7 @@
 import com.android.tools.r8.ir.analysis.TypeChecker;
 import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
 import com.android.tools.r8.ir.analysis.fieldaccess.FieldBitAccessAnalysis;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.FieldValueAnalysis;
 import com.android.tools.r8.ir.analysis.sideeffect.ClassInitializerSideEffectAnalysis;
 import com.android.tools.r8.ir.analysis.sideeffect.ClassInitializerSideEffectAnalysis.ClassInitializerSideEffect;
 import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
@@ -1110,8 +1111,7 @@
     }
 
     if (memberValuePropagation != null) {
-      memberValuePropagation.rewriteWithConstantValues(
-          code, method.method.holder, isProcessedConcurrently);
+      memberValuePropagation.rewriteWithConstantValues(code, method.method.holder);
     }
     if (options.enableEnumValueOptimization) {
       assert appView.enableWholeProgramOptimizations();
@@ -1217,7 +1217,7 @@
     codeRewriter.rewriteThrowNullPointerException(code);
 
     if (classInitializerDefaultsOptimization != null && !isDebugMode) {
-      classInitializerDefaultsOptimization.optimize(method, code, feedback);
+      classInitializerDefaultsOptimization.optimize(method, code);
     }
 
     if (Log.ENABLED) {
@@ -1249,26 +1249,6 @@
 
     assert code.verifyTypes(appView);
 
-    if (nonNullTracker != null) {
-      // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
-      //   this may not be the right place to collect call site optimization info.
-      // Collecting call-site optimization info depends on the existence of non-null IRs.
-      // Arguments can be changed during the debug mode.
-      if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
-        appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
-      }
-      // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
-      nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
-    }
-    if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
-      codeRewriter.removeAssumeInstructions(code);
-      assert code.isConsistentSSA();
-    }
-    // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
-    assert code.verifyNoNullabilityBottomTypes();
-
-    assert code.verifyTypes(appView);
-
     previous = printMethod(code, "IR before class inlining (SSA)", previous);
 
     if (classInliner != null) {
@@ -1337,6 +1317,26 @@
       assert code.isConsistentSSA();
     }
 
+    if (nonNullTracker != null) {
+      // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
+      //   this may not be the right place to collect call site optimization info.
+      // Collecting call-site optimization info depends on the existence of non-null IRs.
+      // Arguments can be changed during the debug mode.
+      if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
+        appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
+      }
+      // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
+      nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
+    }
+    if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
+      codeRewriter.removeAssumeInstructions(code);
+      assert code.isConsistentSSA();
+    }
+    // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
+    assert code.verifyNoNullabilityBottomTypes();
+
+    assert code.verifyTypes(appView);
+
     previous = printMethod(code, "IR after outline handler (SSA)", previous);
 
     // TODO(mkroghj) Test if shorten live ranges is worth it.
@@ -1384,6 +1384,7 @@
         }
 
         computeDynamicReturnType(feedback, method, code);
+        FieldValueAnalysis.run(appView, code, feedback, method);
         computeInitializedClassesOnNormalExit(feedback, method, code);
         computeMayHaveSideEffects(feedback, method, code);
         computeReturnValueOnlyDependsOnArguments(feedback, method, code);
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 0e13782..b796600 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
@@ -31,9 +31,9 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
 import com.android.tools.r8.ir.desugar.backports.BooleanMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.CollectionMethodGenerators;
+import com.android.tools.r8.ir.desugar.backports.CollectionMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.FloatMethodRewrites;
-import com.android.tools.r8.ir.desugar.backports.ListMethodGenerators;
-import com.android.tools.r8.ir.desugar.backports.ListMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.LongMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.NumericMethodRewrites;
 import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites;
@@ -999,19 +999,63 @@
       name = factory.createString("of");
       for (int i = 0; i <= 10; i++) {
         final int formalCount = i;
-        proto = factory.createProto(factory.listType, Collections.nCopies(i, factory.objectType));
+        proto = factory.createProto(type, Collections.nCopies(i, factory.objectType));
         method = factory.createMethod(type, proto, name);
         addProvider(
             i == 0
-                ? new InvokeRewriter(method, ListMethodRewrites::rewriteEmptyOf)
+                ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteListOfEmpty)
                 : new MethodGenerator(
                     method,
-                    (options, methodArg, ignored) ->
-                        ListMethodGenerators.generateListOf(options, methodArg, formalCount)));
+                    (options, methodArg) ->
+                        CollectionMethodGenerators.generateListOf(options, methodArg, formalCount)));
       }
-      proto = factory.createProto(factory.listType, factory.objectArrayType);
+      proto = factory.createProto(type, factory.objectArrayType);
       method = factory.createMethod(type, proto, name);
-      addProvider(new MethodGenerator(method, BackportedMethods::ListMethods_ofArray, "ofArray"));
+      addProvider(
+          new MethodGenerator(
+              method, BackportedMethods::CollectionMethods_listOfArray, "ofArray"));
+
+      // Set<E> Set.of(<args>) for 0 to 10 arguments and Set.of(E[])
+      type = factory.setType;
+      name = factory.createString("of");
+      for (int i = 0; i <= 10; i++) {
+        final int formalCount = i;
+        proto = factory.createProto(type, Collections.nCopies(i, factory.objectType));
+        method = factory.createMethod(type, proto, name);
+        addProvider(
+            i == 0
+                ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteSetOfEmpty)
+                : new MethodGenerator(
+                    method,
+                    (options, methodArg) ->
+                        CollectionMethodGenerators.generateSetOf(options, methodArg, formalCount)));
+      }
+      proto = factory.createProto(type, factory.objectArrayType);
+      method = factory.createMethod(type, proto, name);
+      addProvider(
+          new MethodGenerator(
+              method, BackportedMethods::CollectionMethods_setOfArray, "ofArray"));
+
+      // Map<K, V> Map.of(<K, V args>) for 0 to 10 pairs and Map.ofEntries(Map.Entry<K, V>[])
+      type = factory.mapType;
+      name = factory.createString("of");
+      for (int i = 0; i <= 10; i++) {
+        final int formalCount = i;
+        proto = factory.createProto(type, Collections.nCopies(i * 2, factory.objectType));
+        method = factory.createMethod(type, proto, name);
+        addProvider(
+            i == 0
+                ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteMapOfEmpty)
+                : new MethodGenerator(
+                    method,
+                    (options, methodArg) ->
+                        CollectionMethodGenerators.generateMapOf(options, methodArg, formalCount)));
+      }
+      proto = factory.createProto(type, factory.createArrayType(1, factory.mapEntryType));
+      method = factory.createMethod(type, proto, "ofEntries");
+      addProvider(
+          new MethodGenerator(
+              method, BackportedMethods::CollectionMethods_mapOfEntries, "ofEntries"));
     }
 
     private void initializeJava11MethodProviders(DexItemFactory factory) {
@@ -1273,7 +1317,7 @@
 
     @Override
     public Code generateTemplateMethod(InternalOptions options, DexMethod method) {
-      return factory.create(options, method, methodName);
+      return factory.create(options, method);
     }
 
     @Override
@@ -1311,7 +1355,7 @@
 
   private interface TemplateMethodFactory {
 
-    Code create(InternalOptions options, DexMethod method, String name);
+    Code create(InternalOptions options, DexMethod method);
   }
 
   private interface MethodInvokeRewriter {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index 8f0e3f6..0922cd2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -5,23 +5,56 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
+import java.util.Collections;
 
+// TODO(b/134732760): In progress.
+// I convert library calls with desugared parameters/return values so they can work normally.
+// In the JSON of the desugared library, one can specify conversions between desugared and
+// non-desugared types. If no conversion is specified, D8/R8 simply generate wrapper classes around
+// the types. Wrappers induce both memory and runtime performance overhead. Wrappers overload
+// all potential called APIs.
+// Since many types are going to be rewritten, I also need to change the signature of the method
+// called so that they are still called with the original types. Hence the vivified types.
+// Given a type from the library, the prefix rewriter rewrites (->) as follow:
+// vivifiedType -> type;
+// type -> desugarType;
+// No vivified types can be present in the compiled program (will necessarily be rewritten).
+// DesugarType is only a rewritten type (generated through rewriting of type).
+// The type, from the library, may either be rewritten to the desugarType,
+// or be a rewritten type (generated through rewriting of vivifiedType).
 public class DesugaredLibraryAPIConverter {
 
-  AppView<?> appView;
+  private static final String VIVIFIED_PREFIX = "$-vivified-$.";
+
+  private final AppView<?> appView;
+  private final DexItemFactory factory;
 
   public DesugaredLibraryAPIConverter(AppView<?> appView) {
     this.appView = appView;
+    this.factory = appView.dexItemFactory();
   }
 
   public void desugar(IRCode code) {
+    // TODO(b/134732760): The current code does not catch library calls into a program override
+    //  which gets rewritten. If method signature has rewritten types and method overrides library,
+    //  I should convert back.
+
     InstructionListIterator iterator = code.instructionListIterator();
     while (iterator.hasNext()) {
       Instruction instruction = iterator.next();
@@ -30,23 +63,29 @@
       }
       InvokeMethod invokeMethod = instruction.asInvokeMethod();
       DexMethod invokedMethod = invokeMethod.getInvokedMethod();
-      if (appView.rewritePrefix.hasRewrittenType(invokedMethod.holder)) {
+      // Rewritting is required only on calls to library methods which are not desugared.
+      if (appView.rewritePrefix.hasRewrittenType(invokedMethod.holder)
+          || invokedMethod.holder.isArrayType()) {
         continue;
       }
-      // In this case, the method has not been rewritten and is not on a rewritten class.
-      // This invoke will (likely) not work at runtime if a desugared type is present.
-      if (appView.rewritePrefix.hasRewrittenType(invokedMethod.proto.returnType)) {
-        warnInvalidInvoke(invokedMethod.proto.returnType, invokedMethod, "return");
+      DexClass dexClass = appView.definitionFor(invokedMethod.holder);
+      if (dexClass == null || !dexClass.isLibraryClass()) {
+        continue;
       }
-      for (DexType argType : invokedMethod.proto.parameters.values) {
+      if (appView.rewritePrefix.hasRewrittenType(invokedMethod.proto.returnType)) {
+        addReturnConversion(code, invokeMethod, iterator);
+      }
+      for (int i = 0; i < invokedMethod.proto.parameters.values.length; i++) {
+        DexType argType = invokedMethod.proto.parameters.values[i];
         if (appView.rewritePrefix.hasRewrittenType(argType)) {
-          warnInvalidInvoke(argType, invokedMethod, "parameter");
+          addParameterConversion(code, invokeMethod, iterator, argType, i);
         }
       }
     }
   }
 
   private void warnInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) {
+    DexType desugaredType = appView.rewritePrefix.rewrittenType(type);
     appView
         .options()
         .reporter
@@ -59,7 +98,128 @@
                     + " may not work correctly at runtime ("
                     + debugString
                     + " type "
-                    + appView.rewritePrefix.rewrittenType(type)
+                    + desugaredType
                     + " is a desugared type)."));
   }
+
+  private DexType vivifiedTypeFor(DexType type) {
+    DexType vivifiedType =
+        factory.createType(DescriptorUtils.javaTypeToDescriptor(VIVIFIED_PREFIX + type.toString()));
+    appView.rewritePrefix.rewriteType(vivifiedType, type);
+    return vivifiedType;
+  }
+
+  private void addParameterConversion(
+      IRCode code,
+      InvokeMethod invokeMethod,
+      InstructionListIterator iterator,
+      DexType argType,
+      int parameter) {
+    if (!appView
+        .options()
+        .desugaredLibraryConfiguration
+        .getCustomConversions()
+        .containsKey(argType)) {
+      // TODO(b/134732760): Add Wrapper Conversions.
+      warnInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter");
+      return;
+    }
+
+    Value inValue = invokeMethod.inValues().get(parameter);
+    DexType argVivifiedType = vivifiedTypeFor(argType);
+    DexType conversionHolder =
+        appView.options().desugaredLibraryConfiguration.getCustomConversions().get(argType);
+
+    // ConversionType has static method "type convert(rewrittenType)".
+    // But everything is going to be rewritten, so we need to call "vivifiedType convert(type)".
+    DexMethod conversionMethod =
+        factory.createMethod(
+            conversionHolder,
+            factory.createProto(argVivifiedType, argType),
+            factory.convertMethodName);
+    Value convertedValue =
+        code.createValue(
+            TypeLatticeElement.fromDexType(
+                argVivifiedType, inValue.getTypeLattice().nullability(), appView));
+    InvokeStatic conversionInstruction =
+        new InvokeStatic(conversionMethod, convertedValue, Collections.singletonList(inValue));
+    conversionInstruction.setPosition(invokeMethod.getPosition());
+    iterator.previous();
+    iterator.add(conversionInstruction);
+    iterator.next();
+
+    // Rewrite invoke (signature and inValue to rewrite).
+    DexMethod newDexMethod =
+        dexMethodWithDifferentParameter(
+            invokeMethod.getInvokedMethod(), argVivifiedType, parameter);
+    Invoke newInvokeMethod =
+        Invoke.create(
+            invokeMethod.getType(),
+            newDexMethod,
+            newDexMethod.proto,
+            invokeMethod.outValue(),
+            invokeMethod.inValues());
+    newInvokeMethod.replaceValue(parameter, conversionInstruction.outValue());
+    iterator.replaceCurrentInstruction(newInvokeMethod);
+  }
+
+  private void addReturnConversion(
+      IRCode code, InvokeMethod invokeMethod, InstructionListIterator iterator) {
+    DexType returnType = invokeMethod.getReturnType();
+    if (!appView
+        .options()
+        .desugaredLibraryConfiguration
+        .getCustomConversions()
+        .containsKey(returnType)) {
+      // TODO(b/134732760): Add Wrapper Conversions.
+      warnInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return");
+      return;
+    }
+
+    DexType returnVivifiedType = vivifiedTypeFor(returnType);
+    DexType conversionHolder =
+        appView.options().desugaredLibraryConfiguration.getCustomConversions().get(returnType);
+
+    // ConversionType has static method "rewrittenType convert(type)".
+    // But everything is going to be rewritten, so we need to call "type convert(vivifiedType)".
+    DexMethod conversionMethod =
+        factory.createMethod(
+            conversionHolder,
+            factory.createProto(returnType, returnVivifiedType),
+            factory.convertMethodName);
+    Value convertedValue =
+        code.createValue(
+            TypeLatticeElement.fromDexType(returnType, Nullability.maybeNull(), appView));
+    invokeMethod.outValue().replaceUsers(convertedValue);
+    InvokeStatic conversionInstruction =
+        new InvokeStatic(
+            conversionMethod, convertedValue, Collections.singletonList(invokeMethod.outValue()));
+    conversionInstruction.setPosition(invokeMethod.getPosition());
+
+    // Rewrite invoke (signature to rewrite).
+    DexMethod newDexMethod =
+        dexMethodWithDifferentReturn(invokeMethod.getInvokedMethod(), returnVivifiedType);
+    Invoke newInvokeMethod =
+        Invoke.create(
+            invokeMethod.getType(),
+            newDexMethod,
+            newDexMethod.proto,
+            invokeMethod.outValue(),
+            invokeMethod.inValues());
+    iterator.replaceCurrentInstruction(newInvokeMethod);
+    iterator.add(conversionInstruction);
+  }
+
+  private DexMethod dexMethodWithDifferentParameter(
+      DexMethod method, DexType newParameterType, int parameter) {
+    DexType[] newParameters = method.proto.parameters.values.clone();
+    newParameters[parameter] = newParameterType;
+    DexProto newProto = factory.createProto(method.proto.returnType, newParameters);
+    return factory.createMethod(method.holder, newProto, method.name);
+  }
+
+  private DexMethod dexMethodWithDifferentReturn(DexMethod method, DexType newReturnType) {
+    DexProto newProto = factory.createProto(newReturnType, method.proto.parameters.values);
+    return factory.createMethod(method.holder, newProto, method.name);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
index 5bf1c8e..e1f2181 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.Pair;
 import com.google.common.collect.ImmutableList;
@@ -22,11 +23,13 @@
 public class DesugaredLibraryConfiguration {
 
   // TODO(b/134732760): should use DexString, DexType, DexMethod or so on when possible.
+  private final AndroidApiLevel requiredCompilationAPILevel;
   private final boolean libraryCompilation;
   private final Map<String, String> rewritePrefix;
   private final Map<DexType, DexType> emulateLibraryInterface;
   private final Map<DexString, Map<DexType, DexType>> retargetCoreLibMember;
   private final Map<DexType, DexType> backportCoreLibraryMember;
+  private final Map<DexType, DexType> customConversions;
   private final List<Pair<DexType, DexString>> dontRewriteInvocation;
 
   public static Builder builder(DexItemFactory dexItemFactory) {
@@ -35,26 +38,32 @@
 
   public static DesugaredLibraryConfiguration empty() {
     return new DesugaredLibraryConfiguration(
+        AndroidApiLevel.B,
         false,
         ImmutableMap.of(),
         ImmutableMap.of(),
         ImmutableMap.of(),
         ImmutableMap.of(),
+        ImmutableMap.of(),
         ImmutableList.of());
   }
 
   public DesugaredLibraryConfiguration(
+      AndroidApiLevel requiredCompilationAPILevel,
       boolean libraryCompilation,
       Map<String, String> rewritePrefix,
       Map<DexType, DexType> emulateLibraryInterface,
       Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
       Map<DexType, DexType> backportCoreLibraryMember,
+      Map<DexType, DexType> customConversions,
       List<Pair<DexType, DexString>> dontRewriteInvocation) {
+    this.requiredCompilationAPILevel = requiredCompilationAPILevel;
     this.libraryCompilation = libraryCompilation;
     this.rewritePrefix = rewritePrefix;
     this.emulateLibraryInterface = emulateLibraryInterface;
     this.retargetCoreLibMember = retargetCoreLibMember;
     this.backportCoreLibraryMember = backportCoreLibraryMember;
+    this.customConversions = customConversions;
     this.dontRewriteInvocation = dontRewriteInvocation;
   }
 
@@ -64,6 +73,10 @@
         : new DesugarPrefixRewritingMapper(rewritePrefix, factory);
   }
 
+  public AndroidApiLevel getRequiredCompilationApiLevel() {
+    return requiredCompilationAPILevel;
+  }
+
   public boolean isLibraryCompilation() {
     return libraryCompilation;
   }
@@ -84,6 +97,10 @@
     return backportCoreLibraryMember;
   }
 
+  public Map<DexType, DexType> getCustomConversions() {
+    return customConversions;
+  }
+
   public List<Pair<DexType, DexString>> getDontRewriteInvocation() {
     return dontRewriteInvocation;
   }
@@ -92,17 +109,24 @@
 
     private final DexItemFactory factory;
 
+    private AndroidApiLevel requiredCompilationAPILevel;
     private boolean libraryCompilation = false;
     private Map<String, String> rewritePrefix = new HashMap<>();
     private Map<DexType, DexType> emulateLibraryInterface = new HashMap<>();
     private Map<DexString, Map<DexType, DexType>> retargetCoreLibMember = new IdentityHashMap<>();
     private Map<DexType, DexType> backportCoreLibraryMember = new HashMap<>();
+    private Map<DexType, DexType> customConversions = new HashMap<>();
     private List<Pair<DexType, DexString>> dontRewriteInvocation = new ArrayList<>();
 
     public Builder(DexItemFactory dexItemFactory) {
       this.factory = dexItemFactory;
     }
 
+    public Builder setRequiredCompilationAPILevel(AndroidApiLevel requiredCompilationAPILevel) {
+      this.requiredCompilationAPILevel = requiredCompilationAPILevel;
+      return this;
+    }
+
     public Builder setProgramCompilation() {
       libraryCompilation = false;
       return this;
@@ -126,6 +150,13 @@
       return this;
     }
 
+    public Builder putCustomConversion(String type, String conversionHolder) {
+      DexType dexType = stringClassToDexType(type);
+      DexType conversionType = stringClassToDexType(conversionHolder);
+      customConversions.put(dexType, conversionType);
+      return this;
+    }
+
     public Builder putRetargetCoreLibMember(String retarget, String rewrittenRetarget) {
       int index = sharpIndex(retarget, "retarget core library member");
       DexString methodName = factory.createString(retarget.substring(index + 1));
@@ -169,11 +200,13 @@
 
     public DesugaredLibraryConfiguration build() {
       return new DesugaredLibraryConfiguration(
+          requiredCompilationAPILevel,
           libraryCompilation,
           ImmutableMap.copyOf(rewritePrefix),
           ImmutableMap.copyOf(emulateLibraryInterface),
           ImmutableMap.copyOf(retargetCoreLibMember),
           ImmutableMap.copyOf(backportCoreLibraryMember),
+          ImmutableMap.copyOf(customConversions),
           ImmutableList.copyOf(dontRewriteInvocation));
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
index f5664d3..fbf3d1f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.gson.JsonArray;
@@ -57,6 +58,10 @@
               "Unsupported desugared library configuration version, please upgrade the D8/R8"
                   + " compiler."));
     }
+    int required_compilation_api_level =
+        jsonConfig.get("required_compilation_api_level").getAsInt();
+    configurationBuilder.setRequiredCompilationAPILevel(
+        AndroidApiLevel.getAndroidApiLevel(required_compilation_api_level));
     JsonArray jsonFlags =
         libraryCompilation
             ? jsonConfig.getAsJsonArray("library_flags")
@@ -100,6 +105,13 @@
         configurationBuilder.putEmulateLibraryInterface(itf.getKey(), itf.getValue().getAsString());
       }
     }
+    if (jsonFlagSet.has("custom_conversion")) {
+      for (Map.Entry<String, JsonElement> conversion :
+          jsonFlagSet.get("custom_conversion").getAsJsonObject().entrySet()) {
+        configurationBuilder.putCustomConversion(
+            conversion.getKey(), conversion.getValue().getAsString());
+      }
+    }
     if (jsonFlagSet.has("dont_rewrite")) {
       JsonArray dontRewrite = jsonFlagSet.get("dont_rewrite").getAsJsonArray();
       for (JsonElement rewrite : dontRewrite) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 320935f..db8f582 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -156,12 +156,15 @@
   }
 
   private void addRewritePrefix(DexType interfaceType, String rewrittenType) {
-    appView.rewritePrefix.addPrefix(
-        getCompanionClassType(interfaceType).toString(),
-        rewrittenType + COMPANION_CLASS_NAME_SUFFIX);
-    appView.rewritePrefix.addPrefix(
-        getEmulateLibraryInterfaceClassType(interfaceType, factory).toString(),
-        rewrittenType + EMULATE_LIBRARY_CLASS_NAME_SUFFIX);
+    appView.rewritePrefix.rewriteType(
+        getCompanionClassType(interfaceType),
+        factory.createType(
+            DescriptorUtils.javaTypeToDescriptor(rewrittenType + COMPANION_CLASS_NAME_SUFFIX)));
+    appView.rewritePrefix.rewriteType(
+        getEmulateLibraryInterfaceClassType(interfaceType, factory),
+        factory.createType(
+            DescriptorUtils.javaTypeToDescriptor(
+                rewrittenType + EMULATE_LIBRARY_CLASS_NAME_SUFFIX)));
   }
 
   boolean isEmulatedInterface(DexType itf) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 65eb486..454085b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.ImmutableSet;
@@ -298,8 +299,11 @@
     String rewrittenString = rewritten.toString();
     String actualRewrittenPrefix = rewrittenString.substring(0, rewrittenString.lastIndexOf('.'));
     assert javaName.startsWith(actualPrefix);
-    appView.rewritePrefix.addPrefix(
-        javaName, actualRewrittenPrefix + javaName.substring(actualPrefix.length()));
+    appView.rewritePrefix.rewriteType(
+        lambdaClassType,
+        factory.createType(
+            DescriptorUtils.javaTypeToDescriptor(
+                actualRewrittenPrefix + javaName.substring(actualPrefix.length()))));
   }
 
   private static <K, V> V getKnown(Map<K, V> map, K key) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
index 2b1b45b..244dfaf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
@@ -23,7 +23,7 @@
 
   public abstract DexType rewrittenType(DexType type);
 
-  public abstract void addPrefix(String prefix, String rewrittenPrefix);
+  public abstract void rewriteType(DexType type, DexType rewrittenType);
 
   public boolean hasRewrittenType(DexType type) {
     return rewrittenType(type) != null;
@@ -35,9 +35,7 @@
 
     private final Set<DexType> notRewritten = Sets.newConcurrentHashSet();
     private final Map<DexType, DexType> rewritten = new ConcurrentHashMap<>();
-    // Prefix is IdentityHashMap, additionalPrefixes requires however concurrent read and writes.
     private final Map<DexString, DexString> initialPrefixes;
-    private final Map<DexString, DexString> additionalPrefixes = new ConcurrentHashMap<>();
     private final DexItemFactory factory;
 
     public DesugarPrefixRewritingMapper(Map<String, String> prefixes, DexItemFactory factory) {
@@ -97,19 +95,25 @@
     }
 
     @Override
-    public void addPrefix(String prefix, String rewrittenPrefix) {
-      additionalPrefixes.put(toDescriptorPrefix(prefix), toDescriptorPrefix(rewrittenPrefix));
+    public void rewriteType(DexType type, DexType rewrittenType) {
+      assert !notRewritten.contains(type)
+          : "New rewriting rule for "
+              + type
+              + " but the compiler has already made decisions based on the fact that this type was"
+              + " not rewritten";
+      assert !rewritten.containsKey(type) || rewritten.get(type) == rewrittenType
+          : "New rewriting rule for "
+              + type
+              + " but the compiler has already made decisions based on a different rewriting rule"
+              + " for this type";
+      rewritten.put(type, rewrittenType);
     }
 
     private DexType computePrefix(DexType type) {
       DexString prefixToMatch = type.descriptor.withoutArray(factory);
-      DexType result1 = lookup(type, prefixToMatch, initialPrefixes);
-      if (result1 != null) {
-        return result1;
-      }
-      DexType result2 = lookup(type, prefixToMatch, additionalPrefixes);
-      if (result2 != null) {
-        return result2;
+      DexType result = lookup(type, prefixToMatch, initialPrefixes);
+      if (result != null) {
+        return result;
       }
       notRewritten.add(type);
       return null;
@@ -122,7 +126,7 @@
           DexString rewrittenTypeDescriptor =
               type.descriptor.withNewPrefix(prefix, map.get(prefix), factory);
           DexType rewrittenType = factory.createType(rewrittenTypeDescriptor);
-          rewritten.put(type, rewrittenType);
+          rewriteType(type, rewrittenType);
           return rewrittenType;
         }
       }
@@ -143,7 +147,7 @@
     }
 
     @Override
-    public void addPrefix(String prefix, String rewrittenPrefix) {}
+    public void rewriteType(DexType type, DexType rewrittenType) {}
 
     @Override
     public boolean isRewriting() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 12d43cb..d746bf1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -126,7 +126,7 @@
     // The only encoded method.
     CfCode code =
         BackportedMethods.CloseResourceMethod_closeResourceImpl(
-            options, twrCloseResourceMethod, null);
+            options, twrCloseResourceMethod);
     MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(
         Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
     DexEncodedMethod method = new DexEncodedMethod(twrCloseResourceMethod,
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 fb025c9..aadf9e5 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
@@ -47,8 +47,7 @@
 
 public final class BackportedMethods {
 
-  public static CfCode BooleanMethods_compare(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode BooleanMethods_compare(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -79,8 +78,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode BooleanMethods_hashCode(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode BooleanMethods_hashCode(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -104,7 +102,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ByteMethods_compare(InternalOptions options, DexMethod method, String name) {
+  public static CfCode ByteMethods_compare(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -122,8 +120,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ByteMethods_compareUnsigned(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ByteMethods_compareUnsigned(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -145,8 +142,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ByteMethods_toUnsignedInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ByteMethods_toUnsignedInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -164,8 +160,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ByteMethods_toUnsignedLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ByteMethods_toUnsignedLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -184,8 +179,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode CharacterMethods_compare(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode CharacterMethods_compare(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -204,7 +198,7 @@
   }
 
   public static CfCode CharacterMethods_toStringCodepoint(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -239,7 +233,7 @@
   }
 
   public static CfCode CloseResourceMethod_closeResourceImpl(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -518,8 +512,400 @@
         ImmutableList.of());
   }
 
+  public static CfCode CollectionMethods_listOfArray(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();
+    return new CfCode(
+        method.holder,
+        3,
+        6,
+        ImmutableList.of(
+            label0,
+            new CfNew(options.itemFactory.createType("Ljava/util/ArrayList;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfInvoke(
+                183,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/ArrayList;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("V"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("<init>")),
+                false),
+            new CfStore(ValueType.OBJECT, 1),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfStore(ValueType.OBJECT, 2),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfArrayLength(),
+            new CfStore(ValueType.INT, 3),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 4),
+            label2,
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 3),
+            new CfIfCmp(If.Type.GE, ValueType.INT, label5),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfLoad(ValueType.INT, 4),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfStore(ValueType.OBJECT, 5),
+            label3,
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.OBJECT, 5),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/Objects;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/Object;"),
+                        options.itemFactory.createType("Ljava/lang/Object;")),
+                    options.itemFactory.createString("requireNonNull")),
+                false),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/ArrayList;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Z"),
+                        options.itemFactory.createType("Ljava/lang/Object;")),
+                    options.itemFactory.createString("add")),
+                false),
+            new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+            label4,
+            new CfIinc(4, 1),
+            new CfGoto(label2),
+            label5,
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/Collections;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/util/List;"),
+                        options.itemFactory.createType("Ljava/util/List;")),
+                    options.itemFactory.createString("unmodifiableList")),
+                false),
+            new CfReturn(ValueType.OBJECT),
+            label6),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode CollectionMethods_mapOfEntries(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,
+        4,
+        8,
+        ImmutableList.of(
+            label0,
+            new CfNew(options.itemFactory.createType("Ljava/util/HashMap;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfInvoke(
+                183,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/HashMap;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("V"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("<init>")),
+                false),
+            new CfStore(ValueType.OBJECT, 1),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfStore(ValueType.OBJECT, 2),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfArrayLength(),
+            new CfStore(ValueType.INT, 3),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 4),
+            label2,
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 3),
+            new CfIfCmp(If.Type.GE, ValueType.INT, label8),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfLoad(ValueType.INT, 4),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfStore(ValueType.OBJECT, 5),
+            label3,
+            new CfLoad(ValueType.OBJECT, 5),
+            new CfInvoke(
+                185,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/Map$Entry;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/Object;")),
+                    options.itemFactory.createString("getKey")),
+                true),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/Objects;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/Object;"),
+                        options.itemFactory.createType("Ljava/lang/Object;")),
+                    options.itemFactory.createString("requireNonNull")),
+                false),
+            new CfStore(ValueType.OBJECT, 6),
+            label4,
+            new CfLoad(ValueType.OBJECT, 5),
+            new CfInvoke(
+                185,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/Map$Entry;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/Object;")),
+                    options.itemFactory.createString("getValue")),
+                true),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/Objects;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/Object;"),
+                        options.itemFactory.createType("Ljava/lang/Object;")),
+                    options.itemFactory.createString("requireNonNull")),
+                false),
+            new CfStore(ValueType.OBJECT, 7),
+            label5,
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.OBJECT, 6),
+            new CfLoad(ValueType.OBJECT, 7),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/HashMap;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/Object;"),
+                        options.itemFactory.createType("Ljava/lang/Object;"),
+                        options.itemFactory.createType("Ljava/lang/Object;")),
+                    options.itemFactory.createString("put")),
+                false),
+            new CfIf(If.Type.EQ, ValueType.OBJECT, label7),
+            label6,
+            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("duplicate key: ")),
+            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.OBJECT, 6),
+            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/Object;")),
+                    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(),
+            label7,
+            new CfIinc(4, 1),
+            new CfGoto(label2),
+            label8,
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/Collections;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/util/Map;"),
+                        options.itemFactory.createType("Ljava/util/Map;")),
+                    options.itemFactory.createString("unmodifiableMap")),
+                false),
+            new CfReturn(ValueType.OBJECT),
+            label9),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode CollectionMethods_setOfArray(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();
+    return new CfCode(
+        method.holder,
+        4,
+        6,
+        ImmutableList.of(
+            label0,
+            new CfNew(options.itemFactory.createType("Ljava/util/HashSet;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfInvoke(
+                183,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/HashSet;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("V"), options.itemFactory.createType("I")),
+                    options.itemFactory.createString("<init>")),
+                false),
+            new CfStore(ValueType.OBJECT, 1),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfStore(ValueType.OBJECT, 2),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfArrayLength(),
+            new CfStore(ValueType.INT, 3),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 4),
+            label2,
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 3),
+            new CfIfCmp(If.Type.GE, ValueType.INT, label6),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfLoad(ValueType.INT, 4),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfStore(ValueType.OBJECT, 5),
+            label3,
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.OBJECT, 5),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/Objects;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/Object;"),
+                        options.itemFactory.createType("Ljava/lang/Object;")),
+                    options.itemFactory.createString("requireNonNull")),
+                false),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/HashSet;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Z"),
+                        options.itemFactory.createType("Ljava/lang/Object;")),
+                    options.itemFactory.createString("add")),
+                false),
+            new CfIf(If.Type.NE, ValueType.INT, label5),
+            label4,
+            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("duplicate element: ")),
+            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.OBJECT, 5),
+            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/Object;")),
+                    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(),
+            label5,
+            new CfIinc(4, 1),
+            new CfGoto(label2),
+            label6,
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/Collections;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/util/Set;"),
+                        options.itemFactory.createType("Ljava/util/Set;")),
+                    options.itemFactory.createString("unmodifiableSet")),
+                false),
+            new CfReturn(ValueType.OBJECT),
+            label7),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode CollectionsMethods_emptyEnumeration(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     return new CfCode(
         method.holder,
@@ -549,8 +935,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode CollectionsMethods_emptyIterator(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode CollectionsMethods_emptyIterator(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     return new CfCode(
         method.holder,
@@ -580,7 +965,7 @@
   }
 
   public static CfCode CollectionsMethods_emptyListIterator(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     return new CfCode(
         method.holder,
@@ -609,8 +994,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode DoubleMethods_hashCode(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode DoubleMethods_hashCode(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -643,8 +1027,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode DoubleMethods_isFinite(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode DoubleMethods_isFinite(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -686,8 +1069,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode FloatMethods_isFinite(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode FloatMethods_isFinite(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -729,8 +1111,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode IntegerMethods_compare(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode IntegerMethods_compare(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -762,8 +1143,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode IntegerMethods_compareUnsigned(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode IntegerMethods_compareUnsigned(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -802,8 +1182,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode IntegerMethods_divideUnsigned(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode IntegerMethods_divideUnsigned(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -816,13 +1195,13 @@
             label0,
             new CfLoad(ValueType.INT, 0),
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
-            new CfConstNumber(4294967295l, ValueType.LONG),
+            new CfConstNumber(4294967295L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
             new CfStore(ValueType.LONG, 2),
             label1,
             new CfLoad(ValueType.INT, 1),
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
-            new CfConstNumber(4294967295l, ValueType.LONG),
+            new CfConstNumber(4294967295L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
             new CfStore(ValueType.LONG, 4),
             label2,
@@ -836,8 +1215,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode IntegerMethods_parseUnsignedInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode IntegerMethods_parseUnsignedInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -865,7 +1243,7 @@
   }
 
   public static CfCode IntegerMethods_parseUnsignedIntWithRadix(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -930,7 +1308,7 @@
             new CfStore(ValueType.LONG, 2),
             label3,
             new CfLoad(ValueType.LONG, 2),
-            new CfConstNumber(4294967295l, ValueType.LONG),
+            new CfConstNumber(4294967295L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
             new CfLoad(ValueType.LONG, 2),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -1025,8 +1403,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode IntegerMethods_remainderUnsigned(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode IntegerMethods_remainderUnsigned(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -1039,13 +1416,13 @@
             label0,
             new CfLoad(ValueType.INT, 0),
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
-            new CfConstNumber(4294967295l, ValueType.LONG),
+            new CfConstNumber(4294967295L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
             new CfStore(ValueType.LONG, 2),
             label1,
             new CfLoad(ValueType.INT, 1),
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
-            new CfConstNumber(4294967295l, ValueType.LONG),
+            new CfConstNumber(4294967295L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
             new CfStore(ValueType.LONG, 4),
             label2,
@@ -1059,8 +1436,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode IntegerMethods_toUnsignedLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode IntegerMethods_toUnsignedLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -1071,7 +1447,7 @@
             label0,
             new CfLoad(ValueType.INT, 0),
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
-            new CfConstNumber(4294967295l, ValueType.LONG),
+            new CfConstNumber(4294967295L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
             new CfReturn(ValueType.LONG),
             label1),
@@ -1079,8 +1455,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode IntegerMethods_toUnsignedString(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode IntegerMethods_toUnsignedString(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -1108,7 +1483,7 @@
   }
 
   public static CfCode IntegerMethods_toUnsignedStringWithRadix(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -1120,7 +1495,7 @@
             label0,
             new CfLoad(ValueType.INT, 0),
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
-            new CfConstNumber(4294967295l, ValueType.LONG),
+            new CfConstNumber(4294967295L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
             new CfStore(ValueType.LONG, 2),
             label1,
@@ -1142,93 +1517,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ListMethods_ofArray(InternalOptions options, DexMethod method, String name) {
-    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();
-    return new CfCode(
-        method.holder,
-        3,
-        6,
-        ImmutableList.of(
-            label0,
-            new CfNew(options.itemFactory.createType("Ljava/util/ArrayList;")),
-            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
-            new CfLoad(ValueType.OBJECT, 0),
-            new CfArrayLength(),
-            new CfInvoke(
-                183,
-                options.itemFactory.createMethod(
-                    options.itemFactory.createType("Ljava/util/ArrayList;"),
-                    options.itemFactory.createProto(
-                        options.itemFactory.createType("V"), options.itemFactory.createType("I")),
-                    options.itemFactory.createString("<init>")),
-                false),
-            new CfStore(ValueType.OBJECT, 1),
-            label1,
-            new CfLoad(ValueType.OBJECT, 0),
-            new CfStore(ValueType.OBJECT, 2),
-            new CfLoad(ValueType.OBJECT, 2),
-            new CfArrayLength(),
-            new CfStore(ValueType.INT, 3),
-            new CfConstNumber(0, ValueType.INT),
-            new CfStore(ValueType.INT, 4),
-            label2,
-            new CfLoad(ValueType.INT, 4),
-            new CfLoad(ValueType.INT, 3),
-            new CfIfCmp(If.Type.GE, ValueType.INT, label5),
-            new CfLoad(ValueType.OBJECT, 2),
-            new CfLoad(ValueType.INT, 4),
-            new CfArrayLoad(MemberType.OBJECT),
-            new CfStore(ValueType.OBJECT, 5),
-            label3,
-            new CfLoad(ValueType.OBJECT, 1),
-            new CfLoad(ValueType.OBJECT, 5),
-            new CfInvoke(
-                184,
-                options.itemFactory.createMethod(
-                    options.itemFactory.createType("Ljava/util/Objects;"),
-                    options.itemFactory.createProto(
-                        options.itemFactory.createType("Ljava/lang/Object;"),
-                        options.itemFactory.createType("Ljava/lang/Object;")),
-                    options.itemFactory.createString("requireNonNull")),
-                false),
-            new CfInvoke(
-                182,
-                options.itemFactory.createMethod(
-                    options.itemFactory.createType("Ljava/util/ArrayList;"),
-                    options.itemFactory.createProto(
-                        options.itemFactory.createType("Z"),
-                        options.itemFactory.createType("Ljava/lang/Object;")),
-                    options.itemFactory.createString("add")),
-                false),
-            new CfStackInstruction(CfStackInstruction.Opcode.Pop),
-            label4,
-            new CfIinc(4, 1),
-            new CfGoto(label2),
-            label5,
-            new CfLoad(ValueType.OBJECT, 1),
-            new CfInvoke(
-                184,
-                options.itemFactory.createMethod(
-                    options.itemFactory.createType("Ljava/util/Collections;"),
-                    options.itemFactory.createProto(
-                        options.itemFactory.createType("Ljava/util/List;"),
-                        options.itemFactory.createType("Ljava/util/List;")),
-                    options.itemFactory.createString("unmodifiableList")),
-                false),
-            new CfReturn(ValueType.OBJECT),
-            label6),
-        ImmutableList.of(),
-        ImmutableList.of());
-  }
-
-  public static CfCode LongMethods_compareUnsigned(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode LongMethods_compareUnsigned(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -1240,12 +1529,12 @@
         ImmutableList.of(
             label0,
             new CfLoad(ValueType.LONG, 0),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
             new CfStore(ValueType.LONG, 4),
             label1,
             new CfLoad(ValueType.LONG, 2),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
             new CfStore(ValueType.LONG, 6),
             label2,
@@ -1267,8 +1556,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode LongMethods_divideUnsigned(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode LongMethods_divideUnsigned(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -1297,12 +1585,12 @@
             new CfIf(If.Type.GE, ValueType.INT, label6),
             label1,
             new CfLoad(ValueType.LONG, 0),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
             new CfStore(ValueType.LONG, 4),
             label2,
             new CfLoad(ValueType.LONG, 2),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
             new CfStore(ValueType.LONG, 6),
             label3,
@@ -1344,12 +1632,12 @@
             new CfStore(ValueType.LONG, 6),
             label10,
             new CfLoad(ValueType.LONG, 6),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
             new CfStore(ValueType.LONG, 8),
             label11,
             new CfLoad(ValueType.LONG, 2),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
             new CfStore(ValueType.LONG, 10),
             label12,
@@ -1371,8 +1659,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode LongMethods_hashCode(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode LongMethods_hashCode(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -1393,8 +1680,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode LongMethods_parseUnsignedLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode LongMethods_parseUnsignedLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -1422,7 +1708,7 @@
   }
 
   public static CfCode LongMethods_parseUnsignedLongWithRadix(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -1679,8 +1965,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode LongMethods_remainderUnsigned(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode LongMethods_remainderUnsigned(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -1709,12 +1994,12 @@
             new CfIf(If.Type.GE, ValueType.INT, label6),
             label1,
             new CfLoad(ValueType.LONG, 0),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
             new CfStore(ValueType.LONG, 4),
             label2,
             new CfLoad(ValueType.LONG, 2),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
             new CfStore(ValueType.LONG, 6),
             label3,
@@ -1758,12 +2043,12 @@
             new CfStore(ValueType.LONG, 6),
             label10,
             new CfLoad(ValueType.LONG, 6),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
             new CfStore(ValueType.LONG, 8),
             label11,
             new CfLoad(ValueType.LONG, 2),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
             new CfStore(ValueType.LONG, 10),
             label12,
@@ -1784,8 +2069,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode LongMethods_toUnsignedString(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode LongMethods_toUnsignedString(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -1813,7 +2097,7 @@
   }
 
   public static CfCode LongMethods_toUnsignedStringWithRadix(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2066,8 +2350,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_addExactInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_addExactInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2115,8 +2398,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_addExactLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_addExactLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2180,8 +2462,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_decrementExactInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_decrementExactInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2216,8 +2497,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_decrementExactLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_decrementExactLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2229,7 +2509,7 @@
         ImmutableList.of(
             label0,
             new CfLoad(ValueType.LONG, 0),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
             new CfIf(If.Type.NE, ValueType.INT, label2),
             label1,
@@ -2253,8 +2533,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_floorDivInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_floorDivInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2312,8 +2591,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_floorDivLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_floorDivLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2375,8 +2653,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_floorDivLongInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_floorDivLongInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -2404,8 +2681,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_floorModInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_floorModInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2455,8 +2731,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_floorModLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_floorModLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2510,8 +2785,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_floorModLongInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_floorModLongInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -2540,8 +2814,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_incrementExactInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_incrementExactInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2576,8 +2849,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_incrementExactLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_incrementExactLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2589,7 +2861,7 @@
         ImmutableList.of(
             label0,
             new CfLoad(ValueType.LONG, 0),
-            new CfConstNumber(9223372036854775807l, ValueType.LONG),
+            new CfConstNumber(9223372036854775807L, ValueType.LONG),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
             new CfIf(If.Type.NE, ValueType.INT, label2),
             label1,
@@ -2613,8 +2885,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_multiplyExactInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_multiplyExactInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2662,8 +2933,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_multiplyExactLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_multiplyExactLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2758,7 +3028,7 @@
             new CfConstNumber(0, ValueType.INT),
             label9,
             new CfLoad(ValueType.LONG, 2),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
             new CfIf(If.Type.EQ, ValueType.INT, label10),
             new CfConstNumber(1, ValueType.INT),
@@ -2803,8 +3073,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_multiplyExactLongInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_multiplyExactLongInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -2832,8 +3101,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_negateExactInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_negateExactInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2867,8 +3135,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_negateExactLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_negateExactLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -2880,7 +3147,7 @@
         ImmutableList.of(
             label0,
             new CfLoad(ValueType.LONG, 0),
-            new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+            new CfConstNumber(-9223372036854775808L, ValueType.LONG),
             new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
             new CfIf(If.Type.NE, ValueType.INT, label2),
             label1,
@@ -2903,8 +3170,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_nextDownDouble(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_nextDownDouble(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -2930,8 +3196,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_nextDownFloat(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_nextDownFloat(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -2957,8 +3222,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_subtractExactInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_subtractExactInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3006,8 +3270,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_subtractExactLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_subtractExactLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3071,8 +3334,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode MathMethods_toIntExact(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode MathMethods_toIntExact(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3113,7 +3375,7 @@
   }
 
   public static CfCode ObjectsMethods_checkFromIndexSize(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3253,8 +3515,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ObjectsMethods_checkFromToIndex(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ObjectsMethods_checkFromToIndex(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3371,8 +3632,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ObjectsMethods_checkIndex(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ObjectsMethods_checkIndex(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3466,8 +3726,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ObjectsMethods_compare(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ObjectsMethods_compare(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3504,8 +3763,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ObjectsMethods_deepEquals(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ObjectsMethods_deepEquals(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3841,8 +4099,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ObjectsMethods_equals(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ObjectsMethods_equals(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3883,8 +4140,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ObjectsMethods_hashCode(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ObjectsMethods_hashCode(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3915,8 +4171,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ObjectsMethods_isNull(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ObjectsMethods_isNull(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3940,8 +4195,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ObjectsMethods_nonNull(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ObjectsMethods_nonNull(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -3966,7 +4220,7 @@
   }
 
   public static CfCode ObjectsMethods_requireNonNullElse(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4000,7 +4254,7 @@
   }
 
   public static CfCode ObjectsMethods_requireNonNullElseGet(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4058,7 +4312,7 @@
   }
 
   public static CfCode ObjectsMethods_requireNonNullMessage(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4093,8 +4347,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ObjectsMethods_toString(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ObjectsMethods_toString(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -4121,8 +4374,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ObjectsMethods_toStringDefault(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ObjectsMethods_toStringDefault(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4154,8 +4406,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode OptionalMethods_ifPresentOrElse(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode OptionalMethods_ifPresentOrElse(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4214,7 +4465,7 @@
   }
 
   public static CfCode OptionalMethods_ifPresentOrElseDouble(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4271,7 +4522,7 @@
   }
 
   public static CfCode OptionalMethods_ifPresentOrElseInt(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4328,7 +4579,7 @@
   }
 
   public static CfCode OptionalMethods_ifPresentOrElseLong(
-      InternalOptions options, DexMethod method, String name) {
+      InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4384,7 +4635,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode OptionalMethods_or(InternalOptions options, DexMethod method, String name) {
+  public static CfCode OptionalMethods_or(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4451,8 +4702,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode OptionalMethods_stream(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode OptionalMethods_stream(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4507,8 +4757,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ShortMethods_compare(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ShortMethods_compare(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -4526,8 +4775,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ShortMethods_compareUnsigned(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ShortMethods_compareUnsigned(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -4549,8 +4797,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ShortMethods_toUnsignedInt(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ShortMethods_toUnsignedInt(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -4568,8 +4815,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode ShortMethods_toUnsignedLong(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode ShortMethods_toUnsignedLong(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     return new CfCode(
@@ -4588,8 +4834,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode StringMethods_joinArray(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode StringMethods_joinArray(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
@@ -4707,8 +4952,7 @@
         ImmutableList.of());
   }
 
-  public static CfCode StringMethods_joinIterable(
-      InternalOptions options, DexMethod method, String name) {
+  public static CfCode StringMethods_joinIterable(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
     CfLabel label2 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodGenerators.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodGenerators.java
new file mode 100644
index 0000000..9e78a3b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodGenerators.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.desugar.backports;
+
+import com.android.tools.r8.cf.code.CfArrayStore;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfNewArray;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import org.objectweb.asm.Opcodes;
+
+public final class CollectionMethodGenerators {
+
+  private CollectionMethodGenerators() {}
+
+  public static CfCode generateListOf(InternalOptions options, DexMethod method, int formalCount) {
+    return generateFixedMethods(options, method, formalCount, options.itemFactory.listType);
+  }
+
+  public static CfCode generateSetOf(InternalOptions options, DexMethod method, int formalCount) {
+    return generateFixedMethods(options, method, formalCount, options.itemFactory.setType);
+  }
+
+  private static CfCode generateFixedMethods(
+      InternalOptions options, DexMethod method, int formalCount, DexType returnType) {
+    Builder<CfInstruction> builder = ImmutableList.builder();
+    builder.add(
+        new CfConstNumber(formalCount, ValueType.INT),
+        new CfNewArray(options.itemFactory.objectArrayType));
+
+    for (int i = 0; i < formalCount; i++) {
+      builder.add(
+          new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+          new CfConstNumber(i, ValueType.INT),
+          new CfLoad(ValueType.OBJECT, i),
+          new CfArrayStore(MemberType.OBJECT));
+    }
+
+    builder.add(
+        new CfInvoke(
+            Opcodes.INVOKESTATIC,
+            options.itemFactory.createMethod(
+                returnType,
+                options.itemFactory.createProto(returnType, options.itemFactory.objectArrayType),
+                options.itemFactory.createString("of")),
+            false),
+        new CfReturn(ValueType.OBJECT));
+
+    return new CfCode(
+        method.holder,
+        4,
+        formalCount,
+        builder.build(),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode generateMapOf(
+      InternalOptions options, DexMethod method, int formalCount) {
+    DexType mapEntryArray =
+        options.itemFactory.createArrayType(1, options.itemFactory.mapEntryType);
+    DexType simpleEntry = options.itemFactory.createType("Ljava/util/AbstractMap$SimpleEntry;");
+    DexMethod simpleEntryConstructor = options.itemFactory.createMethod(
+        simpleEntry,
+        options.itemFactory.createProto(
+            options.itemFactory.voidType,
+            options.itemFactory.objectType,
+            options.itemFactory.objectType),
+        Constants.INSTANCE_INITIALIZER_NAME);
+
+    Builder<CfInstruction> builder = ImmutableList.builder();
+    builder.add(
+        new CfConstNumber(formalCount, ValueType.INT),
+        new CfNewArray(mapEntryArray));
+
+    for (int i = 0; i < formalCount; i++) {
+      builder.add(
+          new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+          new CfConstNumber(i, ValueType.INT),
+          new CfNew(simpleEntry),
+          new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+          new CfLoad(ValueType.OBJECT, i * 2),
+          new CfLoad(ValueType.OBJECT, i * 2 + 1),
+          new CfInvoke(Opcodes.INVOKESPECIAL, simpleEntryConstructor, false),
+          new CfArrayStore(MemberType.OBJECT));
+    }
+
+    builder.add(
+        new CfInvoke(
+            Opcodes.INVOKESTATIC,
+            options.itemFactory.createMethod(
+                options.itemFactory.mapType,
+                options.itemFactory.createProto(
+                    options.itemFactory.mapType,
+                    mapEntryArray),
+                options.itemFactory.createString("ofEntries")),
+            false),
+        new CfReturn(ValueType.OBJECT));
+
+    return new CfCode(
+        method.holder,
+        7,
+        formalCount * 2,
+        builder.build(),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java
new file mode 100644
index 0000000..5f54256
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import java.util.Collections;
+
+public final class CollectionMethodRewrites {
+
+  private CollectionMethodRewrites() {}
+
+  public static void rewriteListOfEmpty(
+      InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
+    rewriteToCollectionMethod(invoke, iterator, factory, "emptyList");
+  }
+
+  public static void rewriteSetOfEmpty(
+      InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
+    rewriteToCollectionMethod(invoke, iterator, factory, "emptySet");
+  }
+
+  public static void rewriteMapOfEmpty(
+      InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
+    rewriteToCollectionMethod(invoke, iterator, factory, "emptyMap");
+  }
+
+  private static void rewriteToCollectionMethod(InvokeMethod invoke,
+      InstructionListIterator iterator, DexItemFactory factory, String methodName) {
+    assert invoke.inValues().isEmpty();
+
+    DexMethod collectionsEmptyList =
+        factory.createMethod(factory.collectionsType, invoke.getInvokedMethod().proto, methodName);
+    InvokeStatic newInvoke =
+        new InvokeStatic(collectionsEmptyList, invoke.outValue(), Collections.emptyList());
+    iterator.replaceCurrentInstruction(newInvoke);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodGenerators.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodGenerators.java
deleted file mode 100644
index 1319752..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodGenerators.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.ir.desugar.backports;
-
-import com.android.tools.r8.cf.code.CfArrayStore;
-import com.android.tools.r8.cf.code.CfConstNumber;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfLoad;
-import com.android.tools.r8.cf.code.CfNewArray;
-import com.android.tools.r8.cf.code.CfReturn;
-import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.code.MemberType;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
-import org.objectweb.asm.Opcodes;
-
-public final class ListMethodGenerators {
-
-  private ListMethodGenerators() {}
-
-  public static CfCode generateListOf(InternalOptions options, DexMethod method, int formalCount) {
-    Builder<CfInstruction> builder = ImmutableList.builder();
-    builder.add(
-        new CfConstNumber(formalCount, ValueType.INT),
-        new CfNewArray(options.itemFactory.objectArrayType));
-
-    for (int i = 0; i < formalCount; i++) {
-      builder.add(
-          new CfStackInstruction(CfStackInstruction.Opcode.Dup),
-          new CfConstNumber(i, ValueType.INT),
-          new CfLoad(ValueType.OBJECT, i),
-          new CfArrayStore(MemberType.OBJECT));
-    }
-
-    builder.add(
-        new CfInvoke(
-            Opcodes.INVOKESTATIC,
-            options.itemFactory.createMethod(
-                options.itemFactory.listType,
-                options.itemFactory.createProto(
-                    options.itemFactory.listType, options.itemFactory.objectArrayType),
-                options.itemFactory.createString("of")),
-            false),
-        new CfReturn(ValueType.OBJECT));
-
-    return new CfCode(
-        method.holder,
-        formalCount + 2,
-        formalCount,
-        builder.build(),
-        ImmutableList.of(),
-        ImmutableList.of());
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodRewrites.java
deleted file mode 100644
index c3245d8..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodRewrites.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar.backports;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import java.util.Collections;
-
-public final class ListMethodRewrites {
-
-  private ListMethodRewrites() {}
-
-  public static void rewriteEmptyOf(
-      InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
-    assert invoke.inValues().isEmpty();
-
-    DexMethod collectionsEmptyList =
-        factory.createMethod(factory.collectionsType, invoke.getInvokedMethod().proto, "emptyList");
-    InvokeStatic newInvoke =
-        new InvokeStatic(collectionsEmptyList, invoke.outValue(), Collections.emptyList());
-    iterator.replaceCurrentInstruction(newInvoke);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index 72009f4..6c2110b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -29,7 +29,6 @@
 import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.DexValue.DexValueShort;
 import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.ArrayPut;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -45,7 +44,6 @@
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.naming.dexitembasedstring.ClassNameComputationInfo;
 import com.android.tools.r8.naming.dexitembasedstring.ClassNameComputationInfo.ClassNameMapping;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -104,7 +102,7 @@
     this.dexItemFactory = appView.dexItemFactory();
   }
 
-  public void optimize(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
+  public void optimize(DexEncodedMethod method, IRCode code) {
     if (!method.isClassInitializer()) {
       return;
     }
@@ -165,14 +163,6 @@
             throw new Unreachable("Unexpected field type " + fieldType + ".");
           }
         }
-      } else if (appView.options().enableFieldTypePropagation && appView.appInfo().hasLiveness()) {
-        AppInfoWithLiveness appInfoWithLiveness = appView.withLiveness().appInfo();
-        if (appInfoWithLiveness.isStaticFieldWrittenOnlyInEnclosingStaticInitializer(field)) {
-          TypeLatticeElement valueType = value.getTypeLattice();
-          assert valueType.strictlyLessThan(
-              TypeLatticeElement.fromDexType(fieldType, Nullability.maybeNull(), appView), appView);
-          feedback.markFieldHasDynamicType(field, valueType);
-        }
       }
     }
 
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 fba5ae7..1f36d25 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
@@ -218,7 +218,7 @@
     // Therefore, Assume elimination may result in a trivial phi:
     //   z <- phi(x, x)
     if (needToCheckTrivialPhis) {
-      code.removeAllTrivialPhis();
+      code.removeAllTrivialPhis(valuesThatRequireWidening);
     }
 
     if (!valuesThatRequireWidening.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 6644b26..787cf33 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -5,12 +5,9 @@
 
 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.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
-import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialClassInitializer;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexReference;
@@ -19,7 +16,6 @@
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
-import com.android.tools.r8.ir.code.Assume;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
 import com.android.tools.r8.ir.code.ConstInstruction;
@@ -31,7 +27,6 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
@@ -44,7 +39,6 @@
 import com.google.common.collect.Sets;
 import java.util.ListIterator;
 import java.util.Set;
-import java.util.function.Predicate;
 
 public class MemberValuePropagation {
 
@@ -335,18 +329,11 @@
         iterator.add(replacement);
       }
       target.getMutableOptimizationInfo().markAsPropagated();
-      return;
-    }
-    if (target.getOptimizationInfo().neverReturnsNull()
-        && current.outValue().getTypeLattice().isReference()
-        && current.outValue().canBeNull()) {
-      insertAssumeNotNull(code, affectedValues, blocks, iterator, current);
     }
   }
 
   private void rewriteStaticGetWithConstantValues(
       IRCode code,
-      Predicate<DexEncodedMethod> isProcessedConcurrently,
       Set<Value> affectedValues,
       ListIterator<BasicBlock> blocks,
       InstructionListIterator iterator,
@@ -390,49 +377,6 @@
         code.method.getMutableOptimizationInfo().markUseIdentifierNameString();
       }
       feedback.markFieldAsPropagated(target);
-      return;
-    }
-
-    if (current.hasOutValue()) {
-      Value outValue = current.outValue();
-      TypeLatticeElement outType = outValue.getTypeLattice();
-      if (outType.isReference() && outType.isNullable()) {
-        TypeLatticeElement dynamicType = target.getOptimizationInfo().getDynamicType();
-        if (dynamicType != null && dynamicType.isDefinitelyNotNull()) {
-          insertAssumeNotNull(code, affectedValues, blocks, iterator, current);
-          return;
-        }
-
-        // In case the class holder of this static field satisfying following criteria:
-        //   -- cannot trigger other static initializer except for its own
-        //   -- is final
-        //   -- has a class initializer which is classified as trivial
-        //      (see CodeRewriter::computeClassInitializerInfo) and
-        //      initializes the field being accessed
-        //
-        // ... and the field itself is not pinned by keep rules (in which case it might
-        // be updated outside the class constructor, e.g. via reflections), it is safe
-        // to assume that the static-get instruction reads the value it initialized value
-        // in class initializer and is never null.
-        // TODO(b/141143236): This should be subsumed entirely by the non-null propagation for
-        //  fields, and thus should be removed.
-        DexClass holderDefinition = appView.definitionFor(field.holder);
-        if (holderDefinition != null
-            && holderDefinition.accessFlags.isFinal()
-            && !field.holder.initializationOfParentTypesMayHaveSideEffects(appView)) {
-          DexEncodedMethod classInitializer = holderDefinition.getClassInitializer();
-          if (classInitializer != null && !isProcessedConcurrently.test(classInitializer)) {
-            TrivialInitializer info =
-                classInitializer.getOptimizationInfo().getTrivialInitializerInfo();
-            if (info != null
-                && ((TrivialClassInitializer) info).field == field
-                && outValue.getTypeLattice().isReference()
-                && outValue.canBeNull()) {
-              insertAssumeNotNull(code, affectedValues, blocks, iterator, current);
-            }
-          }
-        }
-      }
     }
   }
 
@@ -465,39 +409,12 @@
     }
   }
 
-  private void insertAssumeNotNull(
-      IRCode code,
-      Set<Value> affectedValues,
-      ListIterator<BasicBlock> blocks,
-      InstructionListIterator iterator,
-      Instruction current) {
-    Value knownToBeNonNullValue = current.outValue();
-    Set<Value> affectedUsers = knownToBeNonNullValue.affectedValues();
-    TypeLatticeElement typeLattice = knownToBeNonNullValue.getTypeLattice();
-    Value nonNullValue =
-        code.createValue(
-            typeLattice.asReferenceTypeLatticeElement().asNotNull(),
-            knownToBeNonNullValue.getLocalInfo());
-    knownToBeNonNullValue.replaceUsers(nonNullValue);
-    Assume nonNull =
-        Assume.createAssumeNonNullInstruction(
-            nonNullValue, knownToBeNonNullValue, current, appView);
-    nonNull.setPosition(appView.options().debug ? current.getPosition() : Position.none());
-    if (current.getBlock().hasCatchHandlers()) {
-      iterator.split(code, blocks).listIterator(code).add(nonNull);
-    } else {
-      iterator.add(nonNull);
-    }
-    affectedValues.addAll(affectedUsers);
-  }
-
   /**
    * Replace invoke targets and field accesses with constant values where possible.
    *
    * <p>Also assigns value ranges to values where possible.
    */
-  public void rewriteWithConstantValues(
-      IRCode code, DexType callingContext, Predicate<DexEncodedMethod> isProcessedConcurrently) {
+  public void rewriteWithConstantValues(IRCode code, DexType callingContext) {
     IRMetadata metadata = code.metadata();
     if (!metadata.mayHaveFieldGet() && !metadata.mayHaveInvokeMethod()) {
       return;
@@ -516,7 +433,6 @@
         } else if (current.isStaticGet()) {
           rewriteStaticGetWithConstantValues(
               code,
-              isProcessedConcurrently,
               affectedValues,
               blocks,
               iterator,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index 3b06027..02cfe3a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -6,8 +6,10 @@
 import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -30,6 +32,7 @@
 import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import it.unimi.dsi.fastutil.ints.IntList;
@@ -139,6 +142,9 @@
                   && optimizationInfo.getDynamicType().isDefinitelyNotNull()) {
                 knownToBeNonNullValues.add(outValue);
               }
+
+              assert verifyCompanionClassInstanceIsKnownToBeNonNull(
+                  fieldInstruction, encodedField, knownToBeNonNullValues);
             }
           }
         }
@@ -244,6 +250,35 @@
     }
   }
 
+  private boolean verifyCompanionClassInstanceIsKnownToBeNonNull(
+      FieldInstruction instruction,
+      DexEncodedField encodedField,
+      Set<Value> knownToBeNonNullValues) {
+    if (!appView.appInfo().hasLiveness()) {
+      return true;
+    }
+    if (instruction.isStaticGet()) {
+      AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+      DexField field = encodedField.field;
+      DexClass clazz = appViewWithLiveness.definitionFor(field.holder);
+      assert clazz != null;
+      if (clazz.accessFlags.isFinal()
+          && !clazz.initializationOfParentTypesMayHaveSideEffects(appViewWithLiveness)) {
+        DexEncodedMethod classInitializer = clazz.getClassInitializer();
+        if (classInitializer != null) {
+          TrivialInitializer info =
+              classInitializer.getOptimizationInfo().getTrivialInitializerInfo();
+          boolean expectedToBeNonNull =
+              info != null
+                  && info.asTrivialClassInitializer().field == field
+                  && !appViewWithLiveness.appInfo().isPinned(field);
+          assert !expectedToBeNonNull || knownToBeNonNullValues.contains(instruction.outValue());
+        }
+      }
+    }
+    return true;
+  }
+
   private void addNonNullForValues(
       IRCode code,
       ListIterator<BasicBlock> blockIterator,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 4837545..1845186 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -556,8 +556,10 @@
           templateInstructions.add(OutlineInstruction.fromInstruction(current));
         } else if (current.isConstInstruction()) {
           // Don't include const instructions in the template.
+        } else if (current.isAssume()) {
+          // Don't include assume instructions in the template.
         } else {
-          assert false : "Unexpected type of instruction in outlining template.";
+          assert false : "Unexpected type of instruction in outlining template:" + current;
         }
       }
     }
@@ -788,6 +790,12 @@
           include = true;
           instructionIncrement = 0;
         }
+      } else if (instruction.isAssume()) {
+        // Technically, assume instructions will be removed, and thus it should not be included.
+        // However, we should keep searching, so here we pretend to include it with 0 increment.
+        // When adding instruction into the outline candidate, we filter out assume instructions.
+        include = true;
+        instructionIncrement = 0;
       } else {
         include = canIncludeInstruction(instruction);
       }
@@ -986,12 +994,16 @@
 
     // Add the current instruction to the outline.
     private void includeInstruction(Instruction instruction) {
+      if (instruction.isAssume()) {
+        return;
+      }
+
       List<Value> inValues = orderedInValues(instruction, returnValue);
 
       Value prevReturnValue = returnValue;
       if (returnValue != null) {
         for (Value value : inValues) {
-          if (value == returnValue) {
+          if (value.getAliasedValue() == returnValue) {
             assert returnValueUsersLeft > 0;
             returnValueUsersLeft--;
           }
@@ -1013,7 +1025,7 @@
           || instruction.isArithmeticBinop();
       if (inValues.size() > 0) {
         for (int i = 0; i < inValues.size(); i++) {
-          Value value = inValues.get(i);
+          Value value = inValues.get(i).getAliasedValue();
           if (value == prevReturnValue) {
             argumentsMap.add(OutlineInstruction.OUTLINE_TEMP);
             continue;
@@ -1067,7 +1079,6 @@
       }
     }
 
-
     protected abstract void handle(int start, int end, Outline outline);
 
     private void candidate(int start, int index) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index b656678..7df3542 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -8,9 +8,11 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionOrPhi;
+import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
 import com.android.tools.r8.ir.optimize.CodeRewriter;
 import com.android.tools.r8.ir.optimize.Inliner;
@@ -18,9 +20,11 @@
 import com.android.tools.r8.ir.optimize.string.StringOptimizer;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
 import com.google.common.collect.Streams;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
@@ -34,8 +38,14 @@
     NON_CLASS_TYPE,
     UNKNOWN_TYPE,
 
+    // Used by isClassEligible
+    NON_PROGRAM_CLASS,
+    ABSTRACT_OR_INTERFACE,
+    NEVER_CLASS_INLINE,
+    HAS_FINALIZER,
+    TRIGGER_CLINIT,
+
     // Used by InlineCandidateProcessor#isClassAndUsageEligible
-    INELIGIBLE_CLASS,
     HAS_CLINIT,
     HAS_INSTANCE_FIELDS,
     NON_FINAL_TYPE,
@@ -46,7 +56,8 @@
   }
 
   private final LambdaRewriter lambdaRewriter;
-  private final ConcurrentHashMap<DexClass, Boolean> knownClasses = new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<DexClass, EligibilityStatus> knownClasses =
+      new ConcurrentHashMap<>();
 
   public ClassInliner(LambdaRewriter lambdaRewriter) {
     this.lambdaRewriter = lambdaRewriter;
@@ -56,7 +67,15 @@
       DexEncodedMethod context, Instruction root, EligibilityStatus status) {
     if (Log.ENABLED && Log.isLoggingEnabledFor(ClassInliner.class)) {
       Log.info(getClass(), "At %s,", context.toSourceString());
-      Log.info(getClass(), "ClassInlining eligibility of %s: %s,", root, status);
+      Log.info(getClass(), "ClassInlining eligibility of `%s`: %s.", root, status);
+    }
+  }
+
+  private void logIneligibleUser(
+      DexEncodedMethod context, Instruction root, InstructionOrPhi ineligibleUser) {
+    if (Log.ENABLED && Log.isLoggingEnabledFor(ClassInliner.class)) {
+      Log.info(getClass(), "At %s,", context.toSourceString());
+      Log.info(getClass(), "Ineligible user of `%s`: `%s`.", root, ineligibleUser);
     }
   }
 
@@ -133,7 +152,7 @@
   //       return 1;
   //     }
   //     static int method3() {
-  //       return "F::getX";
+  //       return 123;
   //     }
   //   }
   //
@@ -195,6 +214,7 @@
         InstructionOrPhi ineligibleUser = processor.areInstanceUsersEligible(defaultOracle);
         if (ineligibleUser != null) {
           // This root may succeed if users change in future.
+          logIneligibleUser(code.method, root, ineligibleUser);
           continue;
         }
 
@@ -208,7 +228,11 @@
         anyInlinedMethods |= processor.processInlining(code, defaultOracle);
 
         // Restore normality.
-        code.removeAllTrivialPhis();
+        Set<Value> affectedValues = Sets.newIdentityHashSet();
+        code.removeAllTrivialPhis(affectedValues);
+        if (!affectedValues.isEmpty()) {
+          new TypeAnalysis(appView).narrowing(affectedValues);
+        }
         assert code.isConsistentSSA();
         rootsIterator.remove();
         repeat = true;
@@ -233,11 +257,11 @@
     }
   }
 
-  private boolean isClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
-    Boolean eligible = knownClasses.get(clazz);
+  private EligibilityStatus isClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
+    EligibilityStatus eligible = knownClasses.get(clazz);
     if (eligible == null) {
-      boolean computed = computeClassEligible(appView, clazz);
-      Boolean existing = knownClasses.putIfAbsent(clazz, computed);
+      EligibilityStatus computed = computeClassEligible(appView, clazz);
+      EligibilityStatus existing = knownClasses.putIfAbsent(clazz, computed);
       assert existing == null || existing == computed;
       eligible = existing == null ? computed : existing;
     }
@@ -248,13 +272,19 @@
   //   - is not an abstract class or interface
   //   - does not declare finalizer
   //   - does not trigger any static initializers except for its own
-  private boolean computeClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
-    if (clazz == null
-        || clazz.isNotProgramClass()
-        || clazz.accessFlags.isAbstract()
-        || clazz.accessFlags.isInterface()
-        || appView.appInfo().neverClassInline.contains(clazz.type)) {
-      return false;
+  private EligibilityStatus computeClassEligible(
+      AppView<AppInfoWithLiveness> appView, DexClass clazz) {
+    if (clazz == null) {
+      return EligibilityStatus.UNKNOWN_TYPE;
+    }
+    if (clazz.isNotProgramClass()) {
+      return EligibilityStatus.NON_PROGRAM_CLASS;
+    }
+    if (clazz.isAbstract() || clazz.isInterface()) {
+      return EligibilityStatus.ABSTRACT_OR_INTERFACE;
+    }
+    if (appView.appInfo().neverClassInline.contains(clazz.type)) {
+      return EligibilityStatus.NEVER_CLASS_INLINE;
     }
 
     // Class must not define finalizer.
@@ -262,11 +292,14 @@
     for (DexEncodedMethod method : clazz.virtualMethods()) {
       if (method.method.name == dexItemFactory.finalizeMethodName
           && method.method.proto == dexItemFactory.objectMethods.finalize.proto) {
-        return false;
+        return EligibilityStatus.HAS_FINALIZER;
       }
     }
 
     // Check for static initializers in this class or any of interfaces it implements.
-    return !clazz.initializationOfParentTypesMayHaveSideEffects(appView);
+    if (clazz.initializationOfParentTypesMayHaveSideEffects(appView)) {
+      return EligibilityStatus.TRIGGER_CLINIT;
+    }
+    return EligibilityStatus.ELIGIBLE;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
index a16e9a3..d06b3f9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.classinliner;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 
 import com.android.tools.r8.graph.AppView;
@@ -14,6 +15,7 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.Phi.RegisterReadType;
 import com.android.tools.r8.ir.code.Value;
@@ -39,6 +41,9 @@
     this.code = code;
     this.root = root;
     this.appView = appView;
+    // Verify that `root` is not aliased.
+    assert root.hasOutValue();
+    assert root.outValue() == root.outValue().getAliasedValue();
   }
 
   void replaceValue(Value oldValue, Value newValue) {
@@ -122,10 +127,10 @@
       Instruction instruction = iterator.previous();
       assert instruction != null;
 
-      if (instruction == root ||
-          (instruction.isInstancePut() &&
-              instruction.asInstancePut().getField() == field &&
-              instruction.asInstancePut().object() == root.outValue())) {
+      if (instruction == root
+          || (instruction.isInstancePut()
+              && instruction.asInstancePut().getField() == field
+              && instruction.asInstancePut().object().getAliasedValue() == root.outValue())) {
         valueProducingInsn = instruction;
         break;
       }
@@ -140,12 +145,17 @@
 
     assert root == valueProducingInsn;
     if (defaultValue == null) {
+      InstructionListIterator it = block.listIterator(code, root);
       // If we met newInstance it means that default value is supposed to be used.
-      defaultValue =
-          code.createValue(TypeLatticeElement.fromDexType(field.type, maybeNull(), appView));
-      ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
-      defaultValueInsn.setPosition(root.getPosition());
-      block.listIterator(code, root).add(defaultValueInsn);
+      if (field.type.isPrimitiveType()) {
+        defaultValue = code.createValue(
+            TypeLatticeElement.fromDexType(field.type, definitelyNotNull(), appView));
+        ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
+        defaultValueInsn.setPosition(root.getPosition());
+        it.add(defaultValueInsn);
+      } else {
+        defaultValue = it.insertConstNullInstruction(code, appView.options());
+      }
     }
     return defaultValue;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index a2e9358..1318135 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.code.Assume;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.IRCode;
@@ -40,16 +41,18 @@
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.kotlin.KotlinInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 
@@ -60,7 +63,7 @@
   private final AppView<AppInfoWithLiveness> appView;
   private final LambdaRewriter lambdaRewriter;
   private final Inliner inliner;
-  private final Predicate<DexClass> isClassEligible;
+  private final Function<DexClass, EligibilityStatus> isClassEligible;
   private final Predicate<DexEncodedMethod> isProcessedConcurrently;
   private final DexEncodedMethod method;
   private final Instruction root;
@@ -83,7 +86,7 @@
       AppView<AppInfoWithLiveness> appView,
       LambdaRewriter lambdaRewriter,
       Inliner inliner,
-      Predicate<DexClass> isClassEligible,
+      Function<DexClass, EligibilityStatus> isClassEligible,
       Predicate<DexEncodedMethod> isProcessedConcurrently,
       DexEncodedMethod method,
       Instruction root) {
@@ -140,8 +143,9 @@
   //      * class has class initializer marked as TrivialClassInitializer, and
   //        class initializer initializes the field we are reading here.
   EligibilityStatus isClassAndUsageEligible() {
-    if (!isClassEligible.test(eligibleClassDefinition)) {
-      return EligibilityStatus.INELIGIBLE_CLASS;
+    EligibilityStatus status = isClassEligible.apply(eligibleClassDefinition);
+    if (status != EligibilityStatus.ELIGIBLE) {
+      return status;
     }
 
     if (root.isNewInstance()) {
@@ -251,7 +255,7 @@
    *
    * @return null if all users are eligible, or the first ineligible user.
    */
-  protected InstructionOrPhi areInstanceUsersEligible(Supplier<InliningOracle> defaultOracle) {
+  InstructionOrPhi areInstanceUsersEligible(Supplier<InliningOracle> defaultOracle) {
     // No Phi users.
     if (eligibleInstance.numberOfPhiUsers() > 0) {
       return eligibleInstance.firstPhiUser(); // Not eligible.
@@ -259,11 +263,19 @@
 
     Set<Instruction> currentUsers = eligibleInstance.uniqueUsers();
     while (!currentUsers.isEmpty()) {
-      Set<Instruction> indirectUsers = new HashSet<>();
+      Set<Instruction> indirectUsers = Sets.newIdentityHashSet();
       for (Instruction user : currentUsers) {
+        if (user.isAssume()) {
+          if (user.outValue().numberOfPhiUsers() > 0) {
+            return user.outValue().firstPhiUser(); // Not eligible.
+          }
+          indirectUsers.addAll(user.outValue().uniqueUsers());
+          continue;
+        }
         // Field read/write.
         if (user.isInstanceGet()
-            || (user.isInstancePut() && user.asInstancePut().value() != eligibleInstance)) {
+            || (user.isInstancePut()
+                && user.asInstancePut().value().getAliasedValue() != eligibleInstance)) {
           DexField field = user.asFieldInstruction().getField();
           if (field.holder == eligibleClass
               && eligibleClassDefinition.lookupInstanceField(field) != null) {
@@ -288,7 +300,7 @@
               boolean isCorrespondingConstructorCall =
                   root.isNewInstance()
                       && !invoke.inValues().isEmpty()
-                      && root.outValue() == invoke.inValues().get(0);
+                      && root.outValue() == invoke.getReceiver();
               if (isCorrespondingConstructorCall) {
                 InliningInfo inliningInfo =
                     isEligibleConstructorCall(user.asInvokeDirect(), singleTarget, defaultOracle);
@@ -343,6 +355,7 @@
 
   // Process inlining, includes the following steps:
   //
+  //  * remove linked assume instructions if any so that users of the eligible field are up-to-date.
   //  * replace unused instance usages as arguments which are never used
   //  * inline extra methods if any, collect new direct method calls
   //  * inline direct methods if any
@@ -352,6 +365,9 @@
   //
   // Returns `true` if at least one method was inlined.
   boolean processInlining(IRCode code, Supplier<InliningOracle> defaultOracle) {
+    // Verify that `eligibleInstance` is not aliased.
+    assert eligibleInstance == eligibleInstance.getAliasedValue();
+
     replaceUsagesAsUnusedArgument(code);
 
     boolean anyInlinedMethods = forceInlineExtraMethodInvocations(code);
@@ -374,11 +390,14 @@
         // methods that need to be inlined anyway.
         return true;
       }
-      assert extraMethodCalls.isEmpty();
-      assert unusedArguments.isEmpty();
+      assert extraMethodCalls.isEmpty()
+          : "Remaining extra method calls: " + StringUtils.join(extraMethodCalls.entrySet(), ", ");
+      assert unusedArguments.isEmpty()
+          : "Remaining unused arg: " + StringUtils.join(unusedArguments, ", ");
     }
 
     anyInlinedMethods |= forceInlineDirectMethodInvocations(code);
+    removeAssumeInstructionsLinkedToEligibleInstance();
     removeMiscUsages(code);
     removeFieldReads(code);
     removeFieldWrites();
@@ -419,6 +438,22 @@
     return true;
   }
 
+  private void removeAssumeInstructionsLinkedToEligibleInstance() {
+    for (Instruction user : eligibleInstance.aliasedUsers()) {
+      if (!user.isAssume()) {
+        continue;
+      }
+      Assume<?> assumeInstruction = user.asAssume();
+      Value src = assumeInstruction.src();
+      Value dest = assumeInstruction.outValue();
+      assert dest.numberOfPhiUsers() == 0;
+      dest.replaceUsers(src);
+      removeInstruction(user);
+    }
+    // Verify that no more assume instructions are left as users.
+    assert eligibleInstance.aliasedUsers().stream().noneMatch(Instruction::isAssume);
+  }
+
   // Remove miscellaneous users before handling field reads.
   private void removeMiscUsages(IRCode code) {
     boolean needToRemoveUnreachableBlocks = false;
@@ -495,8 +530,8 @@
     }
   }
 
-  private void replaceFieldRead(IRCode code,
-      InstanceGet fieldRead, Map<DexField, FieldValueHelper> fieldHelpers) {
+  private void replaceFieldRead(
+      IRCode code, InstanceGet fieldRead, Map<DexField, FieldValueHelper> fieldHelpers) {
     Value value = fieldRead.outValue();
     if (value != null) {
       FieldValueHelper helper =
@@ -508,7 +543,10 @@
         fieldValueHelper.replaceValue(value, newValue);
       }
       assert value.numberOfAllUsers() == 0;
-      new TypeAnalysis(appView).narrowing(newValue.affectedValues());
+      // `newValue` could be a phi introduced by FieldValueHelper. Its initial type is set as the
+      // type of read field, but it could be more precise than that due to (multiple) inlining.
+      // Instead of values affected by `newValue`, it's necessary to begin with `newValue` itself.
+      new TypeAnalysis(appView).narrowing(ImmutableSet.of(newValue));
     }
     removeInstruction(fieldRead);
   }
@@ -539,7 +577,8 @@
     assert isEligibleSingleTarget(singleTarget);
 
     // Must be a constructor called on the receiver.
-    if (invoke.inValues().lastIndexOf(eligibleInstance) != 0) {
+    if (ListUtils.lastIndexMatching(
+        invoke.inValues(), v -> v.getAliasedValue() == eligibleInstance) != 0) {
       return null;
     }
 
@@ -594,7 +633,7 @@
         : null;
   }
 
-  // An invoke is eligible for inlinining in the following cases:
+  // An invoke is eligible for inlining in the following cases:
   //
   // - if it does not return the receiver
   // - if there are no uses of the out value
@@ -645,7 +684,8 @@
       DexEncodedMethod singleTarget,
       Set<Instruction> indirectUsers) {
     assert isEligibleSingleTarget(singleTarget);
-    if (invoke.inValues().lastIndexOf(eligibleInstance) > 0) {
+    if (ListUtils.lastIndexMatching(
+        invoke.inValues(), v -> v.getAliasedValue() == eligibleInstance) > 0) {
       return null; // Instance passed as an argument.
     }
     return isEligibleVirtualMethodCall(
@@ -714,7 +754,8 @@
       return false;
     }
     if (invoke.isInvokeMethodWithReceiver()
-        && invoke.asInvokeMethodWithReceiver().getReceiver() == eligibleInstance) {
+        && invoke.asInvokeMethodWithReceiver().getReceiver().getAliasedValue()
+            == eligibleInstance) {
       return false;
     }
     if (invoke.isInvokeSuper()) {
@@ -755,7 +796,7 @@
 
     // If we got here with invocation on receiver the user is ineligible.
     if (invoke.isInvokeMethodWithReceiver()) {
-      if (arguments.get(0) == eligibleInstance) {
+      if (arguments.get(0).getAliasedValue() == eligibleInstance) {
         return false;
       }
 
@@ -775,7 +816,7 @@
     }
 
     for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
-      Value argument = arguments.get(argIndex);
+      Value argument = arguments.get(argIndex).getAliasedValue();
       if (argument == eligibleInstance && optimizationInfo.getParameterUsages(argIndex).notUsed()) {
         // Reference can be removed since it's not used.
         unusedArguments.add(new Pair<>(invoke, argIndex));
@@ -796,7 +837,7 @@
       Supplier<InliningOracle> defaultOracle) {
     // Go through all arguments, see if all usages of eligibleInstance are good.
     for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
-      Value argument = arguments.get(argIndex);
+      Value argument = arguments.get(argIndex).getAliasedValue();
       if (argument != eligibleInstance) {
         continue; // Nothing to worry about.
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index e77f932..1eb64de 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -284,10 +284,9 @@
    */
   private final Set<DexEncodedMethod> pendingReflectiveUses = Sets.newLinkedHashSet();
 
-  /**
-   * A cache for DexMethod that have been marked reachable.
-   */
-  private final Set<DexMethod> virtualTargetsMarkedAsReachable = Sets.newIdentityHashSet();
+  /** A cache for DexMethod that have been marked reachable. */
+  private final Map<DexMethod, DexEncodedMethod> virtualTargetsMarkedAsReachable =
+      Maps.newIdentityHashMap();
 
   /**
    * A set of references we have reported missing to dedupe warnings.
@@ -596,9 +595,14 @@
 
     private final DexEncodedMethod currentMethod;
 
-    private UseRegistry(DexItemFactory factory, DexEncodedMethod currentMethod) {
+    private UseRegistry(DexItemFactory factory, DexProgramClass holder, DexEncodedMethod method) {
       super(factory);
-      this.currentMethod = currentMethod;
+      assert holder.type == method.method.holder;
+      this.currentMethod = method;
+    }
+
+    private KeepReasonWitness reportClassReferenced(DexProgramClass referencedClass) {
+      return graphReporter.reportClassReferencedFrom(referencedClass, currentMethod);
     }
 
     @Override
@@ -744,10 +748,8 @@
       // the field as live, if the holder is an interface.
       if (appView.options().enableUnusedInterfaceRemoval) {
         if (encodedField.field != field) {
-          markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, currentMethod));
-          markTypeAsLive(
-              encodedField.field.type,
-              type -> graphReporter.reportClassReferencedFrom(type, currentMethod));
+          markTypeAsLive(clazz, reportClassReferenced(clazz));
+          markTypeAsLive(encodedField.field.type, this::reportClassReferenced);
         }
       }
 
@@ -784,10 +786,8 @@
       // the field as live, if the holder is an interface.
       if (appView.options().enableUnusedInterfaceRemoval) {
         if (encodedField.field != field) {
-          markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, currentMethod));
-          markTypeAsLive(
-              encodedField.field.type,
-              type -> graphReporter.reportClassReferencedFrom(type, currentMethod));
+          markTypeAsLive(clazz, reportClassReferenced(clazz));
+          markTypeAsLive(encodedField.field.type, this::reportClassReferenced);
         }
       }
 
@@ -923,7 +923,7 @@
 
     @Override
     public boolean registerTypeReference(DexType type) {
-      markTypeAsLive(type, clazz -> graphReporter.reportClassReferencedFrom(clazz, currentMethod));
+      markTypeAsLive(type, this::reportClassReferenced);
       return true;
     }
 
@@ -1056,8 +1056,7 @@
           markClassAsInstantiatedWithCompatRule(baseClass);
         } else {
           // This also handles reporting of missing classes.
-          markTypeAsLive(
-              baseType, clazz -> graphReporter.reportClassReferencedFrom(clazz, currentMethod));
+          markTypeAsLive(baseType, this::reportClassReferenced);
         }
         return true;
       }
@@ -1847,9 +1846,6 @@
       boolean interfaceInvoke,
       KeepReason reason,
       BiPredicate<DexProgramClass, DexEncodedMethod> possibleTargetsFilter) {
-    if (!virtualTargetsMarkedAsReachable.add(method)) {
-      return;
-    }
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Marking virtual method `%s` as reachable.", method);
     }
@@ -1867,8 +1863,13 @@
       return;
     }
 
-    DexEncodedMethod resolutionTarget =
-        findAndMarkResolutionTarget(method, interfaceInvoke, reason);
+    DexEncodedMethod resolutionTarget = virtualTargetsMarkedAsReachable.get(method);
+    if (resolutionTarget != null) {
+      registerMethod(resolutionTarget, reason);
+      return;
+    }
+    resolutionTarget = findAndMarkResolutionTarget(method, interfaceInvoke, reason);
+    virtualTargetsMarkedAsReachable.put(method, resolutionTarget);
     if (resolutionTarget == null || !resolutionTarget.isValidVirtualTarget(options)) {
       // There is no valid resolution, so any call will lead to a runtime exception.
       return;
@@ -2514,7 +2515,7 @@
       method.parameterAnnotationsList.forEachAnnotation(
           annotation -> processAnnotation(method, annotation));
     }
-    method.registerCodeReferences(new UseRegistry(options.itemFactory, method));
+    method.registerCodeReferences(new UseRegistry(options.itemFactory, clazz, method));
 
     // Add all dependent members to the workqueue.
     enqueueRootItems(rootSet.getDependentItems(method));
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index 7f16e07..487afa2 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -256,6 +256,11 @@
     if (appView.appInfo().neverMerge.contains(clazz.type)) {
       return MergeGroup.DONT_MERGE;
     }
+    if (appView.options().featureSplitConfiguration != null &&
+        appView.options().featureSplitConfiguration.isInFeature(clazz)) {
+      // TODO(b/141452765): Allow class merging between classes in features.
+      return MergeGroup.DONT_MERGE;
+    }
     if (clazz.staticFields().size() + clazz.directMethods().size() + clazz.virtualMethods().size()
         == 0) {
       return MergeGroup.DONT_MERGE;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index cd77544..b8b8482 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -374,6 +374,12 @@
         || appInfo.neverMerge.contains(clazz.type)) {
       return false;
     }
+    if (appView.options().featureSplitConfiguration != null &&
+        appView.options().featureSplitConfiguration.isInFeature(clazz)) {
+      // TODO(b/141452765): Allow class merging between classes in features.
+      return false;
+    }
+
     // Note that the property "singleSubtype == null" cannot change during merging, since we visit
     // classes in a top-down order.
     DexType singleSubtype = appInfo.getSingleSubtype(clazz.type);
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 2e45d5f..9c135c8 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -239,7 +239,6 @@
   public boolean enableInitializedClassesInInstanceMethodsAnalysis = true;
   public boolean enableRedundantFieldLoadElimination = true;
   public boolean enableValuePropagation = true;
-  public boolean enableFieldTypePropagation = true;
   public boolean enableUninstantiatedTypeOptimization = true;
   // TODO(b/138917494): Disable until we have numbers on potential performance penalties.
   public boolean enableRedundantConstNumberOptimization = false;
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index a9c3f98..370d5e2 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -8,9 +8,19 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 public class ListUtils {
 
+  public static <T> int lastIndexMatching(List<T> list, Predicate<T> tester) {
+    for (int i = list.size() - 1; i >= 0; i--) {
+      if (tester.test(list.get(i))) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
   public static <S, T> List<T> map(Collection<S> list, Function<S, T> fn) {
     List<T> result = new ArrayList<>(list.size());
     for (S element : list) {
diff --git a/src/test/desugaredLibraryConversions/conversions/TimeConversions.java b/src/test/desugaredLibraryConversions/conversions/TimeConversions.java
new file mode 100644
index 0000000..b5adf3c
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/conversions/TimeConversions.java
@@ -0,0 +1,114 @@
+// 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 java.time;
+
+public class TimeConversions {
+
+  public static j$.time.ZonedDateTime convert(java.time.ZonedDateTime dateTime) {
+    if (dateTime == null) {
+      return null;
+    }
+    return j$.time.ZonedDateTime.of(
+        dateTime.getYear(),
+        dateTime.getMonthValue(),
+        dateTime.getDayOfMonth(),
+        dateTime.getHour(),
+        dateTime.getMinute(),
+        dateTime.getSecond(),
+        dateTime.getNano(),
+        convert(dateTime.getZone()));
+  }
+
+  public static java.time.ZonedDateTime convert(j$.time.ZonedDateTime dateTime) {
+    if (dateTime == null) {
+      return null;
+    }
+    return java.time.ZonedDateTime.of(
+        dateTime.getYear(),
+        dateTime.getMonthValue(),
+        dateTime.getDayOfMonth(),
+        dateTime.getHour(),
+        dateTime.getMinute(),
+        dateTime.getSecond(),
+        dateTime.getNano(),
+        convert(dateTime.getZone()));
+  }
+
+  // ZoneId conversion works because in practice only two final classes are used.
+  // ZoneId is responsible for using one of the other final classes.
+  // This does not support custom implementations of ZoneId.
+
+  public static j$.time.ZoneId convert(java.time.ZoneId zoneId) {
+    if (zoneId == null) {
+      return null;
+    }
+    return j$.time.ZoneId.of(zoneId.getId());
+  }
+
+  public static java.time.ZoneId convert(j$.time.ZoneId zoneId) {
+    if (zoneId == null) {
+      return null;
+    }
+    return java.time.ZoneId.of(zoneId.getId());
+  }
+
+  public static j$.time.MonthDay convert(java.time.MonthDay monthDay) {
+    if (monthDay == null) {
+      return null;
+    }
+    return j$.time.MonthDay.of(monthDay.getMonthValue(), monthDay.getDayOfMonth());
+  }
+
+  public static java.time.MonthDay convert(j$.time.MonthDay monthDay) {
+    if (monthDay == null) {
+      return null;
+    }
+    return java.time.MonthDay.of(monthDay.getMonthValue(), monthDay.getDayOfMonth());
+  }
+
+  public static j$.time.Instant convert(java.time.Instant instant) {
+    if (instant == null) {
+      return null;
+    }
+    return j$.time.Instant.ofEpochSecond(instant.getEpochSecond(), (long) instant.getNano());
+  }
+
+  public static java.time.Instant convert(j$.time.Instant instant) {
+    if (instant == null) {
+      return null;
+    }
+    return java.time.Instant.ofEpochSecond(instant.getEpochSecond(), (long) instant.getNano());
+  }
+
+  // Following conversions are hidden (Used by tests APIs only).
+
+  public static j$.time.LocalDate convert(java.time.LocalDate date) {
+    if (date == null) {
+      return null;
+    }
+    return j$.time.LocalDate.of(date.getYear(), date.getMonthValue(), date.getDayOfMonth());
+  }
+
+  public static java.time.LocalDate convert(j$.time.LocalDate date) {
+    if (date == null) {
+      return null;
+    }
+    return java.time.LocalDate.of(date.getYear(), date.getMonthValue(), date.getDayOfMonth());
+  }
+
+  public static j$.time.Duration convert(java.time.Duration duration) {
+    if (duration == null) {
+      return null;
+    }
+    return j$.time.Duration.ofSeconds(duration.getSeconds(), duration.getNano());
+  }
+
+  public static java.time.Duration convert(j$.time.Duration duration) {
+    if (duration == null) {
+      return null;
+    }
+    return java.time.Duration.ofSeconds(duration.getSeconds(), duration.getNano());
+  }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/Clock.java b/src/test/desugaredLibraryConversions/stubs/Clock.java
new file mode 100644
index 0000000..51f6e52
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/Clock.java
@@ -0,0 +1,7 @@
+// 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 j$.time;
+
+public class Clock {}
diff --git a/src/test/desugaredLibraryConversions/stubs/Duration.java b/src/test/desugaredLibraryConversions/stubs/Duration.java
new file mode 100644
index 0000000..e559a8f
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/Duration.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public class Duration {
+  public static Duration ofSeconds(long seconds, long nanoAdjustment) {
+    return null;
+  }
+
+  public int getSeconds() {
+    return 0;
+  }
+
+  public int getNano() {
+    return 0;
+  }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/Instant.java b/src/test/desugaredLibraryConversions/stubs/Instant.java
new file mode 100644
index 0000000..bc399ac
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/Instant.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public class Instant {
+
+  public static Instant ofEpochSecond(long seconds, long nanos) {
+    return null;
+  }
+
+  public long getEpochSecond() {
+    return 0L;
+  }
+
+  public int getNano() {
+    return 0;
+  }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/LocalDate.java b/src/test/desugaredLibraryConversions/stubs/LocalDate.java
new file mode 100644
index 0000000..ddbff3b
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/LocalDate.java
@@ -0,0 +1,23 @@
+// 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 j$.time;
+
+public class LocalDate {
+  public static LocalDate of(int year, int month, int dayOfMonth) {
+    return null;
+  }
+
+  public int getYear() {
+    return 0;
+  }
+
+  public int getMonthValue() {
+    return 0;
+  }
+
+  public int getDayOfMonth() {
+    return 0;
+  }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/MonthDay.java b/src/test/desugaredLibraryConversions/stubs/MonthDay.java
new file mode 100644
index 0000000..25a0392
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/MonthDay.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public final class MonthDay {
+  public static MonthDay of(int monthValue, int dayValue) {
+    return null;
+  }
+
+  public int getMonthValue() {
+    return 0;
+  }
+
+  public int getDayOfMonth() {
+    return 0;
+  }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/ZoneId.java b/src/test/desugaredLibraryConversions/stubs/ZoneId.java
new file mode 100644
index 0000000..27f7230
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/ZoneId.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public final class ZoneId {
+
+  public static ZoneId of(String id) {
+    return null;
+  }
+
+  public String getId() {
+    return null;
+  }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/ZonedDateTime.java b/src/test/desugaredLibraryConversions/stubs/ZonedDateTime.java
new file mode 100644
index 0000000..ff02570
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/ZonedDateTime.java
@@ -0,0 +1,52 @@
+// 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 j$.time;
+
+public final class ZonedDateTime {
+
+  public static ZonedDateTime of(
+      int year,
+      int month,
+      int dayOfMonth,
+      int hour,
+      int minute,
+      int second,
+      int nanoOfSecond,
+      ZoneId zone) {
+    return null;
+  }
+
+  public int getYear() {
+    return 0;
+  }
+
+  public int getMonthValue() {
+    return 0;
+  }
+
+  public int getDayOfMonth() {
+    return 0;
+  }
+
+  public int getHour() {
+    return 0;
+  }
+
+  public int getMinute() {
+    return 0;
+  }
+
+  public int getSecond() {
+    return 0;
+  }
+
+  public int getNano() {
+    return 0;
+  }
+
+  public j$.time.ZoneId getZone() {
+    return null;
+  }
+}
diff --git a/src/test/examplesJava9/backport/MapBackportJava9Main.java b/src/test/examplesJava9/backport/MapBackportJava9Main.java
new file mode 100644
index 0000000..2aa5cfd
--- /dev/null
+++ b/src/test/examplesJava9/backport/MapBackportJava9Main.java
@@ -0,0 +1,247 @@
+// 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 backport;
+
+import java.util.AbstractMap;
+import java.util.Map;
+
+public class MapBackportJava9Main {
+
+  public static void main(String[] args) {
+    testOf0();
+    testOf1();
+    testOf2();
+    testOf10();
+    testOfEntries();
+  }
+
+  private static void testOf0() {
+    Map<Object, Object> ofObject = Map.of();
+    assertEquals(0, ofObject.size());
+    assertEquals(null, ofObject.get(new Object()));
+    assertMutationNotAllowed(ofObject);
+
+    Map<Integer, Integer> ofInteger = Map.of();
+    assertEquals(0, ofInteger.size());
+    assertEquals(null, ofInteger.get(0));
+  }
+
+  private static void testOf1() {
+    Object objectKey0 = new Object();
+    Object objectValue0 = new Object();
+    Map<Object, Object> ofObject = Map.of(objectKey0, objectValue0);
+    assertEquals(1, ofObject.size());
+    assertSame(objectValue0, ofObject.get(objectKey0));
+    assertEquals(null, ofObject.get(new Object()));
+    assertMutationNotAllowed(ofObject);
+
+    Map<Integer, Integer> ofInteger = Map.of(0, 0);
+    assertEquals(1, ofInteger.size());
+    assertEquals(0, ofInteger.get(0));
+    assertEquals(null, ofInteger.get(1));
+
+    try {
+      Map.of((Object) null, 1);
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+    try {
+      Map.of(1, (Object) null);
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  private static void testOf2() {
+    Object objectKey0 = new Object();
+    Object objectValue0 = new Object();
+    Object objectKey1 = new Object();
+    Object objectValue1 = new Object();
+    Map<Object, Object> ofObject = Map.of(objectKey0, objectValue0, objectKey1, objectValue1);
+    assertEquals(2, ofObject.size());
+    assertSame(objectValue0, ofObject.get(objectKey0));
+    assertSame(objectValue1, ofObject.get(objectKey1));
+    assertEquals(null, ofObject.get(new Object()));
+    assertMutationNotAllowed(ofObject);
+
+    Map<Integer, Integer> ofInteger = Map.of(0, 0, 1, 1);
+    assertEquals(2, ofInteger.size());
+    assertEquals(0, ofInteger.get(0));
+    assertEquals(1, ofInteger.get(1));
+    assertEquals(null, ofInteger.get(3));
+
+    Map<Object, Object> ofMixed = Map.of(objectKey0, 0, objectKey1, 1);
+    assertEquals(2, ofMixed.size());
+    assertEquals(0, ofMixed.get(objectKey0));
+    assertEquals(1, ofMixed.get(objectKey1));
+    assertEquals(null, ofMixed.get(new Object()));
+
+    try {
+      Map.of(1, 1, null, 2);
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+    try {
+      Map.of(1, 1, 2, null);
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+
+    try {
+      Map.of(1, 1, 1, 2);
+      throw new AssertionError();
+    } catch (IllegalArgumentException expected) {
+      assertEquals("duplicate key: 1", expected.getMessage());
+    }
+  }
+
+  private static void testOf10() {
+    Object objectKey0 = new Object();
+    Object objectValue0 = new Object();
+    Object objectKey6 = new Object();
+    Object objectValue6 = new Object();
+    Object objectKey9 = new Object();
+    Object objectValue9 = new Object();
+    Map<Object, Object> ofObject =
+        Map.of(objectKey0, objectValue0, new Object(), new Object(), new Object(), new Object(),
+            new Object(), new Object(), new Object(), new Object(), new Object(), new Object(),
+            objectKey6, objectValue6, new Object(), new Object(), new Object(), new Object(),
+            objectKey9, objectValue9);
+    assertEquals(10, ofObject.size());
+    assertSame(objectValue0, ofObject.get(objectKey0));
+    assertSame(objectValue6, ofObject.get(objectKey6));
+    assertSame(objectValue9, ofObject.get(objectKey9));
+    assertEquals(null, ofObject.get(new Object()));
+    assertMutationNotAllowed(ofObject);
+
+    Map<Integer, Integer> ofInteger =
+        Map.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9);
+    assertEquals(10, ofInteger.size());
+    assertEquals(0, ofInteger.get(0));
+    assertEquals(6, ofInteger.get(6));
+    assertEquals(9, ofInteger.get(9));
+    assertEquals(null, ofInteger.get(10));
+
+    Map<Object, Object> ofMixed =
+        Map.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, objectKey9, objectValue9);
+    assertEquals(10, ofMixed.size());
+    assertEquals(0, ofMixed.get(0));
+    assertEquals(6, ofMixed.get(6));
+    assertSame(objectValue9, ofMixed.get(objectKey9));
+    assertEquals(null, ofMixed.get(9));
+
+    try {
+      Map.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, null, objectValue9);
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+    try {
+      Map.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, objectKey9, null);
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+
+    try {
+      Map.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 0, 9);
+      throw new AssertionError();
+    } catch (IllegalArgumentException expected) {
+      assertEquals("duplicate key: 0", expected.getMessage());
+    }
+  }
+
+  private static void testOfEntries() {
+    Object objectKey0 = new Object();
+    Object objectValue0 = new Object();
+    Object objectKey1 = new Object();
+    Object objectValue1 = new Object();
+    Map<Object, Object> ofObject = Map.ofEntries(
+        new AbstractMap.SimpleEntry<>(objectKey0, objectValue0),
+        new AbstractMap.SimpleEntry<>(objectKey1, objectValue1));
+    assertEquals(2, ofObject.size());
+    assertSame(objectValue0, ofObject.get(objectKey0));
+    assertSame(objectValue1, ofObject.get(objectKey1));
+    assertEquals(null, ofObject.get(new Object()));
+    assertMutationNotAllowed(ofObject);
+
+    Map<Integer, Integer> ofInteger = Map.ofEntries(
+        new AbstractMap.SimpleEntry<>(0, 0),
+        new AbstractMap.SimpleEntry<>(1, 1));
+    assertEquals(2, ofInteger.size());
+    assertEquals(0, ofInteger.get(0));
+    assertEquals(1, ofInteger.get(1));
+    assertEquals(null, ofInteger.get(2));
+
+    Map<Object, Object> ofMixed = Map.ofEntries(
+        new AbstractMap.SimpleEntry<>(0, objectValue0),
+        new AbstractMap.SimpleEntry<>(objectKey1, 1));
+    assertEquals(2, ofMixed.size());
+    assertSame(objectValue0, ofMixed.get(0));
+    assertEquals(1, ofMixed.get(objectKey1));
+    assertEquals(null, ofMixed.get(1));
+
+    // Ensure the supplied entry objects are not used directly since they are mutable.
+    Map.Entry<Object, Object> mutableEntry =
+        new AbstractMap.SimpleEntry<>(objectKey0, objectValue0);
+    Map<Object, Object> ofMutableEntry = Map.ofEntries(mutableEntry);
+    mutableEntry.setValue(objectValue1);
+    assertSame(objectValue0, ofMutableEntry.get(objectKey0));
+
+    // Ensure the supplied mutable array is not used directly since it is mutable.
+    @SuppressWarnings("unchecked")
+    Map.Entry<Object, Object>[] mutableArray =
+        new Map.Entry[] { new AbstractMap.SimpleEntry<>(objectKey0, objectValue0) };
+    Map<Object, Object> ofArray = Map.ofEntries(mutableArray);
+    mutableArray[0] = new AbstractMap.SimpleEntry<>(objectKey1, objectValue1);
+    assertSame(objectValue0, ofArray.get(objectKey0));
+    assertEquals(null, ofArray.get(objectKey1));
+
+    try {
+      Map.ofEntries(new AbstractMap.SimpleEntry<Object, Integer>(null, 1));
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+    try {
+      Map.ofEntries(new AbstractMap.SimpleEntry<Object, Integer>(1, null));
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+
+    try {
+      Map.ofEntries(
+          new AbstractMap.SimpleEntry<>(0, objectValue0),
+          new AbstractMap.SimpleEntry<>(0, objectValue1));
+      throw new AssertionError();
+    } catch (IllegalArgumentException expected) {
+      assertEquals("duplicate key: 0", expected.getMessage());
+    }
+  }
+
+  private static void assertMutationNotAllowed(Map<Object, Object> ofObject) {
+    try {
+      ofObject.put(new Object(), new Object());
+      throw new AssertionError();
+    } catch (UnsupportedOperationException expected) {
+    }
+    for (Map.Entry<Object, Object> entry : ofObject.entrySet()) {
+      try {
+        entry.setValue(new Object());
+        throw new AssertionError();
+      } catch (UnsupportedOperationException expected) {
+      }
+    }
+  }
+
+  private static void assertSame(Object expected, Object actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+    }
+  }
+
+  private static void assertEquals(Object expected, Object actual) {
+    if (expected != actual && !expected.equals(actual)) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+    }
+  }
+}
diff --git a/src/test/examplesJava9/backport/SetBackportJava9Main.java b/src/test/examplesJava9/backport/SetBackportJava9Main.java
new file mode 100644
index 0000000..d824bd0
--- /dev/null
+++ b/src/test/examplesJava9/backport/SetBackportJava9Main.java
@@ -0,0 +1,207 @@
+// 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 backport;
+
+import java.util.Set;
+
+public class SetBackportJava9Main {
+
+  public static void main(String[] args) {
+    testOf0();
+    testOf1();
+    testOf2();
+    testOf10();
+    testOfVarargs();
+  }
+
+  private static void testOf0() {
+    Set<Object> ofObject = Set.of();
+    assertEquals(0, ofObject.size());
+    assertFalse(ofObject.contains(new Object()));
+    assertMutationNotAllowed(ofObject);
+
+    Set<Integer> ofInteger = Set.of();
+    assertEquals(0, ofInteger.size());
+    assertFalse(ofInteger.contains(0));
+  }
+
+  private static void testOf1() {
+    Object anObject = new Object();
+    Set<Object> ofObject = Set.of(anObject);
+    assertEquals(1, ofObject.size());
+    assertTrue(ofObject.contains(anObject));
+    assertFalse(ofObject.contains(new Object()));
+    assertMutationNotAllowed(ofObject);
+
+    Set<Integer> ofInteger = Set.of(1);
+    assertEquals(1, ofInteger.size());
+    assertTrue(ofInteger.contains(1));
+    assertFalse(ofInteger.contains(2));
+
+    try {
+      Set.of((Object) null);
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  private static void testOf2() {
+    Object anObject0 = new Object();
+    Object anObject1 = new Object();
+    Set<Object> ofObject = Set.of(anObject0, anObject1);
+    assertEquals(2, ofObject.size());
+    assertTrue(ofObject.contains(anObject0));
+    assertTrue(ofObject.contains(anObject1));
+    assertFalse(ofObject.contains(new Object()));
+    assertMutationNotAllowed(ofObject);
+
+    Set<Integer> ofInteger = Set.of(1, 2);
+    assertEquals(2, ofInteger.size());
+    assertTrue(ofInteger.contains(1));
+    assertTrue(ofInteger.contains(2));
+    assertFalse(ofInteger.contains(3));
+
+    Set<Object> ofMixed = Set.of(anObject0, 1);
+    assertEquals(2, ofMixed.size());
+    assertTrue(ofMixed.contains(anObject0));
+    assertTrue(ofMixed.contains(1));
+    assertFalse(ofMixed.contains(2));
+    assertFalse(ofMixed.contains(anObject1));
+    assertMutationNotAllowed(ofMixed);
+
+    try {
+      Set.of(1, null);
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+
+    try {
+      Set.of(1, 1);
+      throw new AssertionError();
+    } catch (IllegalArgumentException expected) {
+      assertEquals("duplicate element: 1", expected.getMessage());
+    }
+  }
+
+  private static void testOf10() {
+    Object anObject0 = new Object();
+    Object anObject6 = new Object();
+    Object anObject9 = new Object();
+    Set<Object> ofObject =
+        Set.of(anObject0, new Object(), new Object(), new Object(), new Object(), new Object(),
+            anObject6, new Object(), new Object(), anObject9);
+    assertEquals(10, ofObject.size());
+    assertTrue(ofObject.contains(anObject0));
+    assertTrue(ofObject.contains(anObject6));
+    assertTrue(ofObject.contains(anObject9));
+    assertFalse(ofObject.contains(new Object()));
+    assertMutationNotAllowed(ofObject);
+
+    Set<Integer> ofInteger = Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+    assertEquals(10, ofInteger.size());
+    assertTrue(ofInteger.contains(0));
+    assertTrue(ofInteger.contains(6));
+    assertTrue(ofInteger.contains(9));
+    assertFalse(ofInteger.contains(10));
+
+    Set<Object> ofMixed = Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, anObject9);
+    assertEquals(10, ofMixed.size());
+    assertTrue(ofMixed.contains(0));
+    assertTrue(ofMixed.contains(6));
+    assertTrue(ofMixed.contains(anObject9));
+    assertFalse(ofMixed.contains(anObject0));
+    assertMutationNotAllowed(ofMixed);
+
+    try {
+      Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, null);
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+
+    try {
+      Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 0);
+      throw new AssertionError();
+    } catch (IllegalArgumentException expected) {
+      assertEquals("duplicate element: 0", expected.getMessage());
+    }
+  }
+
+  private static void testOfVarargs() {
+    Object anObject0 = new Object();
+    Object anObject6 = new Object();
+    Object anObject10 = new Object();
+    Set<Object> ofObject =
+        Set.of(anObject0, new Object(), new Object(), new Object(), new Object(), new Object(),
+            anObject6, new Object(), new Object(), new Object(), anObject10);
+    assertEquals(11, ofObject.size());
+    assertTrue(ofObject.contains(anObject0));
+    assertTrue(ofObject.contains(anObject6));
+    assertTrue(ofObject.contains(anObject10));
+    assertFalse(ofObject.contains(new Object()));
+    assertMutationNotAllowed(ofObject);
+
+    Set<Integer> ofInteger = Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+    assertEquals(11, ofInteger.size());
+    assertTrue(ofInteger.contains(0));
+    assertTrue(ofInteger.contains(6));
+    assertTrue(ofInteger.contains(10));
+    assertFalse(ofInteger.contains(11));
+
+    Set<Object> ofMixed = Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, anObject10);
+    assertEquals(11, ofMixed.size());
+    assertTrue(ofMixed.contains(0));
+    assertTrue(ofMixed.contains(6));
+    assertTrue(ofMixed.contains(anObject10));
+    assertFalse(ofMixed.contains(10));
+    assertFalse(ofMixed.contains(anObject0));
+    assertMutationNotAllowed(ofMixed);
+
+    // Ensure the supplied mutable array is not used directly since it is mutable.
+    Object[] mutableArray = { anObject0 };
+    Set<Object> ofMutableArray = Set.of(mutableArray);
+    mutableArray[0] = anObject10;
+    assertTrue(ofMutableArray.contains(anObject0));
+    assertFalse(ofMutableArray.contains(anObject10));
+
+    try {
+      Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, null);
+      throw new AssertionError();
+    } catch (NullPointerException expected) {
+    }
+
+    try {
+      Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+      throw new AssertionError();
+    } catch (IllegalArgumentException expected) {
+      assertEquals("duplicate element: 0", expected.getMessage());
+    }
+  }
+
+  private static void assertMutationNotAllowed(Set<Object> ofObject) {
+    try {
+      ofObject.add(new Object());
+      throw new AssertionError();
+    } catch (UnsupportedOperationException expected) {
+    }
+  }
+
+  private static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("Expected <true> but was <false>");
+    }
+  }
+
+  private static void assertFalse(boolean value) {
+    if (value) {
+      throw new AssertionError("Expected <false> but was <true>");
+    }
+  }
+
+  private static void assertEquals(Object expected, Object actual) {
+    if (expected != actual && !expected.equals(actual)) {
+      throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index 6cd2bdf..fee1235 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -93,7 +93,7 @@
           .assertSuccess();
     } else {
       testForD8()
-          .setMinApi(parameters.getRuntime())
+          .setMinApi(parameters.getApiLevel())
           .apply(this::configureProgram)
           .compile()
           .run(parameters.getRuntime(), testClassName)
@@ -117,7 +117,7 @@
         .filter(is -> !ignoredInvokes.contains(is.getMethod().name.toString()))
         .collect(toList());
 
-    AndroidApiLevel apiLevel = parameters.getRuntime().asDex().getMinApiLevel();
+    AndroidApiLevel apiLevel = parameters.getApiLevel();
     long expectedTargetInvokes = invokeStaticCounts.ceilingEntry(apiLevel).getValue();
     long actualTargetInvokes = javaInvokeStatics.size();
     assertEquals("Expected "
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
index edc9e53..3e4b973 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
@@ -14,7 +14,9 @@
 public final class BooleanBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public BooleanBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
index fd03159..28eefe3 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
@@ -22,6 +22,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .withDexRuntimes()
+        .withAllApiLevels()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
index 82d143c..13a4564 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
@@ -14,7 +14,9 @@
 public final class ByteBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public ByteBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
index 1374fcc..f1d51fc 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
@@ -21,6 +21,7 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
+        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
         .build();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
index 4e1f069..e4eef82 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
@@ -14,7 +14,9 @@
 public final class CharacterBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public CharacterBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
index 25c3721..66af3d0 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
@@ -19,7 +19,9 @@
 public final class CollectionsBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public CollectionsBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
index 2f6da00..16cdeda 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
@@ -14,7 +14,9 @@
 public final class DoubleBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public DoubleBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
index d8c1f8d..92db3a6 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
@@ -14,7 +14,9 @@
 public final class FloatBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public FloatBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
index 706cf36..ed697a7 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
@@ -15,7 +15,9 @@
 public final class IntegerBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public IntegerBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
index b512634..a861d1f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.ToolHelper;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.List;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -22,6 +23,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .withDexRuntimes()
+        .withAllApiLevels()
         .build();
   }
 
@@ -29,7 +31,13 @@
       Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
 
   public ListBackportJava9Test(TestParameters parameters) {
-    super(parameters, Byte.class, TEST_JAR, "backport.ListBackportJava9Main");
-    // TODO Once shipped in an actual API level, migrate to ByteBackportTest
+    super(parameters, List.class, TEST_JAR, "backport.ListBackportJava9Main");
+    // TODO Once shipped in an actual API level, migrate to ListBackportTest
+
+    // Available since API 1 and used to test created lists.
+    ignoreInvokes("add");
+    ignoreInvokes("get");
+    ignoreInvokes("set");
+    ignoreInvokes("size");
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
index 4ddc18c..7344bd8 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
@@ -20,7 +20,9 @@
 
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withDexRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public LongBackportSingleMethodTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
index ba8a638..f8a6ad8 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
@@ -15,7 +15,9 @@
 public final class LongBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public LongBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
new file mode 100644
index 0000000..06e41f8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
@@ -0,0 +1,43 @@
+// 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 java.util.Map;
+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 class MapBackportJava9Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .withDexRuntimes()
+        .withAllApiLevels()
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public MapBackportJava9Test(TestParameters parameters) {
+    super(parameters, Map.class, TEST_JAR, "backport.MapBackportJava9Main");
+    // TODO Once shipped in an actual API level, migrate to MapBackportTest
+
+    // Available since API 1 and used to test created maps.
+    ignoreInvokes("entrySet");
+    ignoreInvokes("get");
+    ignoreInvokes("put");
+    ignoreInvokes("size");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
index d822d5e..439ef38 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
@@ -21,6 +21,7 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
+        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .build();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
index cbde5a7..acae4c9 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
@@ -14,7 +14,9 @@
 public final class MathBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public MathBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
index da50daf..7544e87 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
@@ -21,6 +21,7 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
+        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .build();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
index 74aec20..3120a95 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
@@ -15,9 +15,13 @@
 
 import static java.util.Collections.reverseOrder;
 
-@RunWith(Parameterized.class) public final class ObjectsBackportTest extends AbstractBackportTest {
-  @Parameters(name = "{0}") public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+@RunWith(Parameterized.class)
+public final class ObjectsBackportTest extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public ObjectsBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
index e6105d3..d77052a 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import org.junit.runner.RunWith;
@@ -22,6 +23,7 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .build();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
new file mode 100644
index 0000000..b248e0b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
@@ -0,0 +1,42 @@
+// 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 java.util.Set;
+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 class SetBackportJava9Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .withDexRuntimes()
+        .withAllApiLevels()
+        .build();
+  }
+
+  private static final Path TEST_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+  public SetBackportJava9Test(TestParameters parameters) {
+    super(parameters, Set.class, TEST_JAR, "backport.SetBackportJava9Main");
+    // TODO Once shipped in an actual API level, migrate to SetBackportTest
+
+    // Available since API 1 and used to test created sets.
+    ignoreInvokes("add");
+    ignoreInvokes("contains");
+    ignoreInvokes("size");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
index eb2a078..d56ef5b 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
@@ -22,6 +22,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .withDexRuntimes()
+        .withAllApiLevels()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
index dd6e702..2d1a61a 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
@@ -14,7 +14,9 @@
 public final class ShortBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public ShortBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
index e7486b9..43cbf48 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
@@ -21,6 +21,7 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
+        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .build();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
index 915a6fb..1926975 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
@@ -14,7 +14,9 @@
 public final class StrictMathBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public StrictMathBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
index 52610db..6ac9189 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
@@ -18,7 +18,9 @@
 public final class StringBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .build();
   }
 
   public StringBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java b/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java
index 7543fc5..8741c08 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java
@@ -20,6 +20,8 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.List;
@@ -103,6 +105,13 @@
     }
   }
 
+  protected static Path[] getAllFilesWithSuffixInDirectory(Path directory, String suffix)
+      throws IOException {
+    return Files.walk(directory)
+        .filter(path -> path.toString().endsWith(suffix))
+        .toArray(Path[]::new);
+  }
+
   protected KeepRuleConsumer createKeepRuleConsumer(TestParameters parameters) {
     if (requiresAnyCoreLibDesugaring(parameters)) {
       return new PresentKeepRuleConsumer();
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/InconsistentPrefixTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/InconsistentPrefixTest.java
index 167374a..843551e 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/InconsistentPrefixTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/InconsistentPrefixTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import java.nio.file.Path;
@@ -39,11 +40,13 @@
               options ->
                   options.desugaredLibraryConfiguration =
                       new DesugaredLibraryConfiguration(
+                          AndroidApiLevel.B,
                           false,
                           x,
                           ImmutableMap.of(),
                           ImmutableMap.of(),
                           ImmutableMap.of(),
+                          ImmutableMap.of(),
                           ImmutableList.of()))
           .compile();
       fail("Should have raised the compilation error.");
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/KeepRuleShrinkTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/KeepRuleShrinkTest.java
index 30b62a0..843e154 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/KeepRuleShrinkTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/KeepRuleShrinkTest.java
@@ -57,6 +57,7 @@
 
   static class Executor {
 
+    @SuppressWarnings("unchecked")
     public static void main(String[] args) {
       Map<String, String>[] maps =
           new Map[] {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/LintFilesTest.java
new file mode 100644
index 0000000..87a6140
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/LintFilesTest.java
@@ -0,0 +1,94 @@
+// 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.corelib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.GenerateLintFiles;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class LintFilesTest extends TestBase {
+
+  private void checkFileContent(AndroidApiLevel minApiLevel, Path lintFile) throws Exception {
+    // Just do some light probing in the generated lint files.
+    List<String> methods = FileUtils.readAllLines(lintFile);
+    assertTrue(methods.contains("java/util/List/spliterator()Ljava/util/Spliterator;"));
+    assertTrue(methods.contains("java/util/Optional/empty()Ljava/util/Optional;"));
+    assertTrue(methods.contains("java/util/OptionalInt/empty()Ljava/util/OptionalInt;"));
+    assertEquals(
+        minApiLevel == AndroidApiLevel.L,
+        methods.contains("java/util/Collection/parallelStream()Ljava/util/stream/Stream;"));
+    assertEquals(
+        minApiLevel == AndroidApiLevel.L,
+        methods.contains(
+            "java/util/stream/DoubleStream/parallel()Ljava/util/stream/DoubleStream;"));
+    assertEquals(
+        minApiLevel == AndroidApiLevel.L,
+        methods.contains("java/util/stream/IntStream/parallel()Ljava/util/stream/IntStream;"));
+  }
+
+  @Test
+  public void testFileContent() throws Exception {
+    // Test require r8.jar not r8lib.jar, as the class com.android.tools.r8.GenerateLintFiles in
+    // not kept.
+    Assume.assumeTrue(!ToolHelper.isTestingR8Lib());
+
+    Path directory = temp.newFolder().toPath();
+    GenerateLintFiles.main(
+        new String[] {ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(), directory.toString()});
+    InternalOptions options = new InternalOptions(new DexItemFactory(), new Reporter());
+    DesugaredLibraryConfiguration desugaredLibraryConfiguration =
+        new DesugaredLibraryConfigurationParser(
+                options.itemFactory, options.reporter, false, AndroidApiLevel.B.getLevel())
+            .parse(StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING));
+
+    for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
+      if (apiLevel == AndroidApiLevel.R) {
+        // Skip API level 30 for now.
+        continue;
+      }
+
+      if (apiLevel.getLevel()
+          >= desugaredLibraryConfiguration.getRequiredCompilationApiLevel().getLevel()) {
+        Path compileApiLevelDirectory =
+            directory.resolve("compile_api_level_" + apiLevel.getLevel());
+        assertTrue(Files.exists(compileApiLevelDirectory));
+        for (AndroidApiLevel minApiLevel : AndroidApiLevel.values()) {
+          String desugaredApisBaseName =
+              "desugared_apis_" + apiLevel.getLevel() + "_" + minApiLevel.getLevel();
+          if (minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B) {
+            assertTrue(
+                Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt")));
+            assertTrue(
+                Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".jar")));
+            checkFileContent(
+                minApiLevel, compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt"));
+          } else {
+            assertFalse(
+                Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt")));
+            assertFalse(
+                Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".jar")));
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/APIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTest.java
similarity index 95%
rename from src/test/java/com/android/tools/r8/desugar/corelib/APIConversionTest.java
rename to src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTest.java
index 167ecf7..7a68a55 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/APIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTest.java
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-package com.android.tools.r8.desugar.corelib;
+package com.android.tools.r8.desugar.corelib.conversionTests;
 
 import static org.hamcrest.CoreMatchers.endsWith;
 import static org.hamcrest.core.StringContains.containsString;
@@ -10,6 +10,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.corelib.CoreLibDesugarTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.Arrays;
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTestBase.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTestBase.java
new file mode 100644
index 0000000..eca2a47
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTestBase.java
@@ -0,0 +1,59 @@
+// 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.corelib.conversionTests;
+
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.corelib.CoreLibDesugarTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class APIConversionTestBase extends CoreLibDesugarTestBase {
+
+  private static final Path CONVERSION_FOLDER = Paths.get("src/test/desugaredLibraryConversions");
+
+  public Path[] getTimeConversionClasses() throws IOException {
+    File conversionFolder = temp.newFolder("conversions");
+    File stubsFolder = temp.newFolder("stubs");
+
+    // Compile the stubs to be able to compile the conversions.
+    ToolHelper.runJavac(
+        CfVm.JDK8,
+        null,
+        stubsFolder.toPath(),
+        getAllFilesWithSuffixInDirectory(CONVERSION_FOLDER.resolve("stubs/"), "java"));
+
+    // Compile the conversions using the stubs.
+    ArrayList<Path> classPath = new ArrayList<>();
+    classPath.add(stubsFolder.toPath());
+    ToolHelper.runJavac(
+        CfVm.JDK8,
+        classPath,
+        conversionFolder.toPath(),
+        getAllFilesWithSuffixInDirectory(CONVERSION_FOLDER.resolve("conversions"), "java"));
+
+    Path[] classes = getAllFilesWithSuffixInDirectory(conversionFolder.toPath(), "class");
+    assert classes.length > 0
+        : "Something went wrong during compilation, check the runJavac return value for debugging.";
+    return classes;
+  }
+
+  protected Path buildDesugaredLibraryWithConversionExtension(AndroidApiLevel apiLevel) {
+    Path[] timeConversionClasses = null;
+    try {
+      timeConversionClasses = getTimeConversionClasses();
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+    ArrayList<Path> paths = new ArrayList<>();
+    Collections.addAll(paths, timeConversionClasses);
+    return buildDesugaredLibrary(apiLevel, "", false, paths);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TimeConversionCompilationTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TimeConversionCompilationTest.java
new file mode 100644
index 0000000..2cdf6e7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TimeConversionCompilationTest.java
@@ -0,0 +1,135 @@
+// 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.corelib.conversionTests;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestRuntime.DexRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.time.ZoneId;
+import java.util.TimeZone;
+import org.junit.Test;
+
+public class TimeConversionCompilationTest extends APIConversionTestBase {
+
+  @Test
+  public void testTimeGeneratedDex() throws Exception {
+    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+    Path desugaredLib = temp.newFolder().toPath().resolve("conversion_dex.zip");
+    L8Command.Builder l8Builder =
+        L8Command.builder(diagnosticsHandler)
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+            .addProgramFiles(getTimeConversionClasses())
+            .addDesugaredLibraryConfiguration(
+                StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+            .setMinApiLevel(AndroidApiLevel.B.getLevel())
+            .setOutput(desugaredLib, OutputMode.DexIndexed);
+    ToolHelper.runL8(l8Builder.build(), x -> {});
+    this.checkTimeConversionGeneratedDex(new CodeInspector(desugaredLib));
+  }
+
+  private void checkTimeConversionGeneratedDex(CodeInspector inspector) {
+    ClassSubject clazz = inspector.clazz("j$.time.TimeConversions");
+    assertThat(clazz, isPresent());
+    assertEquals(13, clazz.allMethods().size());
+  }
+
+  @Test
+  public void testRewrittenAPICalls() throws Exception {
+    testForD8()
+        .setMinApi(AndroidApiLevel.B)
+        .addInnerClasses(TimeConversionCompilationTest.class)
+        .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+        .compile()
+        .inspect(this::checkAPIRewritten)
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+        .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class);
+  }
+
+  private void checkAPIRewritten(CodeInspector inspector) {
+    MethodSubject mainMethod = inspector.clazz(Executor.class).uniqueMethodWithName("main");
+    // Check the API calls are not using j$ types.
+    assertTrue(
+        mainMethod
+            .streamInstructions()
+            .anyMatch(
+                instr ->
+                    instr.isInvokeStatic()
+                        && instr.getMethod().name.toString().equals("getTimeZone")
+                        && instr
+                            .getMethod()
+                            .proto
+                            .parameters
+                            .values[0]
+                            .toString()
+                            .equals("java.time.ZoneId")));
+    assertTrue(
+        mainMethod
+            .streamInstructions()
+            .anyMatch(
+                instr ->
+                    instr.isInvokeVirtual()
+                        && instr.getMethod().name.toString().equals("toZoneId")
+                        && instr
+                            .getMethod()
+                            .proto
+                            .returnType
+                            .toString()
+                            .equals("java.time.ZoneId")));
+    // Check the conversion instructions are present.
+    assertTrue(
+        mainMethod
+            .streamInstructions()
+            .anyMatch(
+                instr ->
+                    instr.isInvokeStatic()
+                        && instr.getMethod().name.toString().equals("convert")
+                        && instr
+                            .getMethod()
+                            .proto
+                            .parameters
+                            .values[0]
+                            .toString()
+                            .equals("j$.time.ZoneId")));
+    assertTrue(
+        mainMethod
+            .streamInstructions()
+            .anyMatch(
+                instr ->
+                    instr.isInvokeStatic()
+                        && instr.getMethod().name.toString().equals("convert")
+                        && instr
+                            .getMethod()
+                            .proto
+                            .parameters
+                            .values[0]
+                            .toString()
+                            .equals("java.time.ZoneId")));
+  }
+
+  static class Executor {
+    public static void main(String[] args) {
+      ZoneId zoneId = ZoneId.systemDefault();
+      // Following is a call where java.time.ZoneId is a parameter type (getTimeZone()).
+      TimeZone timeZone = TimeZone.getTimeZone(zoneId);
+      // Following is a call where java.time.ZoneId is a return type (toZoneId()).
+      System.out.println(timeZone.toZoneId().getId());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11CoreLibTestBase.java b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11CoreLibTestBase.java
index c106bec..4be9077 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11CoreLibTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11CoreLibTestBase.java
@@ -53,13 +53,6 @@
         .toArray(Path[]::new);
   }
 
-  protected static Path[] getAllFilesWithSuffixInDirectory(Path directory, String suffix)
-      throws IOException {
-    return Files.walk(directory)
-        .filter(path -> path.toString().endsWith(suffix))
-        .toArray(Path[]::new);
-  }
-
   protected static Path getSafeVarArgsFile() {
     return ANDROID_SAFE_VAR_ARGS_LOCATION;
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
index 6b8dc10..baa351d 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
@@ -1,11 +1,12 @@
 {
   "configuration_format_version": 1,
-  "version": "0.2.0",
+  "version": "0.3.0",
   "required_compilation_api_level": 26,
   "library_flags": [
     {
       "api_level_below_or_equal": 25,
       "rewrite_prefix": {
+        "j$.time.": "java.time.",
         "java.time.": "j$.time.",
         "java.util.Desugar": "j$.util.Desugar"
       },
@@ -71,6 +72,14 @@
         "java.util.Date#toInstant": "java.util.DesugarDate",
         "java.util.GregorianCalendar#from": "java.util.DesugarGregorianCalendar",
         "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar"
+      },
+      "custom_conversion": {
+        "java.time.ZonedDateTime": "j$.time.TimeConversions",
+        "java.time.LocalDate": "j$.time.TimeConversions",
+        "java.time.Duration": "j$.time.TimeConversions",
+        "java.time.ZoneId": "j$.time.TimeConversions",
+        "java.time.MonthDay": "j$.time.TimeConversions",
+        "java.time.Instant": "j$.time.TimeConversions"
       }
     },
     {
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index 9ea52d0..25cbe9a 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -4,9 +4,13 @@
 
 package com.android.tools.r8.dexsplitter;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.FeatureSplit;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NeverMerge;
 import com.android.tools.r8.R8FullTestBuilder;
@@ -77,6 +81,26 @@
     assertTrue(processResult.stderr.contains("NoClassDefFoundError"));
   }
 
+  @Test
+  public void testOnR8Splitter() throws IOException, CompilationFailedException,
+      ExecutionException {
+    assumeTrue(parameters.isDexRuntime());
+    Consumer<R8FullTestBuilder> configurator =
+        r8FullTestBuilder -> r8FullTestBuilder.enableMergeAnnotations().noMinification();
+    ProcessResult processResult =
+        testR8Splitter(
+            parameters,
+            ImmutableSet.of(BaseClass.class, BaseWithStatic.class),
+            ImmutableSet.of(FeatureClass.class, AFeatureWithStatic.class),
+            FeatureClass.class,
+            EXPECTED,
+            a -> true,
+            configurator);
+
+    assertEquals(processResult.exitCode, 0);
+    assertTrue(processResult.stdout.equals(StringUtils.lines("42", "foobar")));
+  }
+
   @NeverMerge
   public static class BaseClass implements RunInterface {
 
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java b/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
index 04d3b14..5622177 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
@@ -8,15 +8,19 @@
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ExtractMarker;
 import com.android.tools.r8.FeatureSplit;
+import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
+import java.util.Collection;
 import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -60,21 +64,10 @@
 
   @Test
   public void testTwoFeatures() throws CompilationFailedException, IOException, ExecutionException {
-    Path basePath = temp.newFile("base.zip").toPath();
-    Path feature1Path = temp.newFile("feature1.zip").toPath();
-    Path feature2Path = temp.newFile("feature2.zip").toPath();
-
-    testForR8(parameters.getBackend())
-        .addProgramClasses(BaseClass.class, RunInterface.class, SplitRunner.class)
-        .setMinApi(parameters.getRuntime())
-        .addFeatureSplit(
-            builder -> simpleSplitProvider(builder, feature1Path, temp, FeatureClass.class))
-        .addFeatureSplit(
-            builder -> simpleSplitProvider(builder, feature2Path, temp, FeatureClass2.class))
-        .addKeepAllClassesRule()
-        .compile()
-        .writeToZip(basePath);
-
+    CompiledWithFeature compiledWithFeature = new CompiledWithFeature().invoke();
+    Path basePath = compiledWithFeature.getBasePath();
+    Path feature1Path = compiledWithFeature.getFeature1Path();
+    Path feature2Path = compiledWithFeature.getFeature2Path();
     CodeInspector baseInspector = new CodeInspector(basePath);
     assertTrue(baseInspector.clazz(BaseClass.class).isPresent());
 
@@ -100,6 +93,24 @@
     assertEquals(result.stdout, StringUtils.lines("Testing second"));
   }
 
+  @Test
+  public void testMarkerInFeatures()
+      throws IOException, CompilationFailedException, ExecutionException, ResourceException {
+    CompiledWithFeature compiledWithFeature = new CompiledWithFeature().invoke();
+    Path basePath = compiledWithFeature.getBasePath();
+    Path feature1Path = compiledWithFeature.getFeature1Path();
+    Path feature2Path = compiledWithFeature.getFeature2Path();
+    Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(basePath);
+    Collection<Marker> feature1Markers = ExtractMarker.extractMarkerFromDexFile(feature1Path);
+    Collection<Marker> feature2Markers = ExtractMarker.extractMarkerFromDexFile(feature2Path);
+
+    assertEquals(markers.size(), 1);
+    assertEquals(feature1Markers.size(), 1);
+    assertEquals(feature2Markers.size(), 1);
+    assertEquals(markers.iterator().next(), feature1Markers.iterator().next());
+    assertEquals(markers.iterator().next(), feature2Markers.iterator().next());
+  }
+
   public static class HelloWorld {
     public static void main(String[] args) {
       System.out.println("Hello world");
@@ -139,4 +150,41 @@
       test();
     }
   }
+
+  private class CompiledWithFeature {
+
+    private Path basePath;
+    private Path feature1Path;
+    private Path feature2Path;
+
+    public Path getBasePath() {
+      return basePath;
+    }
+
+    public Path getFeature1Path() {
+      return feature1Path;
+    }
+
+    public Path getFeature2Path() {
+      return feature2Path;
+    }
+
+    public CompiledWithFeature invoke() throws IOException, CompilationFailedException {
+      basePath = temp.newFile("base.zip").toPath();
+      feature1Path = temp.newFile("feature1.zip").toPath();
+      feature2Path = temp.newFile("feature2.zip").toPath();
+
+      testForR8(parameters.getBackend())
+          .addProgramClasses(BaseClass.class, RunInterface.class, SplitRunner.class)
+          .setMinApi(parameters.getRuntime())
+          .addFeatureSplit(
+              builder -> simpleSplitProvider(builder, feature1Path, temp, FeatureClass.class))
+          .addFeatureSplit(
+              builder -> simpleSplitProvider(builder, feature2Path, temp, FeatureClass2.class))
+          .addKeepAllClassesRule()
+          .compile()
+          .writeToZip(basePath);
+      return this;
+    }
+  }
 }
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 9d8c12b..b8154cc 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -1,6 +1,5 @@
 package com.android.tools.r8.dexsplitter;
 
-import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
@@ -66,7 +65,6 @@
                   ByteDataView data,
                   Set<String> descriptors,
                   DiagnosticsHandler handler) {
-                assertEquals(classNames.size(), descriptors.size());
                 for (String descriptor : descriptors) {
                   assertTrue(classNames.contains(DescriptorUtils.descriptorToJavaType(descriptor)));
                 }
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionMethods.java
new file mode 100644
index 0000000..0dd1a53
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionMethods.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+public class CollectionMethods {
+
+  public static <E> List<E> listOfArray(E[] elements) {
+    ArrayList<E> list = new ArrayList<>(elements.length);
+    for (E element : elements) {
+      list.add(Objects.requireNonNull(element));
+    }
+    return Collections.unmodifiableList(list);
+  }
+
+  public static <E> Set<E> setOfArray(E[] elements) {
+    HashSet<E> set = new HashSet<>(elements.length);
+    for (E element : elements) {
+      if (!set.add(Objects.requireNonNull(element))) {
+        throw new IllegalArgumentException("duplicate element: " + element);
+      }
+    }
+    return Collections.unmodifiableSet(set);
+  }
+
+  public static <K, V> Map<K, V> mapOfEntries(Map.Entry<K, V>[] elements) {
+    HashMap<K, V> map = new HashMap<>(elements.length);
+    for (Map.Entry<K, V> element : elements) {
+      K key = Objects.requireNonNull(element.getKey());
+      V value = Objects.requireNonNull(element.getValue());
+      if (map.put(key, value) != null) {
+        throw new IllegalArgumentException("duplicate key: " + key);
+      }
+    }
+    return Collections.unmodifiableMap(map);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index 2bb7dc3..e210196 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -29,6 +29,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -73,11 +74,11 @@
           ByteMethods.class,
           CharacterMethods.class,
           CloseResourceMethod.class,
+          CollectionMethods.class,
           CollectionsMethods.class,
           DoubleMethods.class,
           FloatMethods.class,
           IntegerMethods.class,
-          ListMethods.class,
           LongMethods.class,
           MathMethods.class,
           ObjectsMethods.class,
@@ -99,7 +100,7 @@
   @Test
   public void test() throws Exception {
     ArrayList<Class<?>> sorted = new ArrayList<>(methodTemplateClasses);
-    sorted.sort((a, b) -> a.getTypeName().compareTo(b.getTypeName()));
+    sorted.sort(Comparator.comparing(Class::getTypeName));
     assertEquals("Classes should be listed in sorted order", sorted, methodTemplateClasses);
     assertEquals(
         FileUtils.readTextFile(backportMethodsFile, StandardCharsets.UTF_8),
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/ListMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/ListMethods.java
deleted file mode 100644
index 7d74919..0000000
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/ListMethods.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar.backports;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-public class ListMethods {
-
-  public static <E> List<E> ofArray(E[] elements) {
-    // TODO(140709356): The other overloads should call into this method to ensure consistent
-    //  behavior, but we cannot link against List.of(E[]) because it's only available in Java 9.
-    ArrayList<E> list = new ArrayList<>(elements.length);
-    for (E element : elements) {
-      list.add(Objects.requireNonNull(element));
-    }
-    return Collections.unmodifiableList(list);
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
new file mode 100644
index 0000000..eabd3cc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.outliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class OutlinesWithNonNullTest extends TestBase {
+  private static final String JVM_OUTPUT = StringUtils.lines(
+      "42",
+      "arg",
+      "42",
+      "arg"
+  );
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().build();
+  }
+
+  public OutlinesWithNonNullTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testNonNullOnOneSide() throws Exception {
+    testForR8(parameters.getBackend())
+        .enableClassInliningAnnotations()
+        .enableInliningAnnotations()
+        .addProgramClasses(TestArg.class, TestClassWithNonNullOnOneSide.class)
+        .addKeepMainRule(TestClassWithNonNullOnOneSide.class)
+        .setMinApi(parameters.getRuntime())
+        .allowAccessModification()
+        .noMinification()
+        .addOptionsModification(
+            options -> {
+              options.outline.threshold = 2;
+              options.outline.minSize = 2;
+            })
+        .compile()
+        .inspect(inspector -> validateOutlining(inspector, TestClassWithNonNullOnOneSide.class))
+        .run(parameters.getRuntime(), TestClassWithNonNullOnOneSide.class)
+        .assertSuccessWithOutput(JVM_OUTPUT);
+  }
+
+  @Test
+  public void testNonNullOnBothSides() throws Exception {
+    testForR8(parameters.getBackend())
+        .enableClassInliningAnnotations()
+        .enableInliningAnnotations()
+        .addProgramClasses(TestArg.class, TestClassWithNonNullOnBothSides.class)
+        .addKeepMainRule(TestClassWithNonNullOnBothSides.class)
+        .setMinApi(parameters.getRuntime())
+        .allowAccessModification()
+        .noMinification()
+        .addOptionsModification(
+            options -> {
+              options.outline.threshold = 2;
+              options.outline.minSize = 2;
+            })
+        .compile()
+        .inspect(inspector -> validateOutlining(inspector, TestClassWithNonNullOnBothSides.class))
+        .run(parameters.getRuntime(), TestClassWithNonNullOnBothSides.class)
+        .assertSuccessWithOutput(JVM_OUTPUT);
+  }
+
+  private void validateOutlining(CodeInspector inspector, Class<?> main) {
+    ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+    assertThat(outlineClass, isPresent());
+    MethodSubject outlineMethod = outlineClass.uniqueMethodWithName("outline0");
+    assertThat(outlineMethod, isPresent());
+
+    ClassSubject argClass = inspector.clazz(TestArg.class);
+    assertThat(argClass, isPresent());
+    MethodSubject printHash = argClass.uniqueMethodWithName("printHash");
+    assertThat(printHash, isPresent());
+    MethodSubject printArg= argClass.uniqueMethodWithName("printArg");
+    assertThat(printArg, isPresent());
+
+    ClassSubject classSubject = inspector.clazz(main);
+    assertThat(classSubject, isPresent());
+    MethodSubject method1 = classSubject.uniqueMethodWithName("method1");
+    assertThat(method1, isPresent());
+    assertThat(method1, CodeMatchers.invokesMethod(outlineMethod));
+    assertThat(method1, not(CodeMatchers.invokesMethod(printHash)));
+    assertThat(method1, not(CodeMatchers.invokesMethod(printArg)));
+    MethodSubject method2 = classSubject.uniqueMethodWithName("method2");
+    assertThat(method2, isPresent());
+    assertThat(method2, CodeMatchers.invokesMethod(outlineMethod));
+    assertThat(method2, not(CodeMatchers.invokesMethod(printHash)));
+    assertThat(method2, not(CodeMatchers.invokesMethod(printArg)));
+  }
+
+  @NeverClassInline
+  public static class TestArg {
+    @Override
+    public int hashCode() {
+      return 42;
+    }
+
+    @Override
+    public String toString() {
+      return "arg";
+    }
+
+    @NeverInline
+    static void printHash(Object arg) {
+      if (arg == null) {
+        throw new NullPointerException();
+      }
+      System.out.println(arg.hashCode());
+      // This method guarantees that, at the normal exit, argument is not null.
+    }
+
+    @NeverInline
+    static void printArg(Object arg) {
+      System.out.println(arg);
+    }
+  }
+
+  static class TestClassWithNonNullOnOneSide {
+    @NeverInline
+    static void method1(Object arg) {
+      TestArg.printHash(arg);
+      // We will have non-null aliasing here.
+      TestArg.printArg(arg);
+    }
+
+    @NeverInline
+    static void method2(Object arg) {
+      if (arg != null) {
+        // We will have non-null aliasing here.
+        TestArg.printHash(arg);
+        TestArg.printArg(arg);
+      }
+    }
+
+    public static void main(String... args) {
+      TestArg arg = new TestArg();
+      method1(arg);
+      method2(arg);
+    }
+  }
+
+  static class TestClassWithNonNullOnBothSides {
+    @NeverInline
+    static void method1(Object arg) {
+      TestArg.printHash(arg);
+      // We will have non-null aliasing here.
+      TestArg.printArg(arg);
+    }
+
+    @NeverInline
+    static void method2(Object arg) {
+      TestArg.printHash(arg);
+      // We will have non-null aliasing here.
+      TestArg.printArg(arg);
+    }
+
+    public static void main(String... args) {
+      TestArg arg = new TestArg();
+      method1(arg);
+      method2(arg);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
index 6c87416..3353fd2 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.memberrebinding.b135627418.library.Drawable;
 import com.android.tools.r8.memberrebinding.b135627418.library.DrawableWrapper;
 import com.android.tools.r8.memberrebinding.b135627418.library.InsetDrawable;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -79,11 +80,13 @@
                 options ->
                     options.desugaredLibraryConfiguration =
                         new DesugaredLibraryConfiguration(
+                            AndroidApiLevel.B,
                             false,
                             ImmutableMap.of(packageName + ".runtime", packageName + ".library"),
                             ImmutableMap.of(),
                             ImmutableMap.of(),
                             ImmutableMap.of(),
+                            ImmutableMap.of(),
                             ImmutableList.of()))
             .compile();
 
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
new file mode 100644
index 0000000..8a67d39
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
@@ -0,0 +1,95 @@
+// 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.shaking.keptgraph;
+
+import static com.android.tools.r8.references.Reference.methodFromMethod;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.graphinspector.GraphInspector;
+import com.android.tools.r8.utils.graphinspector.GraphInspector.QueryNode;
+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 KeptByTwoMethods extends TestBase {
+
+  @NeverMerge
+  public static class A {
+
+    @NeverInline
+    void foo() {
+      System.out.println("A.foo!");
+    }
+  }
+
+  @NeverMerge
+  public static class B extends A {
+    // Intermediate to A.
+  }
+
+  public static class TestClass {
+
+    @NeverInline
+    static void bar(B b) {
+      b.foo();
+    }
+
+    @NeverInline
+    static void baz(B b) {
+      b.foo();
+    }
+
+    public static void main(String[] args) {
+      bar(new B());
+      baz(new B());
+    }
+  }
+
+  private static final String EXPECTED = StringUtils.lines("A.foo!", "A.foo!");
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withCfRuntimes().build();
+  }
+
+  private final TestParameters parameters;
+
+  public KeptByTwoMethods(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    GraphInspector inspector =
+        testForR8(parameters.getBackend())
+            .enableGraphInspector()
+            .enableMergeAnnotations()
+            .enableInliningAnnotations()
+            .addKeepMainRule(TestClass.class)
+            .addProgramClasses(TestClass.class, A.class, B.class)
+            .run(parameters.getRuntime(), TestClass.class)
+            .assertSuccessWithOutput(EXPECTED)
+            .graphInspector();
+
+    MethodReference barRef = methodFromMethod(TestClass.class.getDeclaredMethod("bar", B.class));
+    inspector.method(barRef).assertPresent();
+
+    MethodReference bazRef = methodFromMethod(TestClass.class.getDeclaredMethod("baz", B.class));
+    inspector.method(bazRef).assertPresent();
+
+    QueryNode foo =
+        inspector.method(methodFromMethod(A.class.getDeclaredMethod("foo"))).assertPresent();
+
+    foo.assertInvokedFrom(barRef);
+    foo.assertInvokedFrom(bazRef);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java b/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java
new file mode 100644
index 0000000..3a63555
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java
@@ -0,0 +1,50 @@
+// 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 java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+import org.junit.Test;
+
+public class ListUtilsTest {
+
+  private List<Integer> createInputData(int size) {
+    List<Integer> input = new ArrayList<>(size);
+    for (int i = 0; i < size; i++) {
+      input.add(i);
+    }
+    return input;
+  }
+
+  @Test
+  public void lastIndexOf_outOfRange() {
+    List<Integer> input = createInputData(3);
+    Predicate<Integer> tester = x -> x * x == -1;
+    assertEquals(-1, ListUtils.lastIndexMatching(input, tester));
+  }
+
+  @Test
+  public void lastIndexOf_first() {
+    List<Integer> input = createInputData(3);
+    Predicate<Integer> tester = x -> x * x == 0;
+    assertEquals(0, ListUtils.lastIndexMatching(input, tester));
+  }
+
+  @Test
+  public void lastIndexOf_middle() {
+    List<Integer> input = createInputData(4);
+    Predicate<Integer> tester = x -> x * x == 4;
+    assertEquals(2, ListUtils.lastIndexMatching(input, tester));
+  }
+
+  @Test
+  public void lastIndexOf_last() {
+    List<Integer> input = createInputData(2);
+    Predicate<Integer> tester = x -> x * x == 1;
+    assertEquals(1, ListUtils.lastIndexMatching(input, tester));
+  }
+}
diff --git a/tools/archive.py b/tools/archive.py
index 1041b40..62e0e27 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -126,9 +126,6 @@
   create_maven_release.generate_r8_maven_zip(utils.MAVEN_ZIP)
   create_maven_release.generate_r8_maven_zip(
       utils.MAVEN_ZIP_LIB, is_r8lib=True)
-  # Create maven release of the desuage_jdk_libs configuration.
-  create_maven_release.generate_desugar_configuration_maven_zip(
-      utils.DESUGAR_CONFIGURATION_MAVEN_ZIP)
 
   # Generate and copy a full build without dependencies.
   gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
@@ -149,6 +146,12 @@
     utils.COMPATPROGUARDLIB,
     '-Pno_internal'
   ])
+
+  # Create maven release of the desuage_jdk_libs configuration. This require
+  # an r8.jar with dependencies to have been built.
+  create_maven_release.generate_desugar_configuration_maven_zip(
+      utils.DESUGAR_CONFIGURATION_MAVEN_ZIP)
+
   version = GetVersion()
   is_master = IsMaster(version)
   if is_master:
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index b4343f3..30c14c8 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -6,6 +6,7 @@
 import argparse
 import gradle
 import hashlib
+import jdk
 import json
 from os import makedirs
 from os.path import join
@@ -338,6 +339,19 @@
     configuration_dir = join(tmp_dir, 'META-INF', 'desugar', 'd8')
     makedirs(configuration_dir)
     copyfile(configuration, join(configuration_dir, 'desugar.json'))
+
+    lint_dir = join(configuration_dir, 'lint')
+    makedirs(lint_dir)
+    cmd = [
+        jdk.GetJavaExecutable(),
+        '-cp',
+        utils.R8_JAR,
+        'com.android.tools.r8.GenerateLintFiles',
+        configuration,
+        lint_dir]
+    utils.PrintCmd(cmd)
+    subprocess.check_call(cmd)
+
     make_archive(destination, 'zip', tmp_dir)
     move(destination + '.zip', destination)
 
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index f647f4b..89d0da2 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -64,6 +64,8 @@
 # For running on Golem all third-party repositories are bundled as an x20-
 # dependency and then copied to WORKING_DIR. To update the app-bundle use
 # 'run_on_as_app_x20_packager.py'.
+# For showing benchmark data, also include the app in appSegmentBenchmarks in
+# the file <golem_repo>/config/r8/benchmarks.dart.
 APP_REPOSITORIES = [
   # ...
   # Repo({
@@ -314,7 +316,8 @@
               'module': 'sample/android',
               'archives_base_name': 'android',
               'min_sdk': 14,
-              'compile_sdk': 28
+              'compile_sdk': 28,
+              'skip': True
           })
       ]
   }),