Merge "Fix inlining constraints in presence of non-rebound field access"
diff --git a/build.gradle b/build.gradle
index f492319..1a7de42 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1475,14 +1475,6 @@
     setConfiguration(configurations.supportLibs)
 }
 
-task AospJarTest(type: Exec) {
-    dependsOn CompatDx, downloadDeps
-    def script = "tools/test_aosp_jar.py"
-    inputs.files files(script)
-    commandLine "python", script, "--no-build"
-    workingDir = projectDir
-}
-
 def getR8LibTask() {
     if (project.hasProperty('r8lib')) {
         return R8Lib
@@ -1555,16 +1547,14 @@
         testLogging.showStandardStreams = true
     }
     if (project.hasProperty('dex_vm') && project.property('dex_vm') != 'default') {
-        println "Running with non default vm: " + project.property('dex_vm')
+        println "NOTE: Running with non default vm: " + project.property('dex_vm')
         systemProperty 'dex_vm', project.property('dex_vm')
     }
 
-    // Forward vm configurations for test parameters.
-    if (project.hasProperty('dex_vms')) {
-        systemProperty 'dex_vms', project.property('dex_vms')
-    }
-    if (project.hasProperty('cf_vms')) {
-        systemProperty 'cf_vms', project.property('cf_vms')
+    // Forward runtime configurations for test parameters.
+    if (project.hasProperty('runtimes')) {
+        println "NOTE: Running with runtimes: " + project.property('runtimes')
+        systemProperty 'runtimes', project.property('runtimes')
     }
 
     if (project.hasProperty('one_line_per_test')) {
@@ -1596,13 +1586,14 @@
 
     if (project.hasProperty('tool')) {
         if (project.property('tool') == 'r8') {
-            exclude "com/android/tools/r8/art/*/d8/**"
             exclude "com/android/tools/r8/jctf/**"
         } else if (project.property('tool') == 'd8') {
             if (project.hasProperty('only_jctf')) {
                 include "com/android/tools/r8/jctf/d8/**"
             } else {
-                include "com/android/tools/r8/art/*/d8/**"
+                // Don't run anything, deprecated
+                println "Running with deprecated tool d8, not running any tests"
+                include ""
             }
         } else {
             assert(project.property('tool') == 'r8cf')
@@ -1640,9 +1631,6 @@
     if (project.hasProperty('test_dir')) {
         systemProperty 'test_dir', project.property('test_dir')
     }
-    if (project.hasProperty('aosp_jar')) {
-        dependsOn AospJarTest
-    }
     if (project.hasProperty('r8lib') || project.hasProperty('r8lib_no_deps')) {
         dependsOn configureTestForR8Lib
         // We remove build/classes/test from classpath and rely on configureTestForR8Lib to provide
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index a38e3bf..20047c1 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -124,26 +124,6 @@
 
     }
     builders {
-      name: "d8-linux"
-      mixins: "linux"
-      mixins: "normal"
-      mixins: "fast_bot"
-      recipe {
-        properties: "tool:d8"
-        properties: "aosp_jar:True"
-      }
-    }
-    builders {
-      name: "d8-linux-android-4.0.4"
-      mixins: "linux"
-      mixins: "normal"
-      mixins: "fast_bot"
-      recipe {
-        properties: "tool:d8"
-        properties: "dex_vm:4.0.4"
-      }
-    }
-    builders {
       name: "d8-linux-android-4.0.4_release"
       mixins: "linux"
       mixins: "normal"
@@ -153,16 +133,6 @@
       }
     }
     builders {
-      name: "d8-linux-android-4.4.4"
-      mixins: "linux"
-      mixins: "normal"
-      mixins: "fast_bot"
-      recipe {
-        properties: "tool:d8"
-        properties: "dex_vm:4.4.4"
-      }
-    }
-    builders {
       name: "d8-linux-android-4.4.4_release"
       mixins: "linux"
       mixins: "normal"
@@ -172,16 +142,6 @@
       }
     }
     builders {
-      name: "d8-linux-android-5.1.1"
-      mixins: "linux"
-      mixins: "normal"
-      mixins: "fast_bot"
-      recipe {
-        properties: "tool:d8"
-        properties: "dex_vm:5.1.1"
-      }
-    }
-    builders {
       name: "d8-linux-android-5.1.1_release"
       mixins: "linux"
       mixins: "normal"
@@ -191,16 +151,6 @@
       }
     }
     builders {
-      name: "d8-linux-android-6.0.1"
-      mixins: "linux"
-      mixins: "normal"
-      mixins: "fast_bot"
-      recipe {
-        properties: "tool:d8"
-        properties: "dex_vm:6.0.1"
-      }
-    }
-    builders {
       name: "d8-linux-android-6.0.1_release"
       mixins: "linux"
       mixins: "normal"
@@ -210,16 +160,6 @@
       }
     }
     builders {
-      name: "d8-linux-android-7.0.0"
-      mixins: "linux"
-      mixins: "normal"
-      mixins: "fast_bot"
-      recipe {
-        properties: "tool:d8"
-        properties: "dex_vm:7.0.0"
-      }
-    }
-    builders {
       name: "d8-linux-android-7.0.0_release"
       mixins: "linux"
       mixins: "normal"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 9f1897a..a830e6f 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -57,36 +57,6 @@
     short_name: "cf-jctf"
   }
   builders {
-    name: "buildbucket/luci.r8.ci/d8-linux"
-    category: "D8"
-    short_name: "linux"
-  }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-android-4.0.4"
-    category: "D8"
-    short_name: "4.0.4"
-  }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-android-4.4.4"
-    category: "D8"
-    short_name: "4.4.4"
-  }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-android-5.1.1"
-    category: "D8"
-    short_name: "5.1.1"
-  }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-android-6.0.1"
-    category: "D8"
-    short_name: "6.0.1"
-  }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-android-7.0.0"
-    category: "D8"
-    short_name: "7.0.0"
-  }
-  builders {
     name: "buildbucket/luci.r8.ci/windows"
     category: "win"
     short_name: "win"
@@ -242,36 +212,6 @@
     short_name: "cf-jctf"
   }
   builders {
-    name: "buildbucket/luci.r8.ci/d8-linux"
-    category: "D8"
-    short_name: "linux"
-  }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-android-4.0.4"
-    category: "D8"
-    short_name: "4.0.4"
-  }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-android-4.4.4"
-    category: "D8"
-    short_name: "4.4.4"
-  }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-android-5.1.1"
-    category: "D8"
-    short_name: "5.1.1"
-  }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-android-6.0.1"
-    category: "D8"
-    short_name: "6.0.1"
-  }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-android-7.0.0"
-    category: "D8"
-    short_name: "7.0.0"
-  }
-  builders {
     name: "buildbucket/luci.r8.ci/windows"
     category: "win"
     short_name: "win"
diff --git a/infra/config/global/luci-notify.cfg b/infra/config/global/luci-notify.cfg
index bb27c81..58bad4c 100644
--- a/infra/config/global/luci-notify.cfg
+++ b/infra/config/global/luci-notify.cfg
@@ -28,56 +28,26 @@
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
-    name: "d8-linux"
-    bucket: "ci"
-    repository: "https://r8.googlesource.com/r8"
-  }
-  builders {
-    name: "d8-linux-android-4.0.4"
-    bucket: "ci"
-    repository: "https://r8.googlesource.com/r8"
-  }
-  builders {
     name: "d8-linux-android-4.0.4_release"
     bucket: "ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
-    name: "d8-linux-android-4.4.4"
-    bucket: "ci"
-    repository: "https://r8.googlesource.com/r8"
-  }
-  builders {
     name: "d8-linux-android-4.4.4_release"
     bucket: "ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
-    name: "d8-linux-android-5.1.1"
-    bucket: "ci"
-    repository: "https://r8.googlesource.com/r8"
-  }
-  builders {
     name: "d8-linux-android-5.1.1_release"
     bucket: "ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
-    name: "d8-linux-android-6.0.1"
-    bucket: "ci"
-    repository: "https://r8.googlesource.com/r8"
-  }
-  builders {
     name: "d8-linux-android-6.0.1_release"
     bucket: "ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
-    name: "d8-linux-android-7.0.0"
-    bucket: "ci"
-    repository: "https://r8.googlesource.com/r8"
-  }
-  builders {
     name: "d8-linux-android-7.0.0_release"
     bucket: "ci"
     repository: "https://r8.googlesource.com/r8"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index dca0298..80de746 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -24,14 +24,6 @@
     refs: "refs/heads/master"
   }
   triggers: "archive"
-  triggers: "d8-linux"
-  triggers: "d8-linux"
-  triggers: "d8-linux-android-4.0.4"
-  triggers: "d8-linux-android-4.4.4"
-  triggers: "d8-linux-android-5.1.1"
-  triggers: "d8-linux-android-6.0.1"
-  triggers: "d8-linux-android-7.0.0"
-  triggers: "linux"
   triggers: "linux"
   triggers: "linux-android-4.0.4"
   triggers: "linux-android-4.4.4"
@@ -97,29 +89,6 @@
 }
 
 job {
-  id: "d8-linux"
-  acl_sets: "default"
-  triggering_policy: {
-    max_concurrent_invocations: 2
-  }
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
-    builder: "d8-linux"
-  }
-}
-
-job {
-  id: "d8-linux-android-4.0.4"
-  acl_sets: "default"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
-    builder: "d8-linux-android-4.0.4"
-  }
-}
-
-job {
   id: "d8-linux-android-4.0.4_release"
   acl_sets: "default"
   buildbucket {
@@ -130,16 +99,6 @@
 }
 
 job {
-  id: "d8-linux-android-4.4.4"
-  acl_sets: "default"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
-    builder: "d8-linux-android-4.4.4"
-  }
-}
-
-job {
   id: "d8-linux-android-4.4.4_release"
   acl_sets: "default"
   buildbucket {
@@ -150,19 +109,6 @@
 }
 
 job {
-  id: "d8-linux-android-5.1.1"
-  acl_sets: "default"
-  triggering_policy: {
-    max_concurrent_invocations: 2
-  }
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
-    builder: "d8-linux-android-5.1.1"
-  }
-}
-
-job {
   id: "d8-linux-android-5.1.1_release"
   acl_sets: "default"
   buildbucket {
@@ -173,16 +119,6 @@
 }
 
 job {
-  id: "d8-linux-android-6.0.1"
-  acl_sets: "default"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
-    builder: "d8-linux-android-6.0.1"
-  }
-}
-
-job {
   id: "d8-linux-android-6.0.1_release"
   acl_sets: "default"
   buildbucket {
@@ -193,16 +129,6 @@
 }
 
 job {
-  id: "d8-linux-android-7.0.0"
-  acl_sets: "default"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
-    builder: "d8-linux-android-7.0.0"
-  }
-}
-
-job {
   id: "d8-linux-android-7.0.0_release"
   acl_sets: "default"
   buildbucket {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index ad6c7d2..20eafd2 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -8,8 +8,10 @@
 import static com.android.tools.r8.graph.ClassKind.PROGRAM;
 
 import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.ProgramResource;
 import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.errors.CompilationError;
@@ -115,7 +117,12 @@
       classReader.readSources();
       ThreadUtils.awaitFutures(futures);
       classReader.initializeLazyClassCollection(builder);
-      builder.addProgramResourceProviders(inputApp.getProgramResourceProviders());
+      for (ProgramResourceProvider provider : inputApp.getProgramResourceProviders()) {
+        DataResourceProvider dataResourceProvider = provider.getDataResourceProvider();
+        if (dataResourceProvider != null) {
+          builder.addDataResourceProvider(dataResourceProvider);
+        }
+      }
     } catch (ResourceException e) {
       throw options.reporter.fatalError(new StringDiagnostic(e.getMessage(), e.getOrigin()));
     } finally {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index ab06751..d4fb8e3 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.DexFilePerClassFileConsumer;
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.ProgramConsumer;
-import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.dex.FileWriter.ByteBufferResult;
 import com.android.tools.r8.errors.CompilationError;
@@ -53,12 +52,10 @@
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
-import java.util.stream.Collectors;
 
 public class ApplicationWriter {
 
@@ -333,17 +330,11 @@
     }
     DataResourceConsumer dataResourceConsumer = options.dataResourceConsumer;
     if (dataResourceConsumer != null) {
-      List<DataResourceProvider> dataResourceProviders = application.programResourceProviders
-          .stream()
-          .map(ProgramResourceProvider::getDataResourceProvider)
-          .filter(Objects::nonNull)
-          .collect(Collectors.toList());
-
       ResourceAdapter resourceAdapter =
           new ResourceAdapter(appView, application.dexItemFactory, graphLense, namingLens, options);
       Set<String> generatedResourceNames = new HashSet<>();
 
-      for (DataResourceProvider dataResourceProvider : dataResourceProviders) {
+      for (DataResourceProvider dataResourceProvider : application.dataResourceProviders) {
         try {
           dataResourceProvider.accept(
               new Visitor() {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 293f6f1..3c8fd46 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.origin.Origin;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
 import java.util.Collections;
@@ -106,7 +105,7 @@
     dexItemFactory.clearSubtypeInformation();
     dexItemFactory.objectType.tagAsSubtypeRoot();
     Map<DexType, Set<DexType>> map = new IdentityHashMap<>();
-    for (DexClass clazz : Iterables.<DexClass>concat(app.classes(), app.libraryClasses())) {
+    for (DexClass clazz : app.allClasses()) {
       populateAllSuperTypes(map, clazz.type, clazz, app::definitionFor);
     }
     for (Map.Entry<DexType, Set<DexType>> entry : map.entrySet()) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
index 474704a..bb5919e 100644
--- a/src/main/java/com/android/tools/r8/graph/AppServices.java
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.DataEntryResource;
 import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.DataResourceProvider.Visitor;
-import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.origin.Origin;
@@ -97,14 +96,8 @@
     }
 
     public AppServices build() {
-      Iterable<ProgramResourceProvider> programResourceProviders =
-          appView.appInfo().app().programResourceProviders;
-      for (ProgramResourceProvider programResourceProvider : programResourceProviders) {
-        DataResourceProvider dataResourceProvider =
-            programResourceProvider.getDataResourceProvider();
-        if (dataResourceProvider != null) {
-          readServices(dataResourceProvider);
-        }
+      for (DataResourceProvider provider : appView.appInfo().app().dataResourceProviders) {
+        readServices(provider);
       }
       return new AppServices(appView, services);
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index ead547f..8602d85 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -6,7 +6,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.naming.ClassNameMapper;
@@ -24,10 +24,7 @@
 
 public abstract class DexApplication {
 
-  // Maps type into class, may be used concurrently.
-  final ProgramClassCollection programClasses;
-
-  public final ImmutableList<ProgramResourceProvider> programResourceProviders;
+  public final ImmutableList<DataResourceProvider> dataResourceProviders;
 
   public final ImmutableSet<DexType> mainDexList;
   public final String deadCode;
@@ -41,22 +38,17 @@
   // Information on the lexicographically largest string referenced from code.
   public final DexString highestSortingString;
 
-  /**
-   * Constructor should only be invoked by the DexApplication.Builder.
-   */
+  /** Constructor should only be invoked by the DexApplication.Builder. */
   DexApplication(
       ClassNameMapper proguardMap,
-      ProgramClassCollection programClasses,
-      ImmutableList<ProgramResourceProvider> programResourceProviders,
+      ImmutableList<DataResourceProvider> dataResourceProviders,
       ImmutableSet<DexType> mainDexList,
       String deadCode,
       DexItemFactory dexItemFactory,
       DexString highestSortingString,
       Timing timing) {
-    assert programClasses != null;
     this.proguardMap = proguardMap;
-    this.programClasses = programClasses;
-    this.programResourceProviders = programResourceProviders;
+    this.dataResourceProviders = dataResourceProviders;
     this.mainDexList = mainDexList;
     this.deadCode = deadCode;
     this.dexItemFactory = dexItemFactory;
@@ -72,23 +64,38 @@
   // Also note that the order may add to non-determinism in reporting errors for invalid
   // code, but this non-determinism exists even with the same order of classes since we
   // may process classes concurrently and fail-fast on the first error.
-  private <T> boolean reorderClasses(List<T> classes) {
-    if (!InternalOptions.DETERMINISTIC_DEBUGGING) {
-      Collections.shuffle(classes);
+  private static class ReorderBox<T> {
+
+    private List<T> classes;
+
+    ReorderBox(List<T> classes) {
+      this.classes = classes;
     }
-    return true;
+
+    boolean reorderClasses() {
+      if (!InternalOptions.DETERMINISTIC_DEBUGGING) {
+        List<T> shuffled = new ArrayList<>(classes);
+        Collections.shuffle(shuffled);
+        classes = ImmutableList.copyOf(shuffled);
+      }
+      return true;
+    }
+
+    List<T> getClasses() {
+      return classes;
+    }
   }
 
+  abstract List<DexProgramClass> programClasses();
+
   public List<DexProgramClass> classes() {
-    programClasses.forceLoad(type -> true);
-    List<DexProgramClass> classes = programClasses.getAllClasses();
-    assert reorderClasses(classes);
-    return classes;
+    ReorderBox<DexProgramClass> box = new ReorderBox<>(programClasses());
+    assert box.reorderClasses();
+    return box.getClasses();
   }
 
   public Iterable<DexProgramClass> classesWithDeterministicOrder() {
-    programClasses.forceLoad(type -> true);
-    List<DexProgramClass> classes = programClasses.getAllClasses();
+    List<DexProgramClass> classes = new ArrayList<>(programClasses());
     // To keep the order deterministic, we sort the classes by their type, which is a unique key.
     classes.sort((a, b) -> a.type.slowCompareTo(b.type));
     return classes;
@@ -96,10 +103,7 @@
 
   public abstract DexClass definitionFor(DexType type);
 
-  public DexProgramClass programDefinitionFor(DexType type) {
-    DexClass clazz = programClasses.get(type);
-    return clazz == null ? null : clazz.asProgramClass();
-  }
+  public abstract DexProgramClass programDefinitionFor(DexType type);
 
   @Override
   public abstract String toString();
@@ -116,9 +120,9 @@
     // new or removing existing classes), classpath and library
     // collections will be considered monolithic collections.
 
-    final List<DexProgramClass> programClasses;
+    final List<DexProgramClass> programClasses = new ArrayList<>();
 
-    final List<ProgramResourceProvider> programResourceProviders = new ArrayList<>();
+    final List<DataResourceProvider> dataResourceProviders = new ArrayList<>();
 
     public final DexItemFactory dexItemFactory;
     ClassNameMapper proguardMap;
@@ -130,7 +134,6 @@
     private final Collection<DexProgramClass> synthesizedClasses;
 
     public Builder(DexItemFactory dexItemFactory, Timing timing) {
-      this.programClasses = new ArrayList<>();
       this.dexItemFactory = dexItemFactory;
       this.timing = timing;
       this.deadCode = null;
@@ -140,8 +143,8 @@
     abstract T self();
 
     public Builder(DexApplication application) {
-      programClasses = application.programClasses.getAllClasses();
-      addProgramResourceProviders(application.programResourceProviders);
+      programClasses.addAll(application.programClasses());
+      dataResourceProviders.addAll(application.dataResourceProviders);
       proguardMap = application.getProguardMap();
       timing = application.timing;
       highestSortingString = application.highestSortingString;
@@ -164,11 +167,8 @@
       return self();
     }
 
-    public synchronized T addProgramResourceProviders(
-        List<ProgramResourceProvider> programResourceProviders) {
-      if (programResourceProviders != null) {
-        this.programResourceProviders.addAll(programResourceProviders);
-      }
+    public synchronized T addDataResourceProvider(DataResourceProvider provider) {
+      dataResourceProviders.add(provider);
       return self();
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 1e125d9..fbfb968 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -6,51 +6,77 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.graph.LazyLoadedDexApplication.AllClasses;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.ProgramClassCollection;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 
 public class DirectMappedDexApplication extends DexApplication {
 
-  private final AllClasses allClasses;
-  private final ImmutableMap<DexType, DexLibraryClass> libraryClasses;
+  // Unmodifiable mapping of all types to their definitions.
+  private final Map<DexType, DexClass> allClasses;
+  // Collections of the three different types for iteration.
+  private final ImmutableList<DexProgramClass> programClasses;
+  private final ImmutableList<DexClasspathClass> classpathClasses;
+  private final ImmutableList<DexLibraryClass> libraryClasses;
 
-  private DirectMappedDexApplication(ClassNameMapper proguardMap,
-      AllClasses allClasses,
-      ProgramClassCollection programClasses,
-      ImmutableList<ProgramResourceProvider> programResourceProviders,
-      ImmutableMap<DexType, DexLibraryClass> libraryClasses,
-      ImmutableSet<DexType> mainDexList, String deadCode,
-      DexItemFactory dexItemFactory, DexString highestSortingString,
+  private DirectMappedDexApplication(
+      ClassNameMapper proguardMap,
+      Map<DexType, DexClass> allClasses,
+      ImmutableList<DexProgramClass> programClasses,
+      ImmutableList<DexClasspathClass> classpathClasses,
+      ImmutableList<DexLibraryClass> libraryClasses,
+      ImmutableList<DataResourceProvider> dataResourceProviders,
+      ImmutableSet<DexType> mainDexList,
+      String deadCode,
+      DexItemFactory dexItemFactory,
+      DexString highestSortingString,
       Timing timing) {
-    super(proguardMap, programClasses, programResourceProviders, mainDexList, deadCode,
-        dexItemFactory, highestSortingString, timing);
-    this.allClasses = allClasses;
+    super(
+        proguardMap,
+        dataResourceProviders,
+        mainDexList,
+        deadCode,
+        dexItemFactory,
+        highestSortingString,
+        timing);
+    this.allClasses = Collections.unmodifiableMap(allClasses);
+    this.programClasses = programClasses;
+    this.classpathClasses = classpathClasses;
     this.libraryClasses = libraryClasses;
   }
 
+  public Collection<DexClass> allClasses() {
+    return allClasses.values();
+  }
+
+  @Override
+  List<DexProgramClass> programClasses() {
+    return programClasses;
+  }
+
   public Collection<DexLibraryClass> libraryClasses() {
-    return libraryClasses.values();
+    return libraryClasses;
   }
 
   @Override
   public DexClass definitionFor(DexType type) {
     assert type.isClassType() : "Cannot lookup definition for type: " + type;
-    DexClass result = programClasses.get(type);
-    if (result == null) {
-      result = libraryClasses.get(type);
-    }
-    return result;
+    return allClasses.get(type);
+  }
+
+  @Override
+  public DexProgramClass programDefinitionFor(DexType type) {
+    DexClass clazz = definitionFor(type);
+    return clazz instanceof DexProgramClass ? clazz.asProgramClass() : null;
   }
 
   @Override
@@ -76,8 +102,7 @@
   public DirectMappedDexApplication rewrittenWithLense(GraphLense graphLense) {
     // As a side effect, this will rebuild the program classes and library classes maps.
     DirectMappedDexApplication rewrittenApplication = this.builder().build().asDirect();
-    assert rewrittenApplication.mappingIsValid(graphLense, programClasses.getAllTypes());
-    assert rewrittenApplication.mappingIsValid(graphLense, libraryClasses.keySet());
+    assert rewrittenApplication.mappingIsValid(graphLense, allClasses.keySet());
     return rewrittenApplication;
   }
 
@@ -99,22 +124,22 @@
 
   public static class Builder extends DexApplication.Builder<Builder> {
 
-    private final AllClasses allClasses;
-    private final List<DexLibraryClass> libraryClasses = new ArrayList<>();
+    private final ImmutableList<DexLibraryClass> libraryClasses;
+    private final ImmutableList<DexClasspathClass> classpathClasses;
 
     Builder(LazyLoadedDexApplication application) {
       super(application);
       // As a side-effect, this will force-load all classes.
-      this.allClasses = application.loadAllClasses();
-      Map<DexType, DexClass> allClasses = this.allClasses.getClasses();
-      // TODO(120884788): This filter will only add library classes which are not program classes.
-      Iterables.filter(allClasses.values(), DexLibraryClass.class).forEach(libraryClasses::add);
+      AllClasses allClasses = application.loadAllClasses();
+      assert application.programClasses().equals(allClasses.getProgramClasses());
+      libraryClasses = allClasses.getLibraryClasses();
+      classpathClasses = allClasses.getClasspathClasses();
     }
 
     private Builder(DirectMappedDexApplication application) {
       super(application);
-      this.allClasses = application.allClasses;
-      this.libraryClasses.addAll(application.libraryClasses.values());
+      libraryClasses = application.libraryClasses;
+      classpathClasses = application.classpathClasses;
     }
 
     @Override
@@ -125,13 +150,27 @@
     @Override
     public DexApplication build() {
       // Rebuild the map. This will fail if keys are not unique.
+      // TODO(zerny): It seems weird that we have conflict resolution here.
+      ImmutableList<DexProgramClass> newProgramClasses =
+          ImmutableList.copyOf(
+              ProgramClassCollection.create(
+                      programClasses, ProgramClassCollection::resolveClassConflictImpl)
+                  .getAllClasses());
+      // TODO(zerny): Consider not rebuilding the map if no program classes are added.
+      Map<DexType, DexClass> allClasses = new IdentityHashMap<>(
+          newProgramClasses.size() + classpathClasses.size() + libraryClasses.size());
+      // Note: writing classes in reverse priority order, so a duplicate will be correctly ordered.
+      // There should never be duplicates and that is asserted in the addAll subroutine.
+      addAll(allClasses, libraryClasses);
+      addAll(allClasses, classpathClasses);
+      addAll(allClasses, programClasses);
       return new DirectMappedDexApplication(
           proguardMap,
           allClasses,
-          ProgramClassCollection.create(
-              programClasses, ProgramClassCollection::resolveClassConflictImpl),
-          ImmutableList.copyOf(programResourceProviders),
-          libraryClasses.stream().collect(ImmutableMap.toImmutableMap(c -> c.type, c -> c)),
+          newProgramClasses,
+          classpathClasses,
+          libraryClasses,
+          ImmutableList.copyOf(dataResourceProviders),
           ImmutableSet.copyOf(mainDexList),
           deadCode,
           dexItemFactory,
@@ -139,4 +178,12 @@
           timing);
     }
   }
+
+  private static <T extends DexClass> void addAll(
+      Map<DexType, DexClass> allClasses, Iterable<T> toAdd) {
+    for (DexClass clazz : toAdd) {
+      DexClass old = allClasses.put(clazz.type, clazz);
+      assert old == null;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index b236428..a58b9ac 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -6,7 +6,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.ClasspathClassCollection;
@@ -15,32 +15,49 @@
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
 import java.util.IdentityHashMap;
+import java.util.List;
 import java.util.Map;
 
 public class LazyLoadedDexApplication extends DexApplication {
 
+  private final ProgramClassCollection programClasses;
   private final ClasspathClassCollection classpathClasses;
   private final LibraryClassCollection libraryClasses;
 
-  /**
-   * Constructor should only be invoked by the DexApplication.Builder.
-   */
-  private LazyLoadedDexApplication(ClassNameMapper proguardMap,
+  /** Constructor should only be invoked by the DexApplication.Builder. */
+  private LazyLoadedDexApplication(
+      ClassNameMapper proguardMap,
       ProgramClassCollection programClasses,
-      ImmutableList<ProgramResourceProvider> programResourceProviders,
+      ImmutableList<DataResourceProvider> dataResourceProviders,
       ClasspathClassCollection classpathClasses,
       LibraryClassCollection libraryClasses,
-      ImmutableSet<DexType> mainDexList, String deadCode,
-      DexItemFactory dexItemFactory, DexString highestSortingString,
+      ImmutableSet<DexType> mainDexList,
+      String deadCode,
+      DexItemFactory dexItemFactory,
+      DexString highestSortingString,
       Timing timing) {
-    super(proguardMap, programClasses, programResourceProviders, mainDexList, deadCode,
-        dexItemFactory, highestSortingString, timing);
+    super(
+        proguardMap,
+        dataResourceProviders,
+        mainDexList,
+        deadCode,
+        dexItemFactory,
+        highestSortingString,
+        timing);
+    this.programClasses = programClasses;
     this.classpathClasses = classpathClasses;
     this.libraryClasses = libraryClasses;
   }
 
   @Override
+  List<DexProgramClass> programClasses() {
+    programClasses.forceLoad(t -> true);
+    return programClasses.getAllClasses();
+  }
+
+  @Override
   public DexClass definitionFor(DexType type) {
     assert type.isClassType() : "Cannot lookup definition for type: " + type;
     DexClass clazz = programClasses.get(type);
@@ -53,59 +70,97 @@
     return clazz;
   }
 
+  @Override
+  public DexProgramClass programDefinitionFor(DexType type) {
+    assert type.isClassType() : "Cannot lookup definition for type: " + type;
+    return programClasses.get(type);
+  }
+
   static class AllClasses {
-    private Map<DexType, DexClass> libraryClasses;
-    private Map<DexType, DexClass> classpathClasses;
-    private Map<DexType, DexClass> programClasses;
-    private Map<DexType, DexClass> classes;
+
+    // Mapping of all types to their definitions.
+    private final Map<DexType, DexClass> allClasses;
+    // Collections of the three different types for iteration.
+    private final ImmutableList<DexProgramClass> programClasses;
+    private final ImmutableList<DexClasspathClass> classpathClasses;
+    private final ImmutableList<DexLibraryClass> libraryClasses;
 
     AllClasses(
-        LibraryClassCollection libraryClasses,
-        ClasspathClassCollection classpathClasses,
-        ProgramClassCollection programClasses) {
-      load(libraryClasses, classpathClasses, programClasses);
-
+        LibraryClassCollection libraryClassesLoader,
+        ClasspathClassCollection classpathClassesLoader,
+        ProgramClassCollection programClassesLoader) {
       // Collect loaded classes in the precedence order program classes, class path classes and
       // library classes.
-      // TODO(b/120884788): Change this.
-      classes = new IdentityHashMap<>();
-      classes.putAll(this.programClasses);
-      if (classpathClasses != null) {
-        classpathClasses.getAllClasses().forEach(clazz -> classes.putIfAbsent(clazz.type, clazz));
+      // TODO(b/120884788): Change library priority.
+      assert programClassesLoader != null;
+      // Program classes are supposed to be loaded, but force-loading them is no-op.
+      programClassesLoader.forceLoad(type -> true);
+      Map<DexType, DexProgramClass> allProgramClasses = programClassesLoader.getAllClassesInMap();
+      int expectedMaxSize = allProgramClasses.size();
+      programClasses = ImmutableList.copyOf(allProgramClasses.values());
+
+      Map<DexType, DexClasspathClass> allClasspathClasses = null;
+      if (classpathClassesLoader != null) {
+        classpathClassesLoader.forceLoad(type -> true);
+        allClasspathClasses = classpathClassesLoader.getAllClassesInMap();
+        expectedMaxSize += allClasspathClasses.size();
       }
-      if (libraryClasses != null) {
-        libraryClasses.getAllClasses().forEach(clazz -> classes.putIfAbsent(clazz.type, clazz));
+
+      Map<DexType, DexLibraryClass> allLibraryClasses = null;
+      if (libraryClassesLoader != null) {
+        libraryClassesLoader.forceLoad(type -> true);
+        allLibraryClasses = libraryClassesLoader.getAllClassesInMap();
+        expectedMaxSize += allLibraryClasses.size();
       }
+
+      // Note: using hash map for building as the immutable builder does not support contains.
+      Map<DexType, DexClass> prioritizedClasses = new IdentityHashMap<>(expectedMaxSize);
+      prioritizedClasses.putAll(allProgramClasses);
+
+      if (allClasspathClasses != null) {
+        ImmutableList.Builder<DexClasspathClass> builder = ImmutableList.builder();
+        allClasspathClasses.forEach(
+            (type, clazz) -> {
+              if (!prioritizedClasses.containsKey(type)) {
+                prioritizedClasses.put(type, clazz);
+                builder.add(clazz);
+              }
+            });
+        classpathClasses = builder.build();
+      } else {
+        classpathClasses = ImmutableList.of();
+      }
+
+      if (allLibraryClasses != null) {
+        ImmutableList.Builder<DexLibraryClass> builder = ImmutableList.builder();
+        allLibraryClasses.forEach(
+            (type, clazz) -> {
+              if (!prioritizedClasses.containsKey(type)) {
+                prioritizedClasses.put(type, clazz);
+                builder.add(clazz);
+              }
+            });
+        libraryClasses = builder.build();
+      } else {
+        libraryClasses = ImmutableList.of();
+      }
+      allClasses = Collections.unmodifiableMap(prioritizedClasses);
     }
 
-    public Map<DexType, DexClass> getLibraryClasses() {
-      return libraryClasses;
+    public Map<DexType, DexClass> getAllClasses() {
+      return allClasses;
     }
 
-    public Map<DexType, DexClass> getClasspathClasses() {
+    public ImmutableList<DexProgramClass> getProgramClasses() {
+      return programClasses;
+    }
+
+    public ImmutableList<DexClasspathClass> getClasspathClasses() {
       return classpathClasses;
     }
 
-    public Map<DexType, DexClass> getClasses() {
-      return classes;
-    }
-
-    private void load(
-        LibraryClassCollection libraryClasses,
-        ClasspathClassCollection classpathClasses,
-        ProgramClassCollection programClasses) {
-      if (libraryClasses != null) {
-        libraryClasses.forceLoad(type -> true);
-        this.libraryClasses = libraryClasses.getAllClassesInMap();
-      }
-      if (classpathClasses != null) {
-        classpathClasses.forceLoad(type -> true);
-        this.classpathClasses = classpathClasses.getAllClassesInMap();
-      }
-      assert programClasses != null;
-      // Program classes are supposed to be loaded, but force-loading them is no-op.
-      programClasses.forceLoad(type -> true);
-      this.programClasses = programClasses.getAllClassesInMap();
+    public ImmutableList<DexLibraryClass> getLibraryClasses() {
+      return libraryClasses;
     }
   }
 
@@ -156,7 +211,7 @@
       return new LazyLoadedDexApplication(
           proguardMap,
           ProgramClassCollection.create(programClasses, resolver),
-          ImmutableList.copyOf(programResourceProviders),
+          ImmutableList.copyOf(dataResourceProviders),
           classpathClasses,
           libraryClasses,
           ImmutableSet.copyOf(mainDexList),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java
deleted file mode 100644
index b708ae9..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.GraphLense;
-import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableMap;
-
-class InterfaceMethodDesugaringLense extends NestedGraphLense {
-
-  InterfaceMethodDesugaringLense(
-      BiMap<DexMethod, DexMethod> methodMapping,
-      GraphLense previous, DexItemFactory factory) {
-    super(
-        ImmutableMap.of(),
-        methodMapping,
-        ImmutableMap.of(),
-        ImmutableBiMap.of(),
-        methodMapping.inverse(),
-        previous,
-        factory);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/ClassMap.java b/src/main/java/com/android/tools/r8/utils/ClassMap.java
index 33c1bf3..d8366df 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassMap.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassMap.java
@@ -135,11 +135,11 @@
     return loadedClasses;
   }
 
-  public Map<DexType, DexClass> getAllClassesInMap() {
+  public Map<DexType, T> getAllClassesInMap() {
     if (classProvider.get() != null) {
       throw new Unreachable("Getting all classes from not fully loaded collection.");
     }
-    ImmutableMap.Builder<DexType, DexClass> builder = ImmutableMap.builder();
+    ImmutableMap.Builder<DexType, T> builder = ImmutableMap.builder();
     // This is fully loaded, so the class map will no longer change.
     for (Map.Entry<DexType, Supplier<T>> entry : classes.entrySet()) {
       builder.put(entry.getKey(), entry.getValue().get());
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 52952a5..8b2eaf5 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -2118,6 +2118,7 @@
     ArtCommandBuilder builder = buildArtCommand(processedFile, specification, dexVm);
     if (dexVm.isNewerThan(DexVm.ART_4_4_4_HOST)) {
       builder.appendArtOption("-Ximage:/system/non/existent/image.art");
+      builder.appendArtOption("-Xnoimage-dex2oat");
     }
     for (String s : ToolHelper.getBootLibs(dexVm)) {
       builder.appendBootClassPath(new File(s).getCanonicalPath());
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 4cefe0c..12735cf 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -6,27 +6,24 @@
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.TestRuntime.DexRuntime;
+import com.android.tools.r8.TestRuntime.NoneRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.errors.Unreachable;
-import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class TestParametersBuilder {
 
   // Static computation of VMs configured as available by the testing invocation.
-  private static final List<CfVm> availableCfVms = getAvailableCfVms();
-  private static final List<DexVm> availableDexVms = getAvailableDexVms();
+  private static final List<TestRuntime> availableRuntimes =
+      getAvailableRuntimes().collect(Collectors.toList());
 
-  // Predicate describing which available CF runtimes are applicable to the test.
-  // Built via the methods found below. Default none.
-  private Predicate<CfVm> cfRuntimePredicate = vm -> false;
-
-  // Predicate describing which available DEX runtimes are applicable to the test.
-  // Built via the methods found below. Default none.
-  private Predicate<DexVm.Version> dexRuntimePredicate = vm -> false;
+  // Predicate describing which test parameters are applicable to the test.
+  // Built via the methods found below. Default to no applicable parameters, i.e., the emtpy set.
+  private Predicate<TestParameters> filter = param -> false;
 
   private TestParametersBuilder() {}
 
@@ -34,110 +31,108 @@
     return new TestParametersBuilder();
   }
 
+  private TestParametersBuilder withFilter(Predicate<TestParameters> predicate) {
+    filter = filter.or(predicate);
+    return this;
+  }
+
+  private TestParametersBuilder withCfRuntimeFilter(Predicate<CfVm> predicate) {
+    return withFilter(p -> p.isCfRuntime() && predicate.test(p.getRuntime().asCf().getVm()));
+  }
+
+  private TestParametersBuilder withDexRuntimeFilter(Predicate<DexVm.Version> predicate) {
+    return withFilter(
+        p -> p.isDexRuntime() && predicate.test(p.getRuntime().asDex().getVm().getVersion()));
+  }
+
+  public TestParametersBuilder withNoneRuntime() {
+    return withFilter(p -> p.getRuntime() == NoneRuntime.getInstance());
+  }
+
   public TestParametersBuilder withAllRuntimes() {
     return withCfRuntimes().withDexRuntimes();
   }
 
   /** Add specific runtime if available. */
   public TestParametersBuilder withCfRuntime(CfVm runtime) {
-    cfRuntimePredicate = cfRuntimePredicate.or(vm -> vm == runtime);
-    return this;
+    return withCfRuntimeFilter(vm -> vm == runtime);
   }
 
   /** Add all available CF runtimes. */
   public TestParametersBuilder withCfRuntimes() {
-    cfRuntimePredicate = vm -> true;
-    return this;
+    return withCfRuntimeFilter(vm -> true);
   }
 
   /** Add all available CF runtimes between {@param startInclusive} and {@param endInclusive}. */
   public TestParametersBuilder withCfRuntimes(CfVm startInclusive, CfVm endInclusive) {
-    cfRuntimePredicate =
-        cfRuntimePredicate.or(
-            vm -> startInclusive.lessThanOrEqual(vm) && vm.lessThanOrEqual(endInclusive));
-    return this;
+    return withCfRuntimeFilter(
+        vm -> startInclusive.lessThanOrEqual(vm) && vm.lessThanOrEqual(endInclusive));
   }
 
   /** Add all available CF runtimes starting from and including {@param startInclusive}. */
   public TestParametersBuilder withCfRuntimesStartingFromIncluding(CfVm startInclusive) {
-    cfRuntimePredicate = cfRuntimePredicate.or(vm -> startInclusive.lessThanOrEqual(vm));
-    return this;
+    return withCfRuntimeFilter(vm -> startInclusive.lessThanOrEqual(vm));
   }
 
   /** Add all available CF runtimes starting from and excluding {@param startExcluding}. */
   public TestParametersBuilder withCfRuntimesStartingFromExcluding(CfVm startExcluding) {
-    cfRuntimePredicate = cfRuntimePredicate.or(vm -> startExcluding.lessThan(vm));
-    return this;
+    return withCfRuntimeFilter(vm -> startExcluding.lessThan(vm));
   }
 
   /** Add all available CF runtimes ending at and including {@param endInclusive}. */
   public TestParametersBuilder withCfRuntimesEndingAtIncluding(CfVm endInclusive) {
-    cfRuntimePredicate = cfRuntimePredicate.or(vm -> vm.lessThanOrEqual(endInclusive));
-    return this;
+    return withCfRuntimeFilter(vm -> vm.lessThanOrEqual(endInclusive));
   }
 
   /** Add all available CF runtimes ending at and excluding {@param endExclusive}. */
   public TestParametersBuilder withCfRuntimesEndingAtExcluding(CfVm endExclusive) {
-    cfRuntimePredicate = cfRuntimePredicate.or(vm -> vm.lessThan(endExclusive));
-    return this;
+    return withCfRuntimeFilter(vm -> vm.lessThan(endExclusive));
   }
 
   /** Add all available DEX runtimes. */
   public TestParametersBuilder withDexRuntimes() {
-    dexRuntimePredicate = vm -> true;
-    return this;
+    return withDexRuntimeFilter(vm -> true);
   }
 
   /** Add specific runtime if available. */
   public TestParametersBuilder withDexRuntime(DexVm.Version runtime) {
-    dexRuntimePredicate = dexRuntimePredicate.or(vm -> vm == runtime);
-    return this;
+    return withDexRuntimeFilter(vm -> vm == runtime);
   }
 
   /** Add all available CF runtimes between {@param startInclusive} and {@param endInclusive}. */
   public TestParametersBuilder withDexRuntimes(
       DexVm.Version startInclusive, DexVm.Version endInclusive) {
-    dexRuntimePredicate =
-        dexRuntimePredicate.or(
-            vm -> startInclusive.isOlderThanOrEqual(vm) && vm.isOlderThanOrEqual(endInclusive));
-    return this;
+    return withDexRuntimeFilter(
+        vm -> startInclusive.isOlderThanOrEqual(vm) && vm.isOlderThanOrEqual(endInclusive));
   }
 
   /** Add all available DEX runtimes starting from and including {@param startInclusive}. */
   public TestParametersBuilder withDexRuntimesStartingFromIncluding(DexVm.Version startInclusive) {
-    dexRuntimePredicate = dexRuntimePredicate.or(vm -> startInclusive.isOlderThanOrEqual(vm));
-    return this;
+    return withDexRuntimeFilter(vm -> startInclusive.isOlderThanOrEqual(vm));
   }
 
   /** Add all available DEX runtimes starting from and excluding {@param startExcluding}. */
   public TestParametersBuilder withDexRuntimesStartingFromExcluding(DexVm.Version startExcluding) {
-    dexRuntimePredicate =
-        dexRuntimePredicate.or(vm -> vm != startExcluding && startExcluding.isOlderThanOrEqual(vm));
-    return this;
+    return withDexRuntimeFilter(
+        vm -> vm != startExcluding && startExcluding.isOlderThanOrEqual(vm));
   }
 
   /** Add all available DEX runtimes ending at and including {@param endInclusive}. */
   public TestParametersBuilder withDexRuntimesEndingAtIncluding(DexVm.Version endInclusive) {
-    dexRuntimePredicate = dexRuntimePredicate.or(vm -> vm.isOlderThanOrEqual(endInclusive));
-    return this;
+    return withDexRuntimeFilter(vm -> vm.isOlderThanOrEqual(endInclusive));
   }
 
   /** Add all available DEX runtimes ending at and excluding {@param endExclusive}. */
   public TestParametersBuilder withDexRuntimesEndingAtExcluding(DexVm.Version endExclusive) {
-    dexRuntimePredicate =
-        dexRuntimePredicate.or(vm -> vm != endExclusive && vm.isOlderThanOrEqual(endExclusive));
-    return this;
+    return withDexRuntimeFilter(vm -> vm != endExclusive && vm.isOlderThanOrEqual(endExclusive));
   }
 
   public TestParametersCollection build() {
-    ImmutableList.Builder<TestParameters> parameters = ImmutableList.builder();
-    availableCfVms.stream()
-        .filter(cfRuntimePredicate)
-        .forEach(vm -> parameters.add(new TestParameters(new CfRuntime(vm))));
-    availableDexVms.stream()
-        .filter(vm -> dexRuntimePredicate.test(vm.getVersion()))
-        .forEach(vm -> parameters.add(new TestParameters(new DexRuntime(vm))));
-    return new TestParametersCollection(parameters.build());
+    return new TestParametersCollection(
+        getAvailableRuntimes()
+            .map(TestParameters::new)
+            .filter(filter)
+            .collect(Collectors.toList()));
   }
 
   // Public method to check that the CF runtime coincides with the system runtime.
@@ -158,33 +153,44 @@
     return isSystemJdk(vm);
   }
 
-  public static List<CfVm> getAvailableCfVms() {
-    String cfVmsProperty = System.getProperty("cf_vms");
-    if (cfVmsProperty != null) {
-      return Arrays.stream(cfVmsProperty.split(":"))
-          .filter(s -> !s.isEmpty())
-          .map(TestRuntime.CfVm::fromName)
-          .filter(TestParametersBuilder::isSupportedJdk)
-          .collect(Collectors.toList());
+  private static Stream<TestRuntime> getAvailableRuntimes() {
+    String runtimesProperty = System.getProperty("runtimes");
+    Stream<TestRuntime> runtimes;
+    if (runtimesProperty != null) {
+      runtimes =
+          Arrays.stream(runtimesProperty.split(":"))
+              .filter(s -> !s.isEmpty())
+              .map(
+                  name -> {
+                    TestRuntime runtime = TestRuntime.fromName(name);
+                    if (runtime != null) {
+                      return runtime;
+                    }
+                    throw new RuntimeException("Unexpected runtime property name: " + name);
+                  });
     } else {
-      // TODO(b/127785410) Support multiple VMs at the same time.
-      return Arrays.stream(TestRuntime.CfVm.values())
-          .filter(TestParametersBuilder::isSystemJdk)
-          .collect(Collectors.toList());
+      runtimes =
+          Stream.concat(
+              Stream.of(NoneRuntime.getInstance()),
+              Stream.concat(
+                  Arrays.stream(TestRuntime.CfVm.values()).map(CfRuntime::new),
+                  Arrays.stream(DexVm.Version.values()).map(DexRuntime::new)));
     }
+    // TODO(b/127785410) Support multiple VMs at the same time.
+    return runtimes.filter(runtime -> !runtime.isCf() || isSupportedJdk(runtime.asCf().getVm()));
+  }
+
+  public static List<CfVm> getAvailableCfVms() {
+    return getAvailableRuntimes()
+        .filter(TestRuntime::isCf)
+        .map(runtime -> runtime.asCf().getVm())
+        .collect(Collectors.toList());
   }
 
   public static List<DexVm> getAvailableDexVms() {
-    String dexVmsProperty = System.getProperty("dex_vms");
-    if (dexVmsProperty != null) {
-      return Arrays.stream(dexVmsProperty.split(":"))
-          .filter(s -> !s.isEmpty())
-          .map(v -> DexVm.fromShortName(v + "_host"))
-          .collect(Collectors.toList());
-    } else {
-      return Arrays.stream(DexVm.Version.values())
-          .map(v -> DexVm.fromShortName(v.toString() + "_host"))
-          .collect(Collectors.toList());
-    }
+    return getAvailableRuntimes()
+        .filter(TestRuntime::isDex)
+        .map(runtime -> runtime.asDex().getVm())
+        .collect(Collectors.toList());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/TestParametersCollection.java b/src/test/java/com/android/tools/r8/TestParametersCollection.java
index 53a0714..52bc8d5 100644
--- a/src/test/java/com/android/tools/r8/TestParametersCollection.java
+++ b/src/test/java/com/android/tools/r8/TestParametersCollection.java
@@ -3,16 +3,16 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import com.google.common.collect.ImmutableList;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.stream.Stream;
 import org.jetbrains.annotations.NotNull;
 
 public class TestParametersCollection implements Iterable<TestParameters> {
 
-  private final ImmutableList<TestParameters> parameters;
+  private final Collection<TestParameters> parameters;
 
-  public TestParametersCollection(ImmutableList<TestParameters> parameters) {
+  public TestParametersCollection(Collection<TestParameters> parameters) {
     assert parameters != null;
     this.parameters = parameters;
   }
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index d94a100..cd0bd3f 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -11,6 +11,23 @@
 // Base class for the runtime structure in the test parameters.
 public class TestRuntime {
 
+  public static TestRuntime fromName(String name) {
+    if (NoneRuntime.NAME.equals(name)) {
+      return NoneRuntime.getInstance();
+    }
+    CfVm cfVm = CfVm.fromName(name);
+    if (cfVm != null) {
+      return new CfRuntime(cfVm);
+    }
+    if (name.startsWith("dex-")) {
+      DexVm dexVm = DexVm.fromShortName(name.substring(4) + "_host");
+      if (dexVm != null) {
+        return new DexRuntime(dexVm);
+      }
+    }
+    return null;
+  }
+
   // Enum describing the possible/supported CF runtimes.
   public enum CfVm {
     JDK8("jdk8"),
@@ -24,7 +41,7 @@
           return value;
         }
       }
-      throw new Unreachable("Unexpected CfVm name: " + v);
+      return null;
     }
 
     CfVm(String name) {
@@ -53,10 +70,31 @@
     }
   }
 
+  public static class NoneRuntime extends TestRuntime {
+
+    private static final String NAME = "none";
+    private static final NoneRuntime INSTANCE = new NoneRuntime();
+
+    private NoneRuntime() {}
+
+    public static NoneRuntime getInstance() {
+      return INSTANCE;
+    }
+
+    @Override
+    public String toString() {
+      return NAME;
+    }
+  }
+
   // Wrapper for the DEX runtimes.
   public static class DexRuntime extends TestRuntime {
     private final DexVm vm;
 
+    public DexRuntime(DexVm.Version version) {
+      this(DexVm.fromVersion(version));
+    }
+
     public DexRuntime(DexVm vm) {
       assert vm != null;
       this.vm = vm;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 1ab645d..3853ced 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -222,6 +222,10 @@
       return SHORT_NAME_MAP.get(shortName);
     }
 
+    public static DexVm fromVersion(Version version) {
+      return SHORT_NAME_MAP.get(version.shortName + "_" + Kind.HOST.toString());
+    }
+
     public boolean isNewerThan(DexVm other) {
       return version.isNewerThan(other.version);
     }
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index ef53f73..2b00c20 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -81,7 +81,6 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -113,9 +112,9 @@
   private static final int MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS = 10;
   private static List<String> MANY_CLASSES;
 
-  @Parameters
+  @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withCfRuntimes().build();
+    return getTestParameters().withNoneRuntime().build();
   }
 
   public MainDexListTests(TestParameters parameters) {
diff --git a/tools/test.py b/tools/test.py
index 6f664eb..a37329f 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -87,9 +87,6 @@
   result.add_option('--jctf-compile-only', '--jctf_compile_only',
       help="Don't run, only compile JCTF tests.",
       default=False, action='store_true')
-  result.add_option('--aosp-jar', '--aosp_jar',
-      help='Run aosp_jar test.',
-      default=False, action='store_true')
   result.add_option('--disable-assertions', '--disable_assertions',
       help='Disable assertions when running tests.',
       default=False, action='store_true')
@@ -171,8 +168,6 @@
     gradle_args.append('-Ponly_jctf')
   if options.jctf_compile_only:
     gradle_args.append('-Pjctf_compile_only')
-  if options.aosp_jar:
-    gradle_args.append('-Paosp_jar')
   if options.disable_assertions:
     gradle_args.append('-Pdisable_assertions')
   if options.with_code_coverage:
@@ -255,27 +250,26 @@
 
   rotate_test_reports()
 
+  if options.only_jctf:
+    # Note: not setting -Pruntimes will run with all available runtimes.
+    return_code = gradle.RunGradle(gradle_args, throw_on_failure=False)
+    return 0
+
   # Now run tests on selected runtime(s).
   vms_to_test = [options.dex_vm] if options.dex_vm != "all" else ALL_ART_VMS
 
-  # The full set of VMs is configured in the first run, then set to empty below.
-  dex_vms_property = ':'.join(vms_to_test)
-
-  if options.only_jctf:
-    vms_to_test = ['default']
-
   for art_vm in vms_to_test:
     vm_suffix = "_" + options.dex_vm_kind if art_vm != "default" else ""
+    runtimes = ['dex-' + art_vm]
+    # Only append the "none" runtime and JVMs if running on the "default" DEX VM.
+    if art_vm == "default":
+      runtimes.extend(['jdk8', 'jdk9', 'none'])
     return_code = gradle.RunGradle(
         gradle_args + [
           '-Pdex_vm=%s' % art_vm + vm_suffix,
-          '-Pdex_vms=%s' % dex_vms_property
-        ] +
-        # Only run the CF VMs on the 'default' configuration.
-        ([] if art_vm == "default" else ['-Pcf_vms=']),
+          '-Pruntimes=%s' % ':'.join(runtimes),
+        ],
         throw_on_failure=False)
-    # Only run the full set of DEX VMs on the first run.
-    dex_vms_property = ""
     if options.generate_golden_files_to:
       sha1 = '%s' % utils.get_HEAD_sha1()
       with utils.ChangedWorkingDirectory(options.generate_golden_files_to):
diff --git a/tools/test_aosp_jar.py b/tools/test_aosp_jar.py
deleted file mode 100755
index 6f7e8f5..0000000
--- a/tools/test_aosp_jar.py
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-# for details. All rights reserved. Use of this source code is governed by a
-# BSD-style license that can be found in the LICENSE file.
-
-# Test prebuilt AOSP jar files: compile with D8 and run dex2out to validate
-
-from __future__ import print_function
-from glob import glob
-from itertools import chain
-from os.path import join
-import argparse
-import jdk
-import os
-import subprocess
-import sys
-
-import gradle
-
-import dex2oat
-import utils
-
-REPLAY_SCRIPT_DIR = join(utils.REPO_ROOT, 'third_party',
-    'android_cts_baseline', 'dx_replay')
-REPLAY_SCRIPT = join(REPLAY_SCRIPT_DIR, 'replay_script.py')
-OUT_DIR = join(REPLAY_SCRIPT_DIR, 'out')
-
-def parse_arguments():
-  parser = argparse.ArgumentParser(
-      description = 'Run D8 (CompatDX) and dex2oat on prebuilt AOSP jars.')
-  parser.add_argument('--no-build', default = False, action = 'store_true')
-  return parser.parse_args()
-
-def Main():
-  args = parse_arguments()
-
-  if not args.no_build:
-    gradle.RunGradle(['CompatDx'])
-
-  cmd = [REPLAY_SCRIPT, jdk.GetJavaExecutable(), '-jar', utils.COMPATDX_JAR]
-  utils.PrintCmd(cmd)
-  subprocess.check_call(cmd)
-
-  # collect dex files below OUT_DIR
-  dex_files = (chain.from_iterable(glob(join(x[0], '*.dex'))
-      for x in os.walk(OUT_DIR)))
-
-  for dex_file in dex_files:
-      dex2oat.run(dex_file)
-
-
-if __name__ == '__main__':
-  sys.exit(Main())