Merge "Make sure all golem-dependent dex-segments are reported"
diff --git a/build.gradle b/build.gradle
index a38fd63..f492319 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1597,18 +1597,17 @@
     if (project.hasProperty('tool')) {
         if (project.property('tool') == 'r8') {
             exclude "com/android/tools/r8/art/*/d8/**"
-            exclude "com/android/tools/r8/jctf/d8/**"
-            exclude "com/android/tools/r8/jctf/r8cf/**"
+            exclude "com/android/tools/r8/jctf/**"
         } else if (project.property('tool') == 'd8') {
-            exclude "com/android/tools/r8/art/*/r8/**"
-            exclude "com/android/tools/r8/jctf/r8/**"
-            exclude "com/android/tools/r8/jctf/r8cf/**"
+            if (project.hasProperty('only_jctf')) {
+                include "com/android/tools/r8/jctf/d8/**"
+            } else {
+                include "com/android/tools/r8/art/*/d8/**"
+            }
         } else {
             assert(project.property('tool') == 'r8cf')
-            exclude "com/android/tools/r8/art/*/d8/**"
-            exclude "com/android/tools/r8/art/*/r8/**"
-            exclude "com/android/tools/r8/jctf/d8/**"
-            exclude "com/android/tools/r8/jctf/r8/**"
+            assert(project.hasProperty('only_jctf'))
+            include "com/android/tools/r8/jctf/r8cf/**"
         }
     }
     if (!project.hasProperty('all_tests')) {
@@ -1617,9 +1616,6 @@
     if (!project.hasProperty('jctf') && !project.hasProperty('only_jctf')) {
         exclude "com/android/tools/r8/jctf/**"
     }
-    if (project.hasProperty('only_jctf')) {
-        include "com/android/tools/r8/jctf/**"
-    }
     if (project.hasProperty('shard_count') ) {
       assert project.hasProperty('shard_number')
       int shard_count = project.getProperty('shard_count') as Integer
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 1040635..a38e3bf 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -45,6 +45,11 @@
   }
 }
 
+builder_mixins {
+  name: "fast_bot"
+  execution_timeout_secs: 5400 # 1.5 hours
+}
+
 acl_sets {
   name: "ci"
   acls {
@@ -122,6 +127,7 @@
       name: "d8-linux"
       mixins: "linux"
       mixins: "normal"
+      mixins: "fast_bot"
       recipe {
         properties: "tool:d8"
         properties: "aosp_jar:True"
@@ -131,6 +137,7 @@
       name: "d8-linux-android-4.0.4"
       mixins: "linux"
       mixins: "normal"
+      mixins: "fast_bot"
       recipe {
         properties: "tool:d8"
         properties: "dex_vm:4.0.4"
@@ -149,6 +156,7 @@
       name: "d8-linux-android-4.4.4"
       mixins: "linux"
       mixins: "normal"
+      mixins: "fast_bot"
       recipe {
         properties: "tool:d8"
         properties: "dex_vm:4.4.4"
@@ -167,6 +175,7 @@
       name: "d8-linux-android-5.1.1"
       mixins: "linux"
       mixins: "normal"
+      mixins: "fast_bot"
       recipe {
         properties: "tool:d8"
         properties: "dex_vm:5.1.1"
@@ -185,6 +194,7 @@
       name: "d8-linux-android-6.0.1"
       mixins: "linux"
       mixins: "normal"
+      mixins: "fast_bot"
       recipe {
         properties: "tool:d8"
         properties: "dex_vm:6.0.1"
@@ -203,6 +213,7 @@
       name: "d8-linux-android-7.0.0"
       mixins: "linux"
       mixins: "normal"
+      mixins: "fast_bot"
       recipe {
         properties: "tool:d8"
         properties: "dex_vm:7.0.0"
@@ -218,29 +229,6 @@
       }
     }
     builders {
-      name: "d8-linux-jctf"
-      mixins: "linux"
-      mixins: "jctf"
-      execution_timeout_secs: 43200  # 12h
-      recipe {
-        properties: "tool:d8"
-        properties: "dex_vm:all"
-        properties: "only_jctf:True"
-      }
-    }
-    builders {
-      name: "d8-linux-jctf_release"
-      mixins: "linux"
-      mixins: "jctf"
-      dimensions: "jctf:true"
-      execution_timeout_secs: 43200  # 12h
-      recipe {
-        properties: "tool:d8"
-        properties: "dex_vm:all"
-        properties: "only_jctf:True"
-      }
-    }
-    builders {
       name: "d8-linux_release"
       mixins: "linux"
       mixins: "normal"
@@ -376,7 +364,7 @@
       mixins: "jctf"
       execution_timeout_secs: 43200  # 12h
       recipe {
-        properties: "tool:r8"
+        properties: "tool:d8"
         properties: "dex_vm:all"
         properties: "only_jctf:True"
       }
@@ -387,7 +375,7 @@
       mixins: "jctf"
       execution_timeout_secs: 43200  # 12h
       recipe {
-        properties: "tool:r8"
+        properties: "tool:d8"
         properties: "dex_vm:all"
         properties: "only_jctf:True"
       }
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 8dc59cb..1a3ae77 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -87,11 +87,6 @@
     short_name: "7.0.0"
   }
   builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-jctf"
-    category: "D8"
-    short_name: "jctf"
-  }
-  builders {
     name: "buildbucket/luci.r8.ci/windows"
     category: "win"
     short_name: "win"
@@ -180,11 +175,6 @@
     category: "D8"
     short_name: "7.0.0"
   }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-jctf_release"
-    category: "D8"
-    short_name: "jctf"
-  }
   # TODO(ricow): Windows on the release branches currently do not work
   #              There is no java available.
   #  builders {
@@ -282,11 +272,6 @@
     short_name: "7.0.0"
   }
   builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-jctf"
-    category: "D8"
-    short_name: "jctf"
-  }
-  builders {
     name: "buildbucket/luci.r8.ci/windows"
     category: "win"
     short_name: "win"
@@ -366,9 +351,4 @@
     category: "D8 release"
     short_name: "7.0.0"
   }
-  builders {
-    name: "buildbucket/luci.r8.ci/d8-linux-jctf_release"
-    category: "D8 release"
-    short_name: "jctf"
-  }
 }
diff --git a/infra/config/global/luci-notify.cfg b/infra/config/global/luci-notify.cfg
index c7f9ec7..bb27c81 100644
--- a/infra/config/global/luci-notify.cfg
+++ b/infra/config/global/luci-notify.cfg
@@ -83,16 +83,6 @@
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
-    name: "d8-linux-jctf"
-    bucket: "ci"
-    repository: "https://r8.googlesource.com/r8"
-  }
-  builders {
-    name: "d8-linux-jctf_release"
-    bucket: "ci"
-    repository: "https://r8.googlesource.com/r8"
-  }
-  builders {
     name: "d8-linux_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 f7e4493..dca0298 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -31,7 +31,6 @@
   triggers: "d8-linux-android-5.1.1"
   triggers: "d8-linux-android-6.0.1"
   triggers: "d8-linux-android-7.0.0"
-  triggers: "d8-linux-jctf"
   triggers: "linux"
   triggers: "linux"
   triggers: "linux-android-4.0.4"
@@ -58,7 +57,6 @@
   triggers: "d8-linux-android-5.1.1_release"
   triggers: "d8-linux-android-6.0.1_release"
   triggers: "d8-linux-android-7.0.0_release"
-  triggers: "d8-linux-jctf_release"
   triggers: "d8-linux_release"
   triggers: "linux-android-4.0.4_release"
   triggers: "linux-android-4.4.4_release"
@@ -215,26 +213,6 @@
 }
 
 job {
-  id: "d8-linux-jctf"
-  acl_sets: "default"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
-    builder: "d8-linux-jctf"
-  }
-}
-
-job {
-  id: "d8-linux-jctf_release"
-  acl_sets: "default"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
-    builder: "d8-linux-jctf_release"
-  }
-}
-
-job {
   id: "d8-linux_release"
   acl_sets: "default"
   buildbucket {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index af3c140..535eb25 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -339,8 +339,7 @@
           proguardSeedsData = bytes.toString();
         }
         if (options.enableTreeShaking) {
-          TreePruner pruner =
-              new TreePruner(application, appView.appInfo().withLiveness(), options);
+          TreePruner pruner = new TreePruner(application, appView.withLiveness());
           application = pruner.run();
 
           // Recompute the subtyping information.
@@ -355,11 +354,7 @@
         classesToRetainInnerClassAttributeFor =
             AnnotationRemover.computeClassesToRetainInnerClassAttributeFor(
                 appView.appInfo().withLiveness(), options);
-        new AnnotationRemover(
-                appView.appInfo().withLiveness(),
-                appView.graphLense(),
-                options,
-                classesToRetainInnerClassAttributeFor)
+        new AnnotationRemover(appView.withLiveness(), classesToRetainInnerClassAttributeFor)
             .ensureValid(compatibility)
             .run();
 
@@ -579,7 +574,7 @@
 
           AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
           if (options.enableTreeShaking) {
-            TreePruner pruner = new TreePruner(application, appViewWithLiveness.appInfo(), options);
+            TreePruner pruner = new TreePruner(application, appViewWithLiveness);
             application = pruner.run();
             appViewWithLiveness.setAppInfo(
                 appViewWithLiveness
@@ -597,11 +592,7 @@
             }
             // Remove annotations that refer to types that no longer exist.
             assert classesToRetainInnerClassAttributeFor != null;
-            new AnnotationRemover(
-                    appView.appInfo().withLiveness(),
-                    appView.graphLense(),
-                    options,
-                    classesToRetainInnerClassAttributeFor)
+            new AnnotationRemover(appView.withLiveness(), classesToRetainInnerClassAttributeFor)
                 .run();
             if (!mainDexClasses.isEmpty()) {
               // Remove types that no longer exists from the computed main dex list.
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 1cda441..45deb7f 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.5.11-dev";
+  public static final String LABEL = "1.5.12-dev";
 
   private Version() {
   }
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 057090e..9546590 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -628,7 +628,7 @@
     return builder.build();
   }
 
-  public DexEncodedMethod toForwardingMethod(DexClass holder, AppInfo appInfo) {
+  public DexEncodedMethod toForwardingMethod(DexClass holder, DexDefinitionSupplier definitions) {
     checkIfObsolete();
     // Clear the final flag, as this method is now overwritten. Do this before creating the builder
     // for the forwarding method, as the forwarding method will copy the access flags from this,
@@ -636,7 +636,7 @@
     // final.
     accessFlags.demoteFromFinal();
     DexMethod newMethod =
-        appInfo.dexItemFactory.createMethod(holder.type, method.proto, method.name);
+        definitions.dexItemFactory().createMethod(holder.type, method.proto, method.name);
     Invoke.Type type = accessFlags.isStatic() ? Invoke.Type.STATIC : Invoke.Type.SUPER;
     Builder builder = builder(this);
     builder.setMethod(newMethod);
@@ -646,7 +646,7 @@
       builder.accessFlags.setAbstract();
     } else {
       // Create code that forwards the call to the target.
-      DexClass target = appInfo.definitionFor(method.holder);
+      DexClass target = definitions.definitionFor(method.holder);
       builder.setCode(
           new SynthesizedCode(
               callerPosition ->
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 5dd36c6..95ba121 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -939,14 +939,17 @@
      * <p>Handle methods moved from interface to class or class to interface.
      */
     protected final Type mapVirtualInterfaceInvocationTypes(
-        AppInfo appInfo, DexMethod newMethod, DexMethod originalMethod, Type type) {
+        DexDefinitionSupplier definitions,
+        DexMethod newMethod,
+        DexMethod originalMethod,
+        Type type) {
       if (type == Type.VIRTUAL || type == Type.INTERFACE) {
         // Get the invoke type of the actual definition.
-        DexClass newTargetClass = appInfo.definitionFor(newMethod.holder);
+        DexClass newTargetClass = definitions.definitionFor(newMethod.holder);
         if (newTargetClass == null) {
           return type;
         }
-        DexClass originalTargetClass = appInfo.definitionFor(originalMethod.holder);
+        DexClass originalTargetClass = definitions.definitionFor(originalMethod.holder);
         if (originalTargetClass != null
             && (originalTargetClass.isInterface() ^ (type == Type.INTERFACE))) {
           // The invoke was wrong to start with, so we keep it wrong. This is to ensure we get
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index 92c07d1..f6aaf96 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -40,7 +40,6 @@
 class ClassNameMinifier {
 
   private final AppView<AppInfoWithLiveness> appView;
-  private final AppInfoWithLiveness appInfo;
   private final ClassNamingStrategy classNamingStrategy;
   private final PackageNamingStrategy packageNamingStrategy;
   private final Iterable<? extends DexClass> classes;
@@ -68,7 +67,6 @@
       PackageNamingStrategy packageNamingStrategy,
       Iterable<? extends DexClass> classes) {
     this.appView = appView;
-    this.appInfo = appView.appInfo();
     this.classNamingStrategy = classNamingStrategy;
     this.packageNamingStrategy = packageNamingStrategy;
     this.classes = classes;
@@ -136,7 +134,7 @@
     timing.end();
 
     timing.begin("rename-arrays");
-    appInfo.dexItemFactory.forAllTypes(this::renameArrayTypeIfNeeded);
+    appView.dexItemFactory().forAllTypes(this::renameArrayTypeIfNeeded);
     timing.end();
 
     return new ClassRenaming(Collections.unmodifiableMap(renaming), getPackageRenaming());
@@ -172,13 +170,13 @@
   }
 
   private void renameDanglingType(DexType type) {
-    if (appInfo.wasPruned(type)
+    if (appView.appInfo().wasPruned(type)
         && !renaming.containsKey(type)
         && !noObfuscationTypes.contains(type)) {
       // We have a type that is defined in the program source but is only used in a proto or
       // return type. As we don't need the class, we can rename it to anything as long as it is
       // unique.
-      assert appInfo.definitionFor(type) == null;
+      assert appView.definitionFor(type) == null;
       renaming.put(type, topLevelState.nextTypeName(type));
     }
   }
@@ -217,7 +215,7 @@
   }
 
   private DexType getOutClassForType(DexType type) {
-    DexClass clazz = appInfo.definitionFor(type);
+    DexClass clazz = appView.definitionFor(type);
     if (clazz == null) {
       return null;
     }
@@ -333,7 +331,7 @@
 
   private void renameArrayTypeIfNeeded(DexType type) {
     if (type.isArrayType()) {
-      DexType base = type.toBaseType(appInfo.dexItemFactory);
+      DexType base = type.toBaseType(appView.dexItemFactory());
       DexString value = renaming.get(base);
       if (value != null) {
         int dimensions = type.descriptor.numberOfLeadingSquareBrackets();
@@ -342,7 +340,7 @@
           builder.append('[');
         }
         builder.append(value.toString());
-        DexString descriptor = appInfo.dexItemFactory.createString(builder.toString());
+        DexString descriptor = appView.dexItemFactory().createString(builder.toString());
         renaming.put(type, descriptor);
       }
     }
@@ -371,7 +369,7 @@
       // R.class in Android, which contains constant IDs to assets, can be bundled at any time.
       // Insert `R` immediately so that the class name minifier can skip that name by default.
       StringBuilder rBuilder = new StringBuilder().append(packagePrefix).append("R;");
-      usedTypeNames.add(appInfo.dexItemFactory.createString(rBuilder.toString()));
+      usedTypeNames.add(appView.dexItemFactory().createString(rBuilder.toString()));
     }
 
     public String getPackageName() {
@@ -382,7 +380,7 @@
       StringBuilder nextName = new StringBuilder();
       if (!classNamingStrategy.bypassDictionary() && classDictionaryIterator.hasNext()) {
         nextName.append(packagePrefix).append(classDictionaryIterator.next()).append(';');
-        return appInfo.dexItemFactory.createString(nextName.toString());
+        return appView.dexItemFactory().createString(nextName.toString());
       } else {
         return classNamingStrategy.next(this, type, packagePrefix);
       }
@@ -450,6 +448,7 @@
   }
 
   private boolean isNotKotlinMetadata(DexAnnotation annotation) {
-    return annotation.annotation.type != appInfo.dexItemFactory.kotlin.metadata.kotlinMetadataType;
+    return annotation.annotation.type
+        != appView.dexItemFactory().kotlin.metadata.kotlinMetadataType;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index 96bba7a..def7262 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -14,7 +14,9 @@
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
+import java.io.PrintStream;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Function;
 
 class FieldNameMinifier extends MemberNameMinifier<DexField, DexType> {
@@ -41,17 +43,17 @@
     // dispatch in Java, field resolution still traverses the super type chain and external
     // code might use a subtype to reference the field.
     timing.begin("reserve-classes");
-    reserveNamesInSubtypes(appInfo.dexItemFactory.objectType, globalState);
+    reserveNamesInSubtypes(appView.dexItemFactory().objectType, globalState);
     timing.end();
     // Next, reserve field names in interfaces. These should only be static.
     timing.begin("reserve-interfaces");
-    DexType.forAllInterfaces(appInfo.dexItemFactory,
-        iface -> reserveNamesInSubtypes(iface, globalState));
+    DexType.forAllInterfaces(
+        appView.dexItemFactory(), iface -> reserveNamesInSubtypes(iface, globalState));
     timing.end();
     // Rename the definitions.
     timing.begin("rename-definitions");
-    renameFieldsInSubtypes(appInfo.dexItemFactory.objectType);
-    DexType.forAllInterfaces(appInfo.dexItemFactory, this::renameFieldsInSubtypes);
+    renameFieldsInClasses();
+    renameFieldsInInterfaces();
     timing.end();
     // Rename the references that are not rebound to definitions for some reasons.
     timing.begin("rename-references");
@@ -74,7 +76,7 @@
   }
 
   private void reserveNamesInSubtypes(DexType type, NamingState<DexType, ?> state) {
-    DexClass holder = appInfo.definitionFor(type);
+    DexClass holder = appView.definitionFor(type);
     if (holder == null) {
       return;
     }
@@ -94,19 +96,57 @@
     }
   }
 
-  private void renameFieldsInSubtypes(DexType type) {
-    DexClass clazz = appInfo.definitionFor(type);
+  private void renameFieldsInClasses() {
+    renameFieldsInSubclasses(appView.dexItemFactory().objectType, null);
+  }
+
+  private void renameFieldsInSubclasses(DexType type, DexType parent) {
+    DexClass clazz = appView.definitionFor(type);
     if (clazz == null) {
       return;
     }
+    assert !clazz.isInterface();
+    assert clazz.superType == parent;
+
     NamingState<DexType, ?> state = minifierState.getState(clazz.type);
     assert state != null;
-    clazz.forEachField(field -> renameField(field, state));
-    type.forAllExtendsSubtypes(this::renameFieldsInSubtypes);
+    for (DexEncodedField field : clazz.fields()) {
+      renameField(field, state);
+    }
+    for (DexType subclass : type.allExtendsSubtypes()) {
+      renameFieldsInSubclasses(subclass, type);
+    }
+  }
+
+  private void renameFieldsInInterfaces() {
+    for (DexType interfaceType : DexType.allInterfaces(appView.dexItemFactory())) {
+      renameFieldsInInterface(interfaceType);
+    }
+  }
+
+  private void renameFieldsInInterface(DexType type) {
+    DexClass clazz = appView.definitionFor(type);
+    if (clazz == null) {
+      return;
+    }
+    assert clazz.isInterface();
+    NamingState<DexType, ?> state = minifierState.getState(clazz.type);
+    assert state != null;
+    for (DexEncodedField field : clazz.fields()) {
+      renameField(field, state);
+    }
   }
 
   private void renameField(DexEncodedField encodedField, NamingState<DexType, ?> state) {
     DexField field = encodedField.field;
+
+    Set<String> loggingFilter = appView.options().extensiveFieldMinifierLoggingFilter;
+    if (!loggingFilter.isEmpty()) {
+      if (loggingFilter.contains(field.toSourceString())) {
+        print(field, state, System.out);
+      }
+    }
+
     if (!state.isReserved(field.name, field.type)) {
       renaming.put(
           field, state.assignNewNameFor(field, field.name, field.type, useUniqueMemberNames));
@@ -115,6 +155,7 @@
 
   private void renameNonReboundReferences() {
     // TODO(b/123068484): Collect non-rebound references instead of visiting all references.
+    AppInfoWithLiveness appInfo = appView.appInfo();
     Sets.union(
         Sets.union(appInfo.staticFieldReads.keySet(), appInfo.staticFieldWrites.keySet()),
         Sets.union(appInfo.instanceFieldReads.keySet(), appInfo.instanceFieldWrites.keySet()))
@@ -126,18 +167,18 @@
     if (renaming.containsKey(field)) {
       return;
     }
-    DexEncodedField definition = appInfo.definitionFor(field);
+    DexEncodedField definition = appView.definitionFor(field);
     if (definition != null) {
       assert definition.field == field;
       return;
     }
     // Now, `field` is reference. Find its definition and check if it's renamed.
-    DexClass holder = appInfo.definitionFor(field.holder);
+    DexClass holder = appView.definitionFor(field.holder);
     // We don't care pruned types or library classes.
     if (holder == null || holder.isLibraryClass()) {
       return;
     }
-    definition = appInfo.resolveField(field);
+    definition = appView.appInfo().resolveField(field);
     if (definition == null) {
       // The program is already broken in the sense that it has an unresolvable field reference.
       // Leave it as-is.
@@ -151,4 +192,12 @@
       renaming.put(field, renaming.get(definition.field));
     }
   }
+
+  private void print(DexField field, NamingState<DexType, ?> state, PrintStream out) {
+    out.println("--------------------------------------------------------------------------------");
+    out.println("FieldNameMinifier(`" + field.toSourceString() + "`)");
+    out.println("--------------------------------------------------------------------------------");
+    state.printState(field.type, minifierState::getStateKey, "", out);
+    out.println();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index 35c5147..d6053e3 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.code.ConstString;
 import com.android.tools.r8.code.DexItemBasedConstString;
 import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -33,14 +34,13 @@
  */
 class IdentifierMinifier {
 
-  private final AppInfoWithLiveness appInfo;
+  private final AppView<AppInfoWithLiveness> appView;
   private final ProguardClassFilter adaptClassStrings;
   private final NamingLens lens;
 
-  IdentifierMinifier(
-      AppInfoWithLiveness appInfo, ProguardClassFilter adaptClassStrings, NamingLens lens) {
-    this.appInfo = appInfo;
-    this.adaptClassStrings = adaptClassStrings;
+  IdentifierMinifier(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+    this.appView = appView;
+    this.adaptClassStrings = appView.options().getProguardConfiguration().getAdaptClassStrings();
     this.lens = lens;
   }
 
@@ -52,7 +52,7 @@
   }
 
   private void adaptClassStrings() {
-    for (DexProgramClass clazz : appInfo.classes()) {
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
       if (adaptClassStrings.matches(clazz.type)) {
         for (DexEncodedField field : clazz.staticFields()) {
           adaptClassStringsInStaticField(field);
@@ -110,14 +110,14 @@
       DexString renamed = lens.lookupDescriptor(type);
       // Create a new DexString only when the corresponding string literal will be replaced.
       if (renamed != originalLiteral) {
-        return appInfo.dexItemFactory.createString(descriptorToJavaType(renamed.toString()));
+        return appView.dexItemFactory().createString(descriptorToJavaType(renamed.toString()));
       }
     }
     return originalLiteral;
   }
 
   private void replaceDexItemBasedConstString() {
-    for (DexProgramClass clazz : appInfo.classes()) {
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
       // Some const strings could be moved to field's static value (from <clinit>).
       for (DexEncodedField field : clazz.staticFields()) {
         replaceDexItemBasedConstStringInStaticField(field);
@@ -136,10 +136,10 @@
           dexItemBasedValueString.getClassNameComputationInfo().needsToComputeClassName()
               ? computeClassName(
                   lens.lookupDescriptor(original.asDexType()),
-                  appInfo.definitionFor(original.asDexType()),
+                  appView.definitionFor(original.asDexType()),
                   dexItemBasedValueString.getClassNameComputationInfo(),
-                  appInfo.dexItemFactory)
-              : lens.lookupName(original, appInfo.dexItemFactory);
+                  appView.dexItemFactory())
+              : lens.lookupName(original, appView.dexItemFactory());
       encodedField.setStaticValue(new DexValueString(replacement));
     }
   }
@@ -166,10 +166,10 @@
               cnst.getClassNameComputationInfo().needsToComputeClassName()
                   ? computeClassName(
                       lens.lookupDescriptor(cnst.getItem().asDexType()),
-                      appInfo.definitionFor(cnst.getItem().asDexType()),
+                      appView.definitionFor(cnst.getItem().asDexType()),
                       cnst.getClassNameComputationInfo(),
-                      appInfo.dexItemFactory)
-                  : lens.lookupName(cnst.getItem(), appInfo.dexItemFactory);
+                      appView.dexItemFactory())
+                  : lens.lookupName(cnst.getItem(), appView.dexItemFactory());
           ConstString constString = new ConstString(cnst.AA, replacement);
           constString.setOffset(instruction.getOffset());
           instructions[i] = constString;
@@ -186,10 +186,10 @@
               cnst.getClassNameComputationInfo().needsToComputeClassName()
                   ? computeClassName(
                       lens.lookupDescriptor(cnst.getItem().asDexType()),
-                      appInfo.definitionFor(cnst.getItem().asDexType()),
+                      appView.definitionFor(cnst.getItem().asDexType()),
                       cnst.getClassNameComputationInfo(),
-                      appInfo.dexItemFactory)
-                  : lens.lookupName(cnst.getItem(), appInfo.dexItemFactory);
+                      appView.dexItemFactory())
+                  : lens.lookupName(cnst.getItem(), appView.dexItemFactory());
           instructions.set(i, new CfConstString(replacement));
         }
       }
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index 6c46119..9b55542 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.naming.MethodNameMinifier.shuffleMethods;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -15,7 +16,6 @@
 import com.android.tools.r8.naming.MethodNameMinifier.FrontierState;
 import com.android.tools.r8.naming.MethodNameMinifier.MethodNamingState;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.base.Equivalence;
 import com.google.common.base.Equivalence.Wrapper;
@@ -36,12 +36,11 @@
 
 public class InterfaceMethodNameMinifier {
 
-  private final AppInfoWithLiveness appInfo;
+  private final AppView<AppInfoWithLiveness> appView;
   private final Set<DexCallSite> desugaredCallSites;
   private final Equivalence<DexMethod> equivalence;
   private final FrontierState frontierState;
   private final MemberNameMinifier<DexMethod, DexProto>.State minifierState;
-  private final InternalOptions options;
 
   private final Map<DexCallSite, DexString> callSiteRenamings = new IdentityHashMap<>();
 
@@ -58,18 +57,16 @@
   private final Map<Wrapper<DexMethod>, Set<DexMethod>> sourceMethodsMap = new HashMap<>();
 
   InterfaceMethodNameMinifier(
-      AppInfoWithLiveness appInfo,
+      AppView<AppInfoWithLiveness> appView,
       Set<DexCallSite> desugaredCallSites,
       Equivalence<DexMethod> equivalence,
       FrontierState frontierState,
-      MemberNameMinifier<DexMethod, DexProto>.State minifierState,
-      InternalOptions options) {
-    this.appInfo = appInfo;
+      MemberNameMinifier<DexMethod, DexProto>.State minifierState) {
+    this.appView = appView;
     this.desugaredCallSites = desugaredCallSites;
     this.equivalence = equivalence;
     this.frontierState = frontierState;
     this.minifierState = minifierState;
-    this.options = options;
   }
 
   public Comparator<Wrapper<DexMethod>> createDefaultInterfaceMethodOrdering() {
@@ -81,7 +78,7 @@
   }
 
   private void reserveNamesInInterfaces() {
-    for (DexType type : DexType.allInterfaces(appInfo.dexItemFactory)) {
+    for (DexType type : DexType.allInterfaces(appView.dexItemFactory())) {
       assert type.isInterface();
       frontierState.allocateNamingStateAndReserve(type, type, null);
     }
@@ -95,13 +92,13 @@
     // frontier states of classes that implement them. We add the frontier states so that we can
     // reserve the names for later method naming.
     timing.begin("Compute map");
-    for (DexType type : DexType.allInterfaces(appInfo.dexItemFactory)) {
+    for (DexType type : DexType.allInterfaces(appView.dexItemFactory())) {
       assert type.isInterface();
-      DexClass clazz = appInfo.definitionFor(type);
+      DexClass clazz = appView.definitionFor(type);
       if (clazz != null) {
         assert clazz.isInterface();
         Set<NamingState<DexProto, ?>> collectedStates = getReachableStates(type);
-        for (DexEncodedMethod method : shuffleMethods(clazz.methods(), options)) {
+        for (DexEncodedMethod method : shuffleMethods(clazz.methods(), appView.options())) {
           addStatesToGlobalMapForMethod(method.method, collectedStates, type);
         }
       }
@@ -111,7 +108,7 @@
     // desugared lambdas this is a conservative estimate, as we don't track if the generated
     // lambda classes survive into the output. As multi-interface lambda expressions are rare
     // this is not a big deal.
-    Set<DexCallSite> liveCallSites = Sets.union(desugaredCallSites, appInfo.callSites);
+    Set<DexCallSite> liveCallSites = Sets.union(desugaredCallSites, appView.appInfo().callSites);
     // If the input program contains a multi-interface lambda expression that implements
     // interface methods with different protos, we need to make sure that
     // the implemented lambda methods are renamed to the same name.
@@ -128,7 +125,7 @@
           // Don't report errors, as the set of call sites is a conservative estimate, and can
           // refer to interfaces which has been removed.
           Set<DexEncodedMethod> implementedMethods =
-              appInfo.lookupLambdaImplementedMethods(callSite);
+              appView.appInfo().lookupLambdaImplementedMethods(callSite);
           if (implementedMethods.isEmpty()) {
             return;
           }
@@ -169,7 +166,7 @@
     List<Wrapper<DexMethod>> interfaceMethods =
         globalStateMap.keySet().stream()
             .filter(wrapper -> unificationParent.getOrDefault(wrapper, wrapper).equals(wrapper))
-            .sorted(options.testing.minifier.createInterfaceMethodOrdering(this))
+            .sorted(appView.options().testing.minifier.createInterfaceMethodOrdering(this))
             .collect(Collectors.toList());
 
     // Propagate reserved names to all states.
@@ -242,7 +239,7 @@
     DexMethod method = key.get();
     assert method != null;
 
-    Set<String> loggingFilter = options.extensiveInterfaceMethodMinifierLoggingFilter;
+    Set<String> loggingFilter = appView.options().extensiveInterfaceMethodMinifierLoggingFilter;
     if (!loggingFilter.isEmpty()) {
       if (sourceMethods.stream().map(DexMethod::toSourceString).anyMatch(loggingFilter::contains)) {
         print(method, sourceMethods, collectedStates, System.out);
@@ -361,7 +358,7 @@
   }
 
   private void collectSuperInterfaces(DexType iface, Set<DexType> interfaces) {
-    DexClass clazz = appInfo.definitionFor(iface);
+    DexClass clazz = appView.definitionFor(iface);
     // In cases where we lack the interface's definition, we can at least look at subtypes and
     // tie those up to get proper naming.
     if (clazz != null) {
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
index b6b50b9..5804e9c 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -11,8 +11,8 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.NamingState.InternalState;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import java.util.IdentityHashMap;
@@ -23,9 +23,7 @@
 abstract class MemberNameMinifier<MemberType, StateType extends CachedHashValueDexItem> {
 
   protected final AppView<AppInfoWithLiveness> appView;
-  protected final AppInfoWithLiveness appInfo;
   protected final RootSet rootSet;
-  protected final InternalOptions options;
   protected final List<String> dictionary;
 
   protected final Map<MemberType, DexString> renaming = new IdentityHashMap<>();
@@ -42,18 +40,21 @@
 
   MemberNameMinifier(
       AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
+    ProguardConfiguration proguardConfiguration = appView.options().getProguardConfiguration();
     this.appView = appView;
-    this.appInfo = appView.appInfo();
     this.rootSet = rootSet;
-    this.options = appView.options();
-    this.dictionary = options.getProguardConfiguration().getObfuscationDictionary();
-    this.useUniqueMemberNames = options.getProguardConfiguration().isUseUniqueClassMemberNames();
+    this.dictionary = proguardConfiguration.getObfuscationDictionary();
+    this.useUniqueMemberNames = proguardConfiguration.isUseUniqueClassMemberNames();
     this.overloadAggressively =
-        options.getProguardConfiguration().isOverloadAggressivelyWithoutUseUniqueClassMemberNames();
+        proguardConfiguration.isOverloadAggressivelyWithoutUseUniqueClassMemberNames();
     this.globalState =
         NamingState.createRoot(
-            appInfo.dexItemFactory, dictionary, getKeyTransform(), strategy, useUniqueMemberNames);
-    this.useApplyMapping = options.getProguardConfiguration().hasApplyMappingFile();
+            appView.dexItemFactory(),
+            dictionary,
+            getKeyTransform(),
+            strategy,
+            useUniqueMemberNames);
+    this.useApplyMapping = proguardConfiguration.hasApplyMappingFile();
   }
 
   abstract Function<StateType, ?> getKeyTransform();
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index e9932e1..a93c815 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -142,23 +142,23 @@
     timing.begin("Phase 2");
     InterfaceMethodNameMinifier interfaceMethodNameMinifier =
         new InterfaceMethodNameMinifier(
-            appInfo, desugaredCallSites, equivalence, frontierState, minifierState, options);
+            appView, desugaredCallSites, equivalence, frontierState, minifierState);
     interfaceMethodNameMinifier.assignNamesToInterfaceMethods(timing);
     timing.end();
     // Phase 3: Assign names top-down by traversing the subtype hierarchy.
     timing.begin("Phase 3");
-    assignNamesToClassesMethods(appInfo.dexItemFactory.objectType, false);
+    assignNamesToClassesMethods(appView.dexItemFactory().objectType, false);
     timing.end();
     // Phase 4: Do the same for private methods.
     timing.begin("Phase 4");
-    assignNamesToClassesMethods(appInfo.dexItemFactory.objectType, true);
+    assignNamesToClassesMethods(appView.dexItemFactory().objectType, true);
     timing.end();
 
     return new MethodRenaming(renaming, interfaceMethodNameMinifier.getCallSiteRenamings());
   }
 
   private void assignNamesToClassesMethods(DexType type, boolean doPrivates) {
-    DexClass holder = appInfo.definitionFor(type);
+    DexClass holder = appView.definitionFor(type);
     boolean shouldAssignName = holder != null && !alwaysReserveMemberNames(holder);
     if (shouldAssignName) {
       Map<Wrapper<DexMethod>, DexString> renamingAtThisLevel = new HashMap<>();
@@ -200,7 +200,7 @@
 
   private void reserveNamesInClasses() {
     reserveNamesInClasses(
-        appInfo.dexItemFactory.objectType, appInfo.dexItemFactory.objectType, null);
+        appView.dexItemFactory().objectType, appView.dexItemFactory().objectType, null);
   }
 
   private void reserveNamesInClasses(
@@ -211,7 +211,7 @@
 
     // If this is a library class (or effectively a library class as it is missing) move the
     // frontier forward.
-    DexClass holder = appInfo.definitionFor(type);
+    DexClass holder = appView.definitionFor(type);
     for (DexType subtype : type.allExtendsSubtypes()) {
       assert !subtype.isInterface();
       reserveNamesInClasses(
@@ -235,17 +235,17 @@
               ignore ->
                   parent == null
                       ? NamingState.createRoot(
-                          appInfo.dexItemFactory,
+                          appView.dexItemFactory(),
                           dictionary,
                           getKeyTransform(),
                           strategy,
                           useUniqueMemberNames)
                       : parent.createChild());
 
-      DexClass holder = appInfo.definitionFor(type);
+      DexClass holder = appView.definitionFor(type);
       if (holder != null) {
         boolean keepAll = alwaysReserveMemberNames(holder) || holder.accessFlags.isAnnotation();
-        for (DexEncodedMethod method : shuffleMethods(holder.methods(), options)) {
+        for (DexEncodedMethod method : shuffleMethods(holder.methods(), appView.options())) {
           // TODO(christofferqa): Wouldn't it be sufficient only to reserve names for non-private
           //  methods?
           if (keepAll || rootSet.noObfuscation.contains(method.method)) {
@@ -327,7 +327,7 @@
       out.print(".");
       out.print(name.toSourceString());
       out.println(proto.toSmaliString());
-      parent.printState(proto, indentation + "  ", out);
+      parent.printState(proto, stateKeyGetter, indentation + "  ", out);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index 18a23ba..a2212b0 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.naming;
 
 import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -30,16 +31,16 @@
 
 class MinifiedRenaming extends NamingLens {
 
-  private final AppInfo appInfo;
+  private final AppView<? extends AppInfo> appView;
   private final Map<String, String> packageRenaming;
   private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
 
   MinifiedRenaming(
+      AppView<? extends AppInfo> appView,
       ClassRenaming classRenaming,
       MethodRenaming methodRenaming,
-      FieldRenaming fieldRenaming,
-      AppInfo appInfo) {
-    this.appInfo = appInfo;
+      FieldRenaming fieldRenaming) {
+    this.appView = appView;
     this.packageRenaming = classRenaming.packageRenaming;
     renaming.putAll(classRenaming.classRenaming);
     renaming.putAll(methodRenaming.renaming);
@@ -140,21 +141,21 @@
       // Array methods are never renamed, so do not bother to check.
       return true;
     }
-    DexClass holder = appInfo.definitionFor(item.holder);
+    DexClass holder = appView.definitionFor(item.holder);
     if (holder == null || holder.isLibraryClass()) {
       return true;
     }
     // We don't know which invoke type this method is used for, so checks that it has been
     // rebound either way.
-    DexEncodedMethod staticTarget = appInfo.lookupStaticTarget(item);
-    DexEncodedMethod directTarget = appInfo.lookupDirectTarget(item);
-    DexEncodedMethod virtualTarget = appInfo.lookupVirtualTarget(item.holder, item);
+    DexEncodedMethod staticTarget = appView.appInfo().lookupStaticTarget(item);
+    DexEncodedMethod directTarget = appView.appInfo().lookupDirectTarget(item);
+    DexEncodedMethod virtualTarget = appView.appInfo().lookupVirtualTarget(item.holder, item);
     DexClass staticTargetHolder =
-        staticTarget != null ? appInfo.definitionFor(staticTarget.method.holder) : null;
+        staticTarget != null ? appView.definitionFor(staticTarget.method.holder) : null;
     DexClass directTargetHolder =
-        directTarget != null ? appInfo.definitionFor(directTarget.method.holder) : null;
+        directTarget != null ? appView.definitionFor(directTarget.method.holder) : null;
     DexClass virtualTargetHolder =
-        virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.holder) : null;
+        virtualTarget != null ? appView.definitionFor(virtualTarget.method.holder) : null;
     return (directTarget == null && staticTarget == null && virtualTarget == null)
         || (virtualTarget != null && virtualTarget.method == item)
         || (directTarget != null && directTarget.method == item)
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 0b7bf45..ebaf4a4 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.naming.NamingState.InternalState;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
 import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
@@ -29,37 +28,33 @@
 public class Minifier {
 
   private final AppView<AppInfoWithLiveness> appView;
-  private final AppInfoWithLiveness appInfo;
   private final RootSet rootSet;
   private final Set<DexCallSite> desugaredCallSites;
-  private final InternalOptions options;
 
   public Minifier(
       AppView<AppInfoWithLiveness> appView, RootSet rootSet, Set<DexCallSite> desugaredCallSites) {
     this.appView = appView;
-    this.appInfo = appView.appInfo();
     this.rootSet = rootSet;
     this.desugaredCallSites = desugaredCallSites;
-    this.options = appView.options();
   }
 
   public NamingLens run(Timing timing) {
-    assert options.enableMinification;
+    assert appView.options().enableMinification;
     timing.begin("MinifyClasses");
     ClassNameMinifier classNameMinifier =
         new ClassNameMinifier(
             appView,
             rootSet,
-            new MinificationClassNamingStrategy(appInfo.dexItemFactory),
+            new MinificationClassNamingStrategy(appView.dexItemFactory()),
             new MinificationPackageNamingStrategy(),
             // Use deterministic class order to make sure renaming is deterministic.
-            appInfo.classesWithDeterministicOrder());
+            appView.appInfo().classesWithDeterministicOrder());
     ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
     timing.end();
 
     assert new MinifiedRenaming(
-            classRenaming, MethodRenaming.empty(), FieldRenaming.empty(), appInfo)
-        .verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
+            appView, classRenaming, MethodRenaming.empty(), FieldRenaming.empty())
+        .verifyNoCollisions(appView.appInfo().classes(), appView.dexItemFactory());
 
     MemberNamingStrategy minifyMembers = new MinifierMemberNamingStrategy(appView.dexItemFactory());
     timing.begin("MinifyMethods");
@@ -68,20 +63,19 @@
             .computeRenaming(desugaredCallSites, timing);
     timing.end();
 
-    assert new MinifiedRenaming(classRenaming, methodRenaming, FieldRenaming.empty(), appInfo)
-        .verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
+    assert new MinifiedRenaming(appView, classRenaming, methodRenaming, FieldRenaming.empty())
+        .verifyNoCollisions(appView.appInfo().classes(), appView.dexItemFactory());
 
     timing.begin("MinifyFields");
     FieldRenaming fieldRenaming =
         new FieldNameMinifier(appView, rootSet, minifyMembers).computeRenaming(timing);
     timing.end();
 
-    NamingLens lens = new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
-    assert lens.verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
+    NamingLens lens = new MinifiedRenaming(appView, classRenaming, methodRenaming, fieldRenaming);
+    assert lens.verifyNoCollisions(appView.appInfo().classes(), appView.dexItemFactory());
 
     timing.begin("MinifyIdentifiers");
-    new IdentifierMinifier(
-        appInfo, options.getProguardConfiguration().getAdaptClassStrings(), lens).run();
+    new IdentifierMinifier(appView, lens).run();
     timing.end();
     return lens;
   }
@@ -136,7 +130,7 @@
 
   static class MinifierMemberNamingStrategy implements MemberNamingStrategy {
 
-    char[] EMPTY_CHAR_ARRAY = new char[0];
+    public static char[] EMPTY_CHAR_ARRAY = new char[0];
 
     private final DexItemFactory factory;
 
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
index c44c70d..502fae2 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -3,11 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import static com.android.tools.r8.naming.Minifier.MinifierMemberNamingStrategy.EMPTY_CHAR_ARRAY;
+
 import com.android.tools.r8.graph.CachedHashValueDexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.MemberNameMinifier.MemberNamingStrategy;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
@@ -73,12 +77,8 @@
     // TODO(herhut): Maybe allocate these sparsely and search via state chain.
     InternalState result = usedNames.get(key);
     if (result == null) {
-      if (parent != null) {
-        InternalState parentState = parent.getOrCreateInternalStateFor(key);
-        result = parentState.createChild();
-      } else {
-        result = new InternalState(itemFactory, null, dictionary);
-      }
+      InternalState parentState = parent != null ? parent.getOrCreateInternalStateFor(key) : null;
+      result = new InternalState(itemFactory, parentState, dictionary);
       usedNames.put(key, result);
     }
     return result;
@@ -139,12 +139,23 @@
     state.addRenaming(original, key, newName);
   }
 
-  void printState(ProtoType proto, String indentation, PrintStream out) {
+  void printState(
+      ProtoType proto,
+      Function<NamingState<ProtoType, ?>, DexType> stateKeyGetter,
+      String indentation,
+      PrintStream out) {
     KeyType key = keyTransform.apply(proto);
-    InternalState state = findInternalStateFor(key);
+    InternalState state = getOrCreateInternalStateFor(key);
+    out.print(indentation);
+    out.print("NamingState(node=`");
+    out.print(stateKeyGetter.apply(this).toSourceString());
+    out.print("`, proto=`");
+    out.print(proto.toSourceString());
+    out.print("`, key=`");
+    out.print(key.toString());
+    out.println("`)");
     if (state != null) {
-      state.printReservedNames(indentation, out);
-      state.printRenamings(indentation, out);
+      state.printInternalState(this, stateKeyGetter, indentation + "  ", out);
     } else {
       out.print(indentation);
       out.println("<NO STATE>");
@@ -189,10 +200,6 @@
           && (parentInternalState == null || parentInternalState.isAvailable(name));
     }
 
-    InternalState createChild() {
-      return new InternalState(itemFactory, this, dictionaryIterator);
-    }
-
     void reserveName(DexString name) {
       if (reservedNames == null) {
         reservedNames = Sets.newIdentityHashSet();
@@ -256,6 +263,43 @@
       }
     }
 
+    void printInternalState(
+        NamingState<ProtoType, ?> expectedNamingState,
+        Function<NamingState<ProtoType, ?>, DexType> stateKeyGetter,
+        String indentation,
+        PrintStream out) {
+      assert expectedNamingState == NamingState.this;
+
+      DexType stateKey = stateKeyGetter.apply(expectedNamingState);
+      out.print(indentation);
+      out.print("InternalState(node=`");
+      out.print(stateKey != null ? stateKey.toSourceString() : "<GLOBAL>");
+      out.println("`)");
+
+      printLastName(indentation + "  ", out);
+      printReservedNames(indentation + "  ", out);
+      printRenamings(indentation + "  ", out);
+
+      if (parentInternalState != null) {
+        parentInternalState.printInternalState(
+            expectedNamingState.parent, stateKeyGetter, indentation + "  ", out);
+      }
+    }
+
+    void printLastName(String indentation, PrintStream out) {
+      out.print(indentation);
+      out.print("Last name: ");
+      if (nameCount > 1) {
+        out.print(StringUtils.numberToIdentifier(EMPTY_CHAR_ARRAY, nameCount - 1, false));
+        out.print(" (name count: ");
+        out.print(nameCount);
+        out.print(")");
+      } else {
+        out.print("<NONE>");
+      }
+      out.println();
+    }
+
     void printReservedNames(String indentation, PrintStream out) {
       out.print(indentation);
       out.print("Reserved names:");
@@ -270,9 +314,6 @@
         }
       }
       out.println();
-      if (parentInternalState != null) {
-        parentInternalState.printReservedNames(indentation + "  ", out);
-      }
     }
 
     void printRenamings(String indentation, PrintStream out) {
@@ -295,9 +336,6 @@
         }
       }
       out.println();
-      if (parentInternalState != null) {
-        parentInternalState.printRenamings(indentation + "  ", out);
-      }
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 84c21f6..5ca0da3 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -38,11 +38,9 @@
 public class ProguardMapMinifier {
 
   private final AppView<AppInfoWithLiveness> appView;
-  private final AppInfoWithLiveness appInfo;
   private final RootSet rootSet;
   private final SeedMapper seedMapper;
   private final Set<DexCallSite> desugaredCallSites;
-  private final DexItemFactory factory;
 
   public ProguardMapMinifier(
       AppView<AppInfoWithLiveness> appView,
@@ -50,8 +48,6 @@
       SeedMapper seedMapper,
       Set<DexCallSite> desugaredCallSites) {
     this.appView = appView;
-    this.appInfo = appView.appInfo();
-    this.factory = appInfo.dexItemFactory;
     this.rootSet = rootSet;
     this.seedMapper = seedMapper;
     this.desugaredCallSites = desugaredCallSites;
@@ -69,24 +65,25 @@
     Map<DexReference, MemberNaming> memberNames = new IdentityHashMap<>();
     for (String key : seedMapper.getKeyset()) {
       ClassNamingForMapApplier classNaming = seedMapper.getMapping(key);
-      DexType type = factory.lookupType(factory.createString(key));
+      DexType type =
+          appView.dexItemFactory().lookupType(appView.dexItemFactory().createString(key));
       if (type == null) {
         // The map contains additional mapping of classes compared to what we have seen. This should
         // have no effect.
         continue;
       }
-      DexClass dexClass = appInfo.definitionFor(type);
+      DexClass dexClass = appView.definitionFor(type);
       if (dexClass == null) {
         continue;
       }
-      DexString mappedName = factory.createString(classNaming.renamedName);
-      DexType mappedType = factory.lookupType(mappedName);
+      DexString mappedName = appView.dexItemFactory().createString(classNaming.renamedName);
+      DexType mappedType = appView.dexItemFactory().lookupType(mappedName);
       // The mappedType has to be available:
       // - If it is null we have not seen it.
       // - If the mapped type is itself the name is already reserved (by itself).
       // - If the there is no definition for the mapped type we will not get a naming clash.
       // Otherwise, there will be a naming conflict.
-      if (mappedType != null && type != mappedType && appInfo.definitionFor(mappedType) != null) {
+      if (mappedType != null && type != mappedType && appView.definitionFor(mappedType) != null) {
         appView
             .options()
             .reporter
@@ -100,7 +97,8 @@
           memberNaming -> {
             Signature signature = memberNaming.getOriginalSignature();
             assert !signature.isQualified();
-            DexMethod originalMethod = ((MethodSignature) signature).toDexMethod(factory, type);
+            DexMethod originalMethod =
+                ((MethodSignature) signature).toDexMethod(appView.dexItemFactory(), type);
             assert !memberNames.containsKey(originalMethod);
             memberNames.put(originalMethod, memberNaming);
           });
@@ -108,7 +106,8 @@
           memberNaming -> {
             Signature signature = memberNaming.getOriginalSignature();
             assert !signature.isQualified();
-            DexField originalField = ((FieldSignature) signature).toDexField(factory, type);
+            DexField originalField =
+                ((FieldSignature) signature).toDexField(appView.dexItemFactory(), type);
             assert !memberNames.containsKey(originalField);
             memberNames.put(originalField, memberNaming);
           });
@@ -132,7 +131,7 @@
 
     ApplyMappingMemberNamingStrategy nameStrategy =
         new ApplyMappingMemberNamingStrategy(
-            memberNames, appInfo.dexItemFactory, appView.options().reporter);
+            memberNames, appView.dexItemFactory(), appView.options().reporter);
     timing.begin("MinifyMethods");
     MethodRenaming methodRenaming =
         new MethodNameMinifier(appView, rootSet, nameStrategy)
@@ -146,13 +145,10 @@
 
     appView.options().reporter.failIfPendingErrors();
 
-    NamingLens lens =
-        new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appView.appInfo());
+    NamingLens lens = new MinifiedRenaming(appView, classRenaming, methodRenaming, fieldRenaming);
 
     timing.begin("MinifyIdentifiers");
-    new IdentifierMinifier(
-            appInfo, appView.options().getProguardConfiguration().getAdaptClassStrings(), lens)
-        .run();
+    new IdentifierMinifier(appView, lens).run();
     timing.end();
 
     return lens;
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index b398c7d..325140d 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -4,9 +4,9 @@
 package com.android.tools.r8.optimize;
 
 import com.android.tools.r8.graph.AccessFlags;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -29,7 +29,7 @@
 
 public class MemberRebindingAnalysis {
 
-  private final AppInfoWithLiveness appInfo;
+  private final AppView<AppInfoWithLiveness> appView;
   private final GraphLense lense;
   private final InternalOptions options;
 
@@ -37,14 +37,14 @@
 
   public MemberRebindingAnalysis(AppView<AppInfoWithLiveness> appView) {
     assert appView.graphLense().isContextFreeForMethods();
-    this.appInfo = appView.appInfo();
+    this.appView = appView;
     this.lense = appView.graphLense();
     this.options = appView.options();
-    this.builder = MemberRebindingLense.builder(appInfo);
+    this.builder = MemberRebindingLense.builder(appView);
   }
 
   private DexMethod validTargetFor(DexMethod target, DexMethod original) {
-    DexClass clazz = appInfo.definitionFor(target.holder);
+    DexClass clazz = appView.definitionFor(target.holder);
     assert clazz != null;
     if (!clazz.isLibraryClass()) {
       return target;
@@ -56,12 +56,12 @@
     } else {
       newHolder = firstLibraryClass(target.holder, original.holder);
     }
-    return appInfo.dexItemFactory.createMethod(newHolder, original.proto, original.name);
+    return appView.dexItemFactory().createMethod(newHolder, original.proto, original.name);
   }
 
   private DexField validTargetFor(DexField target, DexField original,
       BiFunction<DexClass, DexField, DexEncodedField> lookup) {
-    DexClass clazz = appInfo.definitionFor(target.holder);
+    DexClass clazz = appView.definitionFor(target.holder);
     assert clazz != null;
     if (!clazz.isLibraryClass()) {
       return target;
@@ -72,12 +72,12 @@
     } else {
       newHolder = firstLibraryClass(target.holder, original.holder);
     }
-    return appInfo.dexItemFactory.createField(newHolder, original.type, original.name);
+    return appView.dexItemFactory().createField(newHolder, original.type, original.name);
   }
 
   private <T> DexType firstLibraryClassForInterfaceTarget(T target, DexType current,
       BiFunction<DexClass, T, ?> lookup) {
-    DexClass clazz = appInfo.definitionFor(current);
+    DexClass clazz = appView.definitionFor(current);
     Object potential = lookup.apply(clazz, target);
     if (potential != null) {
       // Found, return type.
@@ -101,24 +101,24 @@
   }
 
   private DexType firstLibraryClass(DexType top, DexType bottom) {
-    assert appInfo.definitionFor(top).isLibraryClass();
-    DexClass searchClass = appInfo.definitionFor(bottom);
+    assert appView.definitionFor(top).isLibraryClass();
+    DexClass searchClass = appView.definitionFor(bottom);
     while (!searchClass.isLibraryClass()) {
-      searchClass = appInfo.definitionFor(searchClass.superType);
+      searchClass = appView.definitionFor(searchClass.superType);
     }
     return searchClass.type;
   }
 
   private DexEncodedMethod classLookup(DexMethod method) {
-    return appInfo.resolveMethodOnClass(method.holder, method).asResultOfResolve();
+    return appView.appInfo().resolveMethodOnClass(method.holder, method).asResultOfResolve();
   }
 
   private DexEncodedMethod interfaceLookup(DexMethod method) {
-    return appInfo.resolveMethodOnInterface(method.holder, method).asResultOfResolve();
+    return appView.appInfo().resolveMethodOnInterface(method.holder, method).asResultOfResolve();
   }
 
   private DexEncodedMethod anyLookup(DexMethod method) {
-    return appInfo.resolveMethod(method.holder, method).asResultOfResolve();
+    return appView.appInfo().resolveMethod(method.holder, method).asResultOfResolve();
   }
 
   private void computeMethodRebinding(
@@ -130,7 +130,7 @@
       if (!method.holder.isClassType()) {
         continue;
       }
-      DexClass originalClass = appInfo.definitionFor(method.holder);
+      DexClass originalClass = appView.definitionFor(method.holder);
       if (originalClass == null || originalClass.isLibraryClass()) {
         continue;
       }
@@ -138,7 +138,7 @@
       // TODO(b/128404854) Rebind to the lowest library class or program class. For now we allow
       //  searching in library for methods, but this should be done on classpath instead.
       if (target != null && target.method != method) {
-        DexClass targetClass = appInfo.definitionFor(target.method.holder);
+        DexClass targetClass = appView.definitionFor(target.method.holder);
         if (!originalClass.isLibraryClass()) {
           // In Java bytecode, it is only possible to target interface methods that are in one of
           // the immediate super-interfaces via a super-invocation (see IndirectSuperInterfaceTest).
@@ -193,7 +193,7 @@
         findHolderForInterfaceMethodBridge(originalClass, targetClass.type);
     assert bridgeHolder != null;
     assert bridgeHolder != targetClass;
-    DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appInfo);
+    DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appView);
     bridgeHolder.addMethod(bridgeMethod);
     assert lookupTarget.apply(method) == bridgeMethod;
     return bridgeMethod;
@@ -203,10 +203,10 @@
     if (clazz.accessFlags.isInterface()) {
       return clazz;
     }
-    DexClass superClass = appInfo.definitionFor(clazz.superType);
+    DexClass superClass = appView.definitionFor(clazz.superType);
     if (superClass == null
         || superClass.isLibraryClass()
-        || !superClass.type.isSubtypeOf(iface, appInfo)) {
+        || !superClass.type.isSubtypeOf(iface, appView)) {
       return clazz;
     }
     return findHolderForInterfaceMethodBridge(superClass.asProgramClass(), iface);
@@ -214,14 +214,14 @@
 
   private boolean mayNeedBridgeForVisibility(DexType context, DexEncodedMethod method) {
     DexType holderType = method.method.holder;
-    DexClass holder = appInfo.definitionFor(holderType);
+    DexClass holder = appView.definitionFor(holderType);
     if (holder == null) {
       return false;
     }
     ConstraintWithTarget classVisibility =
-        ConstraintWithTarget.deriveConstraint(context, holderType, holder.accessFlags, appInfo);
+        ConstraintWithTarget.deriveConstraint(context, holderType, holder.accessFlags, appView);
     ConstraintWithTarget methodVisibility =
-        ConstraintWithTarget.deriveConstraint(context, holderType, method.accessFlags, appInfo);
+        ConstraintWithTarget.deriveConstraint(context, holderType, method.accessFlags, appView);
     // We may need bridge for visibility if the target class is not visible while the target method
     // is visible from the calling context.
     return classVisibility == ConstraintWithTarget.NEVER
@@ -244,7 +244,7 @@
       DexProgramClass bridgeHolder =
           findHolderForVisibilityBridge(originalClass, targetClass, packageDescriptor);
       assert bridgeHolder != null;
-      DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appInfo);
+      DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appView);
       bridgeHolder.addMethod(bridgeMethod);
       assert lookupTarget.apply(method) == bridgeMethod;
       return bridgeMethod;
@@ -259,13 +259,13 @@
     }
     DexProgramClass newHolder = null;
     // Recurse through supertype chain.
-    if (originalClass.superType.isSubtypeOf(targetClass.type, appInfo)) {
-      DexClass superClass = appInfo.definitionFor(originalClass.superType);
+    if (originalClass.superType.isSubtypeOf(targetClass.type, appView)) {
+      DexClass superClass = appView.definitionFor(originalClass.superType);
       newHolder = findHolderForVisibilityBridge(superClass, targetClass, packageDescriptor);
     } else {
       for (DexType iface : originalClass.interfaces.values) {
-        if (iface.isSubtypeOf(targetClass.type, appInfo)) {
-          DexClass interfaceClass = appInfo.definitionFor(iface);
+        if (iface.isSubtypeOf(targetClass.type, appView)) {
+          DexClass interfaceClass = appView.definitionFor(iface);
           newHolder = findHolderForVisibilityBridge(interfaceClass, targetClass, packageDescriptor);
         }
       }
@@ -296,7 +296,7 @@
               .allMatch(
                   context ->
                       isMemberVisibleFromOriginalContext(
-                          appInfo,
+                          appView,
                           context.method.holder,
                           target.field.holder,
                           target.accessFlags))) {
@@ -307,18 +307,21 @@
   }
 
   public static boolean isMemberVisibleFromOriginalContext(
-      AppInfo appInfo, DexType context, DexType holder, AccessFlags<?> memberAccessFlags) {
-    DexClass clazz = appInfo.definitionFor(holder);
+      DexDefinitionSupplier definitions,
+      DexType context,
+      DexType holder,
+      AccessFlags<?> memberAccessFlags) {
+    DexClass clazz = definitions.definitionFor(holder);
     if (clazz == null) {
       return false;
     }
     ConstraintWithTarget classVisibility =
-        ConstraintWithTarget.deriveConstraint(context, holder, clazz.accessFlags, appInfo);
+        ConstraintWithTarget.deriveConstraint(context, holder, clazz.accessFlags, definitions);
     if (classVisibility == ConstraintWithTarget.NEVER) {
       return false;
     }
     ConstraintWithTarget memberVisibility =
-        ConstraintWithTarget.deriveConstraint(context, holder, memberAccessFlags, appInfo);
+        ConstraintWithTarget.deriveConstraint(context, holder, memberAccessFlags, definitions);
     return memberVisibility != ConstraintWithTarget.NEVER;
   }
 
@@ -337,6 +340,7 @@
   }
 
   public GraphLense run() {
+    AppInfoWithLiveness appInfo = appView.appInfo();
     // Virtual invokes are on classes, so use class resolution.
     computeMethodRebinding(appInfo.virtualInvokes, this::classLookup, Type.VIRTUAL);
     // Interface invokes are always on interfaces, so use interface resolution.
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
index 5e19d48..8a7d1aa 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize;
 
 import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLense;
@@ -14,11 +15,13 @@
 import java.util.Map;
 
 public class MemberRebindingLense extends NestedGraphLense {
-  public static class Builder extends NestedGraphLense.Builder {
-    private final AppInfo appInfo;
 
-    protected Builder(AppInfo appInfo) {
-      this.appInfo = appInfo;
+  public static class Builder extends NestedGraphLense.Builder {
+
+    private final AppView<? extends AppInfo> appView;
+
+    protected Builder(AppView<? extends AppInfo> appView) {
+      this.appView = appView;
     }
 
     public GraphLense build(GraphLense previousLense) {
@@ -26,28 +29,34 @@
       if (methodMap.isEmpty() && fieldMap.isEmpty()) {
         return previousLense;
       }
-      return new MemberRebindingLense(appInfo, methodMap, fieldMap, previousLense);
+      return new MemberRebindingLense(appView, methodMap, fieldMap, previousLense);
     }
   }
 
-  private final AppInfo appInfo;
+  private final AppView<? extends AppInfo> appView;
 
   public MemberRebindingLense(
-      AppInfo appInfo,
+      AppView<? extends AppInfo> appView,
       Map<DexMethod, DexMethod> methodMap,
       Map<DexField, DexField> fieldMap,
       GraphLense previousLense) {
     super(
-        ImmutableMap.of(), methodMap, fieldMap, null, null, previousLense, appInfo.dexItemFactory);
-    this.appInfo = appInfo;
+        ImmutableMap.of(),
+        methodMap,
+        fieldMap,
+        null,
+        null,
+        previousLense,
+        appView.dexItemFactory());
+    this.appView = appView;
   }
 
-  public static Builder builder(AppInfo appInfo) {
-    return new Builder(appInfo);
+  public static Builder builder(AppView<? extends AppInfo> appView) {
+    return new Builder(appView);
   }
 
   @Override
   protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
-    return super.mapVirtualInterfaceInvocationTypes(appInfo, newMethod, originalMethod, type);
+    return super.mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 25e0832..02e7442 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexClass;
@@ -28,21 +29,14 @@
 
 public class AnnotationRemover {
 
-  private final AppInfoWithLiveness appInfo;
-  private final GraphLense lense;
+  private final AppView<AppInfoWithLiveness> appView;
   private final ProguardKeepAttributes keep;
-  private final InternalOptions options;
   private final Set<DexType> classesToRetainInnerClassAttributeFor;
 
   public AnnotationRemover(
-      AppInfoWithLiveness appInfo,
-      GraphLense lense,
-      InternalOptions options,
-      Set<DexType> classesToRetainInnerClassAttributeFor) {
-    this.appInfo = appInfo;
-    this.lense = lense;
-    this.keep = options.getProguardConfiguration().getKeepAttributes();
-    this.options = options;
+      AppView<AppInfoWithLiveness> appView, Set<DexType> classesToRetainInnerClassAttributeFor) {
+    this.appView = appView;
+    this.keep = appView.options().getProguardConfiguration().getKeepAttributes();
     this.classesToRetainInnerClassAttributeFor = classesToRetainInnerClassAttributeFor;
   }
 
@@ -51,7 +45,7 @@
    */
   private boolean filterAnnotations(DexAnnotation annotation) {
     return shouldKeepAnnotation(
-        annotation, isAnnotationTypeLive(annotation), appInfo.dexItemFactory, options);
+        annotation, isAnnotationTypeLive(annotation), appView.dexItemFactory(), appView.options());
   }
 
   static boolean shouldKeepAnnotation(
@@ -114,14 +108,15 @@
   }
 
   private boolean isAnnotationTypeLive(DexAnnotation annotation) {
-    DexType annotationType = annotation.annotation.type.toBaseType(appInfo.dexItemFactory);
-    DexClass definition = appInfo.definitionFor(annotationType);
+    DexType annotationType = annotation.annotation.type.toBaseType(appView.dexItemFactory());
+    DexClass definition = appView.definitionFor(annotationType);
     // TODO(b/73102187): How to handle annotations without definition.
-    if (options.enableTreeShaking && definition == null) {
+    if (appView.options().enableTreeShaking && definition == null) {
       return false;
     }
-    return definition == null || definition.isLibraryClass()
-        || appInfo.liveTypes.contains(annotationType);
+    return definition == null
+        || definition.isLibraryClass()
+        || appView.appInfo().liveTypes.contains(annotationType);
   }
 
   /**
@@ -148,7 +143,7 @@
   }
 
   public AnnotationRemover ensureValid(ProguardConfiguration.Builder compatibility) {
-    keep.ensureValid(options.forceProguardCompatibility, compatibility);
+    keep.ensureValid(appView.options().forceProguardCompatibility, compatibility);
     return this;
   }
 
@@ -226,7 +221,7 @@
   }
 
   public void run() {
-    for (DexProgramClass clazz : appInfo.classes()) {
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
       stripAttributes(clazz);
       clazz.annotations = clazz.annotations.rewrite(this::rewriteAnnotation);
       clazz.forEachMethod(this::processMethod);
@@ -254,15 +249,16 @@
   }
 
   private DexEncodedAnnotation rewriteEncodedAnnotation(DexEncodedAnnotation original) {
-    DexType annotationType = original.type.toBaseType(appInfo.dexItemFactory);
+    GraphLense graphLense = appView.graphLense();
+    DexType annotationType = original.type.toBaseType(appView.dexItemFactory());
     return original.rewrite(
-        lense::lookupType,
-        element -> rewriteAnnotationElement(lense.lookupType(annotationType), element));
+        graphLense::lookupType,
+        element -> rewriteAnnotationElement(graphLense.lookupType(annotationType), element));
   }
 
   private DexAnnotationElement rewriteAnnotationElement(
       DexType annotationType, DexAnnotationElement original) {
-    DexClass definition = appInfo.definitionFor(annotationType);
+    DexClass definition = appView.definitionFor(annotationType);
     // TODO(b/73102187): How to handle annotations without definition.
     if (definition == null) {
       return original;
@@ -277,10 +273,11 @@
   private boolean enclosingMethodPinned(DexClass clazz) {
     return clazz.getEnclosingMethod() != null
         && clazz.getEnclosingMethod().getEnclosingClass() != null
-        && appInfo.isPinned(clazz.getEnclosingMethod().getEnclosingClass());
+        && appView.appInfo().isPinned(clazz.getEnclosingMethod().getEnclosingClass());
   }
 
   private boolean innerClassPinned(DexClass clazz) {
+    AppInfoWithLiveness appInfo = appView.appInfo();
     List<InnerClassAttribute> innerClasses = clazz.getInnerClasses();
     for (InnerClassAttribute innerClass : innerClasses) {
       if (appInfo.isPinned(innerClass.getInner())) {
@@ -310,10 +307,10 @@
     // if only one side is kept - keep the attributes is any class mentioned in these attributes
     // is kept.
     boolean keptAnyway =
-        appInfo.isPinned(clazz.type)
+        appView.appInfo().isPinned(clazz.type)
             || enclosingMethodPinned(clazz)
             || innerClassPinned(clazz)
-            || options.forceProguardCompatibility;
+            || appView.options().forceProguardCompatibility;
     boolean keepForThisInnerClass = false;
     boolean keepForThisEnclosingClass = false;
     if (!keptAnyway) {
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 743e48d..823091c 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -30,24 +31,24 @@
 public class TreePruner {
 
   private final DexApplication application;
-  private final AppInfoWithLiveness appInfo;
-  private final InternalOptions options;
+  private final AppView<AppInfoWithLiveness> appView;
   private final UsagePrinter usagePrinter;
   private final Set<DexType> prunedTypes = Sets.newIdentityHashSet();
 
-  public TreePruner(
-      DexApplication application, AppInfoWithLiveness appInfo, InternalOptions options) {
+  public TreePruner(DexApplication application, AppView<AppInfoWithLiveness> appView) {
     this.application = application;
-    this.appInfo = appInfo;
-    this.options = options;
+    this.appView = appView;
+
+    ProguardConfiguration proguardConfiguration = appView.options().getProguardConfiguration();
     this.usagePrinter =
-        options.getProguardConfiguration() != null
-            && options.getProguardConfiguration().isPrintUsage()
-        ? new UsagePrinter() : UsagePrinter.DONT_PRINT;
+        proguardConfiguration != null && proguardConfiguration.isPrintUsage()
+            ? new UsagePrinter()
+            : UsagePrinter.DONT_PRINT;
   }
 
   public DexApplication run() {
     application.timing.begin("Pruning application...");
+    InternalOptions options = appView.options();
     if (options.debugKeepRules && options.enableMinification) {
       options.reporter.info(
           new StringDiagnostic(
@@ -70,6 +71,8 @@
   }
 
   private List<DexProgramClass> getNewProgramClasses(List<DexProgramClass> classes) {
+    AppInfoWithLiveness appInfo = appView.appInfo();
+    InternalOptions options = appView.options();
     List<DexProgramClass> newClasses = new ArrayList<>();
     for (DexProgramClass clazz : classes) {
       if (!appInfo.liveTypes.contains(clazz.type)) {
@@ -125,6 +128,7 @@
   }
 
   private boolean isAttributeReferencingPrunedItem(EnclosingMethodAttribute attr) {
+    AppInfoWithLiveness appInfo = appView.appInfo();
     return
         (attr.getEnclosingClass() != null
             && !appInfo.liveTypes.contains(attr.getEnclosingClass()))
@@ -133,6 +137,7 @@
   }
 
   private boolean isAttributeReferencingPrunedType(InnerClassAttribute attr) {
+    AppInfoWithLiveness appInfo = appView.appInfo();
     if (!appInfo.liveTypes.contains(attr.getInner())) {
       return true;
     }
@@ -173,6 +178,8 @@
   }
 
   private DexEncodedMethod[] reachableMethods(List<DexEncodedMethod> methods, DexClass clazz) {
+    AppInfoWithLiveness appInfo = appView.appInfo();
+    InternalOptions options = appView.options();
     int firstUnreachable = firstUnreachableIndex(methods, appInfo.liveMethods::contains);
     // Return the original array if all methods are used.
     if (firstUnreachable == -1) {
@@ -232,6 +239,7 @@
   }
 
   private DexEncodedField[] reachableFields(List<DexEncodedField> fields) {
+    AppInfoWithLiveness appInfo = appView.appInfo();
     Predicate<DexField> isReachableOrReferencedField =
         field ->
             appInfo.liveFields.contains(field)
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 e595762..29a6036 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -227,6 +227,7 @@
   }
 
   public Set<String> extensiveLoggingFilter = getExtensiveLoggingFilter();
+  public Set<String> extensiveFieldMinifierLoggingFilter = getExtensiveFieldMinifierLoggingFilter();
   public Set<String> extensiveInterfaceMethodMinifierLoggingFilter =
       getExtensiveInterfaceMethodMinifierLoggingFilter();
 
@@ -304,6 +305,19 @@
     return ImmutableSet.of();
   }
 
+  private static Set<String> getExtensiveFieldMinifierLoggingFilter() {
+    String property =
+        System.getProperty("com.android.tools.r8.extensiveFieldMinifierLoggingFilter");
+    if (property != null) {
+      ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+      for (String method : property.split(";")) {
+        builder.add(method);
+      }
+      return builder.build();
+    }
+    return ImmutableSet.of();
+  }
+
   private static Set<String> getExtensiveInterfaceMethodMinifierLoggingFilter() {
     String property =
         System.getProperty("com.android.tools.r8.extensiveInterfaceMethodMinifierLoggingFilter");
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 60dc9ae..5a71add 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -9,13 +9,15 @@
 import com.android.tools.r8.JctfTestSpecifications.Outcome;
 import com.android.tools.r8.TestCondition.Runtime;
 import com.android.tools.r8.TestCondition.RuntimeSet;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.TestRuntime.DexRuntime;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ArtErrorParser;
 import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
@@ -46,6 +48,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.function.BiFunction;
@@ -1526,19 +1529,53 @@
     runArtTest(ToolHelper.getDexVm(), compilerUnderTest);
   }
 
+  private static class CompilationOptions {
+    private final boolean disableInlining;
+    private final boolean disableClassInlining;
+    private final boolean disableUninstantiatedTypeOptimization;
+    private final boolean hasMissingClasses;
+
+    private CompilationOptions(TestSpecification spec) {
+      this.disableInlining = spec.disableInlining;
+      this.disableClassInlining = spec.disableClassInlining;
+      this.disableUninstantiatedTypeOptimization = spec.disableUninstantiatedTypeOptimization;
+      this.hasMissingClasses = spec.hasMissingClasses;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      CompilationOptions options = (CompilationOptions) o;
+      return disableInlining == options.disableInlining
+          && disableClassInlining == options.disableClassInlining
+          && disableUninstantiatedTypeOptimization == options.disableUninstantiatedTypeOptimization
+          && hasMissingClasses == options.hasMissingClasses;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(
+          disableInlining,
+          disableClassInlining,
+          disableUninstantiatedTypeOptimization,
+          hasMissingClasses);
+    }
+  }
+
   private void executeCompilerUnderTest(
       CompilerUnderTest compilerUnderTest,
       Collection<String> fileNames,
       String resultPath,
       CompilationMode compilationMode,
-      boolean disableInlining,
-      boolean disableClassInlining,
-      boolean disableUninstantiatedTypeOptimization,
-      boolean hasMissingClasses)
+      CompilationOptions compilationOptions)
       throws CompilationFailedException {
-    executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, compilationMode, null,
-        disableInlining, disableClassInlining, disableUninstantiatedTypeOptimization,
-        hasMissingClasses);
+    executeCompilerUnderTest(
+        compilerUnderTest, fileNames, resultPath, compilationMode, null, compilationOptions);
   }
 
   private void executeCompilerUnderTest(
@@ -1547,10 +1584,7 @@
       String resultPath,
       CompilationMode mode,
       String keepRulesFile,
-      boolean disableInlining,
-      boolean disableClassInlining,
-      boolean disableUninstantiatedTypeOptimization,
-      boolean hasMissingClasses)
+      CompilationOptions compilationOptions)
       throws CompilationFailedException {
     assert mode != null;
     switch (compilerUnderTest) {
@@ -1698,20 +1732,20 @@
           ToolHelper.runR8(
               builder.build(),
               options -> {
-                if (disableInlining) {
+                if (compilationOptions.disableInlining) {
                   options.enableInlining = false;
                 }
-                if (disableClassInlining) {
+                if (compilationOptions.disableClassInlining) {
                   options.enableClassInlining = false;
                 }
-                if (disableUninstantiatedTypeOptimization) {
+                if (compilationOptions.disableUninstantiatedTypeOptimization) {
                   options.enableUninstantiatedTypeOptimization = false;
                 }
                 // Make sure we don't depend on this settings.
                 options.classInliningInstructionLimit = 10000;
                 options.lineNumberOptimization = LineNumberOptimization.OFF;
                 // Some tests actually rely on missing classes for what they test.
-                options.ignoreMissingClasses = hasMissingClasses;
+                options.ignoreMissingClasses = compilationOptions.hasMissingClasses;
               });
           break;
         }
@@ -1794,15 +1828,38 @@
             noInlining);
   }
 
-  protected void runJctfTest(CompilerUnderTest compilerUnderTest, String classFilePath,
-      String fullClassName)
-      throws IOException, ProguardRuleParserException, ExecutionException,
-      CompilationFailedException {
-    Runtime runtime;
-    if (compilerUnderTest == CompilerUnderTest.R8CF) {
-      runtime = Runtime.JAVA;
+  private static Runtime getRuntime(TestRuntime vm) {
+    if (vm.isCf()) {
+      return Runtime.JAVA;
+    } else if (vm.isDex()) {
+      return Runtime.fromDexVmVersion(vm.asDex().getVm().getVersion());
     } else {
-      runtime = Runtime.fromDexVmVersion(ToolHelper.getDexVm().getVersion());
+      throw new Unreachable();
+    }
+  }
+
+  private static class VmSpec {
+    final TestRuntime vm;
+    final TestSpecification spec;
+
+    private VmSpec(TestRuntime vm, TestSpecification testSpecification) {
+      this.vm = vm;
+      this.spec = testSpecification;
+    }
+  }
+
+  protected void runJctfTest(
+      CompilerUnderTest compilerUnderTest, String classFilePath, String fullClassName)
+      throws IOException, CompilationFailedException {
+    List<TestRuntime> vms = new ArrayList<>();
+    if (compilerUnderTest == CompilerUnderTest.R8CF) {
+      for (CfVm vm : TestParametersBuilder.getAvailableCfVms()) {
+        vms.add(new TestRuntime.CfRuntime(vm));
+      }
+    } else {
+      for (DexVm vm : TestParametersBuilder.getAvailableDexVms()) {
+        vms.add(new DexRuntime(vm));
+      }
     }
 
     CompilerUnderTest firstCompilerUnderTest =
@@ -1811,19 +1868,28 @@
             : compilerUnderTest;
     CompilationMode compilationMode = defaultCompilationMode(compilerUnderTest);
 
-    File resultDir = temp.newFolder(firstCompilerUnderTest.toString().toLowerCase() + "-output");
+    List<VmSpec> vmSpecs = new ArrayList<>();
+    for (TestRuntime vm : vms) {
+      File resultDir =
+          temp.newFolder(
+              firstCompilerUnderTest.toString().toLowerCase() + "-output-" + vm.toString());
 
-    TestSpecification specification =
-        JctfTestSpecifications.getExpectedOutcome(
-            name,
-            firstCompilerUnderTest,
-            runtime,
-            compilationMode,
-            compilerUnderTest == CompilerUnderTest.R8CF
-                ? jctfOutcomeToSpecificationJava(name, resultDir)
-                : jctfOutcomeToSpecification(name, DexTool.NONE, resultDir, ToolHelper.getDexVm()));
+      TestSpecification specification =
+          JctfTestSpecifications.getExpectedOutcome(
+              name,
+              firstCompilerUnderTest,
+              getRuntime(vm),
+              compilationMode,
+              compilerUnderTest == CompilerUnderTest.R8CF
+                  ? jctfOutcomeToSpecificationJava(name, resultDir)
+                  : jctfOutcomeToSpecification(name, DexTool.NONE, resultDir, vm.asDex().getVm()));
 
-    if (specification.skipTest) {
+      if (!specification.skipTest) {
+        vmSpecs.add(new VmSpec(vm, specification));
+      }
+    }
+
+    if (vmSpecs.isEmpty()) {
       return;
     }
 
@@ -1887,62 +1953,82 @@
     }
 
     if (compilerUnderTest == CompilerUnderTest.R8CF) {
-      runJctfTestDoRunOnJava(fileNames, specification, fullClassName, compilationMode, resultDir);
-    } else {
-
-      runJctfTestDoRunOnArt(
-          fileNames,
-          specification,
-          firstCompilerUnderTest,
-          fullClassName,
-          compilationMode,
-          ToolHelper.getDexVm(),
-          resultDir);
+      assert vmSpecs.size() == 1
+          : "Running the same test on multiple JVMs should share the same build.";
+      for (VmSpec vmSpec : vmSpecs) {
+        runJctfTestDoRunOnJava(
+            fileNames, vmSpec.spec, fullClassName, compilationMode, vmSpec.vm.asCf().getVm());
+      }
+      return;
     }
 
-    // second pass if D8_R8Debug
-    if (compilerUnderTest == CompilerUnderTest.R8_AFTER_D8) {
-      List<String> d8OutputFileNames =
-          Files.list(resultDir.toPath())
-              .filter(FileUtils::isDexFile)
-              .map(Path::toString)
-              .collect(Collectors.toList());
-      File r8ResultDir = temp.newFolder("r8-output");
-      compilationMode = CompilationMode.DEBUG;
-      specification =
+    CompilationOptions compilationOptions = null;
+    File compiledDir = temp.newFolder();
+    for (VmSpec vmSpec : vmSpecs) {
+      CompilationOptions thisOptions = new CompilationOptions(vmSpec.spec);
+      if (compilationOptions == null) {
+        compilationOptions = thisOptions;
+        executeCompilerUnderTest(
+            firstCompilerUnderTest,
+            fileNames,
+            compiledDir.getAbsolutePath(),
+            compilationMode,
+            compilationOptions);
+      } else {
+        // For now compile options don't change across vms.
+        assert compilationOptions.equals(thisOptions);
+      }
+      Files.copy(
+          compiledDir.toPath().resolve("classes.dex"),
+          vmSpec.spec.directory.toPath().resolve("classes.dex"));
+      runJctfTestDoRunOnArt(fileNames, vmSpec.spec, fullClassName, vmSpec.vm.asDex().getVm());
+    }
+
+    if (compilerUnderTest != CompilerUnderTest.R8_AFTER_D8) {
+      return;
+    }
+
+    // Second pass (R8), if R8_AFTER_D8.
+    CompilationOptions r8CompilationOptions = null;
+    File r8CompiledDir = temp.newFolder();
+    for (VmSpec vmSpec : vmSpecs) {
+      File r8ResultDir = temp.newFolder("r8-output-" + vmSpec.vm.toString());
+      TestSpecification specification =
           JctfTestSpecifications.getExpectedOutcome(
               name,
               CompilerUnderTest.R8_AFTER_D8,
-              runtime,
-              compilationMode,
-              jctfOutcomeToSpecification(name, DexTool.DX, r8ResultDir, ToolHelper.getDexVm()));
+              getRuntime(vmSpec.vm),
+              CompilationMode.RELEASE,
+              jctfOutcomeToSpecification(name, DexTool.DX, r8ResultDir, vmSpec.vm.asDex().getVm()));
       if (specification.skipTest) {
-        return;
+        continue;
       }
-      runJctfTestDoRunOnArt(
-          d8OutputFileNames,
-          specification,
-          CompilerUnderTest.R8,
-          fullClassName,
-          compilationMode,
-          ToolHelper.getDexVm(),
-          r8ResultDir);
+      CompilationOptions thisOptions = new CompilationOptions(vmSpec.spec);
+      if (r8CompilationOptions == null) {
+        r8CompilationOptions = thisOptions;
+        executeCompilerUnderTest(
+            CompilerUnderTest.R8,
+            Collections.singletonList(compiledDir.toPath().resolve("classes.dex").toString()),
+            r8CompiledDir.getAbsolutePath(),
+            CompilationMode.RELEASE,
+            r8CompilationOptions);
+      } else {
+        // For now compile options don't change across vms.
+        assert r8CompilationOptions.equals(thisOptions);
+      }
+      Files.copy(
+          r8CompiledDir.toPath().resolve("classes.dex"),
+          specification.directory.toPath().resolve("classes.dex"));
+      runJctfTestDoRunOnArt(fileNames, specification, fullClassName, vmSpec.vm.asDex().getVm());
     }
   }
 
   private void runJctfTestDoRunOnArt(
       Collection<String> fileNames,
       TestSpecification specification,
-      CompilerUnderTest compilerUnderTest,
       String fullClassName,
-      CompilationMode mode,
-      DexVm dexVm,
-      File resultDir)
-      throws IOException, CompilationFailedException {
-    executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getAbsolutePath(), mode,
-        specification.disableInlining, specification.disableClassInlining,
-        specification.disableUninstantiatedTypeOptimization, specification.hasMissingClasses);
-
+      DexVm dexVm)
+      throws IOException {
     if (!ToolHelper.artSupported() && !ToolHelper.dealsWithGoldenFiles()) {
       return;
     }
@@ -1951,16 +2037,9 @@
 
     // Collect the generated dex files.
     File[] outputFiles =
-        resultDir.listFiles((File file) -> file.getName().endsWith(".dex"));
-    if (outputFiles.length == 1) {
-      // Just run Art on classes.dex.
-      processedFile = outputFiles[0];
-    } else {
-      // Run Art on JAR file with multiple dex files.
-      processedFile
-          = temp.getRoot().toPath().resolve(specification.name + ".jar").toFile();
-      buildJar(outputFiles, processedFile);
-    }
+        specification.directory.listFiles((File file) -> file.getName().endsWith(".dex"));
+    assert outputFiles.length == 1;
+    processedFile = outputFiles[0];
 
     boolean compileOnly = System.getProperty("jctf_compile_only", "0").equals("1");
     if (compileOnly || specification.skipRun) {
@@ -1976,7 +2055,7 @@
     if (dexVm.isNewerThan(DexVm.ART_4_4_4_HOST)) {
       builder.appendArtOption("-Ximage:/system/non/existent/image.art");
     }
-    for (String s : ToolHelper.getBootLibs()) {
+    for (String s : ToolHelper.getBootLibs(dexVm)) {
       builder.appendBootClassPath(new File(s).getCanonicalPath());
     }
     builder.setMainClass(JUNIT_TEST_RUNNER);
@@ -2003,20 +2082,18 @@
       TestSpecification specification,
       String fullClassName,
       CompilationMode mode,
-      File resultDir)
+      CfVm vm)
       throws IOException, CompilationFailedException {
+    assert TestParametersBuilder.isSystemJdk(vm);
     if (JctfTestSpecifications.compilationFailsWithAsmMethodTooLarge.contains(specification.name)) {
       expectException(org.objectweb.asm.MethodTooLargeException.class);
     }
     executeCompilerUnderTest(
         CompilerUnderTest.R8CF,
         fileNames,
-        resultDir.getAbsolutePath(),
+        specification.directory.getAbsolutePath(),
         mode,
-        specification.disableInlining,
-        specification.disableClassInlining,
-        specification.disableUninstantiatedTypeOptimization,
-        specification.hasMissingClasses);
+        new CompilationOptions(specification));
 
     boolean compileOnly = System.getProperty("jctf_compile_only", "0").equals("1");
 
@@ -2033,7 +2110,7 @@
     // running the test.
     ProcessResult result =
         ToolHelper.runJava(
-            resultDir.toPath(),
+            specification.directory.toPath(),
             "-Xmx" + ToolHelper.BOT_MAX_HEAP_SIZE,
             JUNIT_TEST_RUNNER,
             fullClassName);
@@ -2168,9 +2245,11 @@
       expectException(CompilationError.class);
       try {
         executeCompilerUnderTest(
-            compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
-            specification.disableInlining, specification.disableClassInlining,
-            specification.disableUninstantiatedTypeOptimization, specification.hasMissingClasses);
+            compilerUnderTest,
+            fileNames,
+            resultDir.getCanonicalPath(),
+            compilationMode,
+            new CompilationOptions(specification));
       } catch (CompilationFailedException e) {
         throw new CompilationError(e.getMessage(), e);
       }
@@ -2179,16 +2258,20 @@
     } else if (specification.failsWithX8) {
       expectException(Throwable.class);
       executeCompilerUnderTest(
-          compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
-          specification.disableInlining, specification.disableClassInlining,
-          specification.disableUninstantiatedTypeOptimization, specification.hasMissingClasses);
+          compilerUnderTest,
+          fileNames,
+          resultDir.getCanonicalPath(),
+          compilationMode,
+          new CompilationOptions(specification));
       System.err.println("Should have failed R8/D8 compilation with an exception.");
       return;
     } else {
       executeCompilerUnderTest(
-          compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
-          specification.disableInlining, specification.disableClassInlining,
-          specification.disableUninstantiatedTypeOptimization, specification.hasMissingClasses);
+          compilerUnderTest,
+          fileNames,
+          resultDir.getCanonicalPath(),
+          compilationMode,
+          new CompilationOptions(specification));
     }
 
     if (!specification.skipRun
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 8059f21..4cefe0c 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -158,7 +158,7 @@
     return isSystemJdk(vm);
   }
 
-  private static List<CfVm> getAvailableCfVms() {
+  public static List<CfVm> getAvailableCfVms() {
     String cfVmsProperty = System.getProperty("cf_vms");
     if (cfVmsProperty != null) {
       return Arrays.stream(cfVmsProperty.split(":"))
@@ -174,7 +174,7 @@
     }
   }
 
-  private static List<DexVm> getAvailableDexVms() {
+  public static List<DexVm> getAvailableDexVms() {
     String dexVmsProperty = System.getProperty("dex_vms");
     if (dexVmsProperty != null) {
       return Arrays.stream(dexVmsProperty.split(":"))
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 7467bfa..1ab645d 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -704,10 +704,10 @@
     }
   }
 
-  public static List<String> getBootLibs() {
-    String prefix = getArtDir(getDexVm()) + "/";
+  public static List<String> getBootLibs(DexVm dexVm) {
+    String prefix = getArtDir(dexVm) + "/";
     List<String> result = new ArrayList<>();
-    BOOT_LIBS.get(getDexVm()).stream().forEach(x -> result.add(prefix + "framework/" + x));
+    BOOT_LIBS.get(dexVm).stream().forEach(x -> result.add(prefix + "framework/" + x));
     return result;
   }
 
diff --git a/src/test/java/com/android/tools/r8/naming/b128656974/B128656974.java b/src/test/java/com/android/tools/r8/naming/b128656974/B128656974.java
new file mode 100644
index 0000000..100b359
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b128656974/B128656974.java
@@ -0,0 +1,129 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming.b128656974;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.naming.testclasses.Greeting;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class B128656974 extends TestBase {
+
+  @Ignore("b/128656974")
+  @Test
+  public void testField() throws Exception {
+    Class<?> main = TestClassMainForField.class;
+    testForR8(Backend.DEX)
+        .addProgramClasses(
+            Greeting.class, Greeting.getGreetingBase(), TestClassSub.class, main)
+        .enableClassInliningAnnotations()
+        .enableInliningAnnotations()
+        .enableMergeAnnotations()
+        .addKeepMainRule(main)
+        .addKeepRules(
+            "-keepclassmembernames class "
+                + TestClassSub.class.getTypeName()
+                + "{ static java.lang.String a; }")
+        .run(main)
+        .assertSuccessWithOutput(StringUtils.lines("TestClassSub.greeting", "TestClassSub.a"))
+        .inspect(inspector -> {
+          ClassSubject greetingBase = inspector.clazz(Greeting.getGreetingBase());
+          assertThat(greetingBase, isPresent());
+          FieldSubject greeting = greetingBase.uniqueFieldWithName("greeting");
+          assertThat(greeting, isPresent());
+          assertThat(greeting, isRenamed());
+          assertNotEquals("a", greeting.getFinalName());
+        });
+  }
+
+  @NeverClassInline
+  static class TestClassSub extends Greeting {
+    // Since this name is kept, renaming of Greeting.greeting should avoid `a`.
+    // Otherwise, we'll see Out-of-order field_ids due to the duplicate fields.
+    static String a;
+
+    TestClassSub() {
+      greeting = "TestClassSub.greeting";
+      a = "TestClassSub.a";
+    }
+
+    @Override
+    public String toString(){
+      return greeting + System.lineSeparator() + a;
+    }
+  }
+
+  static class TestClassMainForField {
+    public static void main(String[] args) {
+      TestClassSub instance = new TestClassSub();
+      System.out.println(instance.toString());
+    }
+  }
+
+  @Test
+  public void testMethod() throws Exception {
+    Class<?> main = TestClassMainForMethod.class;
+    testForR8(Backend.DEX)
+        .addProgramClasses(
+            TestClassBase.class, TestClassSub2.class, main)
+        .enableMergeAnnotations()
+        .enableClassInliningAnnotations()
+        .enableInliningAnnotations()
+        .addKeepMainRule(main)
+        .addKeepRules(
+            "-keepclassmembernames class "
+                + TestClassSub2.class.getTypeName()
+                + "{ void a(...); }")
+        .run(main)
+        .assertSuccessWithOutput(StringUtils.lines("TestClassSub2::a", "TestClassBase::foo"))
+        .inspect(inspector -> {
+          ClassSubject base = inspector.clazz(TestClassBase.class);
+          assertThat(base, isPresent());
+          MethodSubject foo = base.uniqueMethodWithName("foo");
+          assertThat(foo, isPresent());
+          assertThat(foo, isRenamed());
+          assertNotEquals("a", foo.getFinalName());
+        });
+  }
+
+  @NeverMerge
+  static class TestClassBase {
+    @NeverInline
+    void foo() {
+      System.out.println("TestClassBase::foo");
+    }
+  }
+
+  @NeverClassInline
+  static class TestClassSub2 extends TestClassBase {
+    // Since this name is kept, renaming of TestClassBase#foo should avoid `a`.
+    // Otherwise, we'll see Out-of-order method_ids due to the duplicate methods.
+    @NeverInline
+    void a() {
+      System.out.println("TestClassSub2::a");
+    }
+  }
+
+  static class TestClassMainForMethod {
+    public static void main(String[] args) {
+      TestClassSub2 instance = new TestClassSub2();
+      instance.a();
+      instance.foo();
+    }
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/proguard/configuration/InheritanceClauseWithDisjunctionTest.java b/src/test/java/com/android/tools/r8/proguard/configuration/InheritanceClauseWithDisjunctionTest.java
new file mode 100644
index 0000000..6a26c3b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/proguard/configuration/InheritanceClauseWithDisjunctionTest.java
@@ -0,0 +1,275 @@
+// 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.proguard.configuration;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.function.Consumer;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class InheritanceClauseWithDisjunctionTest extends TestBase {
+
+  @Ignore("b/128503974")
+  @Test
+  public void testExtendsClauseWithR8() throws Exception {
+    runTest(
+        testForR8(Backend.DEX),
+        getKeepRulesForExtendsClauseTests(),
+        InheritanceClauseWithDisjunctionTest::inspectExtendsClauseTests);
+  }
+
+  @Test
+  public void testExtendsClauseWithProguard() throws Exception {
+    runTest(
+        testForProguard(),
+        getKeepRulesForExtendsClauseTests(),
+        InheritanceClauseWithDisjunctionTest::inspectExtendsClauseTests);
+  }
+
+  private static List<String> getKeepRulesForExtendsClauseTests() {
+    return ImmutableList.of(
+        "-keep class * extends "
+            + InheritanceClauseWithDisjunctionTestClassA.class.getTypeName()
+            + ", "
+            + InheritanceClauseWithDisjunctionTestClassB.class.getTypeName()
+            + ", "
+            + InheritanceClauseWithDisjunctionTestClassC.class.getTypeName());
+  }
+
+  private static void inspectExtendsClauseTests(CodeInspector inspector) {
+    // ASub extends A and BSub extends B. A and B are kept because Proguard presumably does not
+    // merge them into ASub and BSub, respectively.
+    assertEquals(4, inspector.allClasses().size());
+    assertThat(inspector.clazz(InheritanceClauseWithDisjunctionTestClassA.class), isPresent());
+    assertThat(inspector.clazz(InheritanceClauseWithDisjunctionTestClassASub.class), isPresent());
+    assertThat(inspector.clazz(InheritanceClauseWithDisjunctionTestClassB.class), isPresent());
+    assertThat(inspector.clazz(InheritanceClauseWithDisjunctionTestClassBSub.class), isPresent());
+  }
+
+  @Ignore("b/128503974")
+  @Test
+  public void testExtendsClauseWithNegation1WithR8() throws Exception {
+    runTest(
+        testForR8(Backend.DEX),
+        getKeepRulesForExtendsClauseWithNegation1Tests(),
+        InheritanceClauseWithDisjunctionTest::inspectExtendsClauseWithNegation1Tests);
+  }
+
+  @Test
+  public void testExtendsClauseWithNegation1WithProguard() throws Exception {
+    runTest(
+        testForProguard(),
+        getKeepRulesForExtendsClauseWithNegation1Tests(),
+        InheritanceClauseWithDisjunctionTest::inspectExtendsClauseWithNegation1Tests);
+  }
+
+  private static List<String> getKeepRulesForExtendsClauseWithNegation1Tests() {
+    return ImmutableList.of(
+        "-keep class * extends "
+            + InheritanceClauseWithDisjunctionTestClassA.class.getTypeName()
+            + ", !"
+            + InheritanceClauseWithDisjunctionTestClassB.class.getTypeName());
+  }
+
+  private static void inspectExtendsClauseWithNegation1Tests(CodeInspector inspector) {
+    // Strangely, BSub is kept, although it does not extend A and it extends B.
+    assertEquals(11, inspector.allClasses().size());
+  }
+
+  @Ignore("b/128503974")
+  @Test
+  public void testExtendsClauseWithNegation2WithR8() throws Exception {
+    runTest(
+        testForR8(Backend.DEX),
+        getKeepRulesForExtendsClauseWithNegation2Tests(),
+        InheritanceClauseWithDisjunctionTest::inspectExtendsClauseWithNegation2Tests);
+  }
+
+  @Test
+  public void testExtendsClauseWithNegation2WithProguard() throws Exception {
+    runTest(
+        testForProguard(),
+        getKeepRulesForExtendsClauseWithNegation2Tests(),
+        InheritanceClauseWithDisjunctionTest::inspectExtendsClauseWithNegation2Tests);
+  }
+
+  private static List<String> getKeepRulesForExtendsClauseWithNegation2Tests() {
+    return ImmutableList.of(
+        "-keep class * extends !"
+            + InheritanceClauseWithDisjunctionTestClassB.class.getTypeName()
+            + ", "
+            + InheritanceClauseWithDisjunctionTestClassA.class.getTypeName());
+  }
+
+  private static void inspectExtendsClauseWithNegation2Tests(CodeInspector inspector) {
+    // Strangely, all the types that do not extend A have not kept.
+    assertEquals(2, inspector.allClasses().size());
+    assertThat(inspector.clazz(InheritanceClauseWithDisjunctionTestClassA.class), isPresent());
+    assertThat(inspector.clazz(InheritanceClauseWithDisjunctionTestClassASub.class), isPresent());
+  }
+
+  @Ignore("b/128503974")
+  @Test
+  public void testExtendsClauseWithMatchAllNegationWithR8() throws Exception {
+    runTest(
+        testForR8(Backend.DEX),
+        getKeepRulesForExtendsClauseWithMatchAllNegationTests(),
+        InheritanceClauseWithDisjunctionTest::inspectExtendsClauseWithMatchAllNegationTests);
+  }
+
+  @Test
+  public void testExtendsClauseWithMatchAllNegationWithProguard() throws Exception {
+    runTest(
+        testForProguard(),
+        getKeepRulesForExtendsClauseWithMatchAllNegationTests(),
+        InheritanceClauseWithDisjunctionTest::inspectExtendsClauseWithMatchAllNegationTests);
+  }
+
+  private static List<String> getKeepRulesForExtendsClauseWithMatchAllNegationTests() {
+    return ImmutableList.of(
+        "-keep class * extends "
+            + InheritanceClauseWithDisjunctionTestClassA.class.getTypeName()
+            + ", !"
+            + InheritanceClauseWithDisjunctionTestClassA.class.getTypeName());
+  }
+
+  private static void inspectExtendsClauseWithMatchAllNegationTests(CodeInspector inspector) {
+    // Every type extends A or does not extend A.
+    assertEquals(11, inspector.allClasses().size());
+  }
+
+  @Ignore("b/128503974")
+  @Test
+  public void testImplementsClauseWithR8() throws Exception {
+    runTest(
+        testForR8(Backend.DEX),
+        getKeepRulesForImplementsClauseTests(),
+        InheritanceClauseWithDisjunctionTest::inspectImplementsClauseTests);
+  }
+
+  @Test
+  public void testImplementsClauseWithProguard() throws Exception {
+    try {
+      runTest(
+          testForProguard(),
+          getKeepRulesForImplementsClauseTests(),
+          InheritanceClauseWithDisjunctionTest::inspectImplementsClauseTests);
+      fail();
+    } catch (CompilationFailedException e) {
+      // Strangely, nothing matches implements I or J (not even InheritanceClauseWithDisjunction-
+      // TestClassIJSub, which implements both I and J!).
+      assertThat(e.getMessage(), containsString("The output jar is empty"));
+    }
+  }
+
+  private static List<String> getKeepRulesForImplementsClauseTests() {
+    return ImmutableList.of(
+        "-keep class * implements "
+            + InheritanceClauseWithDisjunctionTestInterfaceI.class.getTypeName()
+            + ", "
+            + InheritanceClauseWithDisjunctionTestInterfaceJ.class.getTypeName()
+            + ", "
+            + InheritanceClauseWithDisjunctionTestInterfaceK.class.getTypeName());
+  }
+
+  private static void inspectImplementsClauseTests(CodeInspector inspector) {
+    // Strangely, nothing matches implements I or J (not even InheritanceClauseWithDisjunctionTest-
+    // ClassIJSub, which implements both I and J!).
+    assertEquals(0, inspector.allClasses().size());
+  }
+
+  @Ignore("b/128503974")
+  @Test
+  public void testImplementsClauseWithNegationWithR8() throws Exception {
+    runTest(
+        testForR8(Backend.DEX),
+        getKeepRulesForImplementsClauseTests(),
+        InheritanceClauseWithDisjunctionTest::inspectImplementsClauseWithNegationTests);
+  }
+
+  @Test
+  public void testImplementsClauseWithNegationWithProguard() throws Exception {
+    runTest(
+        testForProguard(),
+        getKeepRulesForImplementsClauseWithNegationTests(),
+        InheritanceClauseWithDisjunctionTest::inspectImplementsClauseWithNegationTests);
+  }
+
+  private static List<String> getKeepRulesForImplementsClauseWithNegationTests() {
+    return ImmutableList.of(
+        "-keep class * implements "
+            + InheritanceClauseWithDisjunctionTestInterfaceI.class.getTypeName()
+            + ", !"
+            + InheritanceClauseWithDisjunctionTestInterfaceI.class.getTypeName());
+  }
+
+  private static void inspectImplementsClauseWithNegationTests(CodeInspector inspector) {
+    // Every type implements I or does not implement I.
+    assertEquals(11, inspector.allClasses().size());
+  }
+
+  private static void runTest(
+      TestShrinkerBuilder<?, ?, ?, ?, ?> builder,
+      List<String> keepRules,
+      Consumer<CodeInspector> consumer)
+      throws Exception {
+    builder
+        .addProgramClasses(
+            InheritanceClauseWithDisjunctionTestClassA.class,
+            InheritanceClauseWithDisjunctionTestClassASub.class,
+            InheritanceClauseWithDisjunctionTestClassB.class,
+            InheritanceClauseWithDisjunctionTestClassBSub.class,
+            InheritanceClauseWithDisjunctionTestClassC.class,
+            InheritanceClauseWithDisjunctionTestInterfaceI.class,
+            InheritanceClauseWithDisjunctionTestClassISub.class,
+            InheritanceClauseWithDisjunctionTestInterfaceJ.class,
+            InheritanceClauseWithDisjunctionTestClassJSub.class,
+            InheritanceClauseWithDisjunctionTestInterfaceK.class,
+            InheritanceClauseWithDisjunctionTestClassIJKSub.class)
+        .addKeepRules(keepRules)
+        .compile()
+        .inspect(consumer);
+  }
+}
+
+class InheritanceClauseWithDisjunctionTestClassA {}
+
+class InheritanceClauseWithDisjunctionTestClassASub
+    extends InheritanceClauseWithDisjunctionTestClassA {}
+
+class InheritanceClauseWithDisjunctionTestClassB {}
+
+class InheritanceClauseWithDisjunctionTestClassBSub
+    extends InheritanceClauseWithDisjunctionTestClassB {}
+
+class InheritanceClauseWithDisjunctionTestClassC {}
+
+interface InheritanceClauseWithDisjunctionTestInterfaceI {}
+
+class InheritanceClauseWithDisjunctionTestClassISub
+    implements InheritanceClauseWithDisjunctionTestInterfaceI {}
+
+interface InheritanceClauseWithDisjunctionTestInterfaceJ {}
+
+class InheritanceClauseWithDisjunctionTestClassJSub
+    implements InheritanceClauseWithDisjunctionTestInterfaceJ {}
+
+interface InheritanceClauseWithDisjunctionTestInterfaceK {}
+
+class InheritanceClauseWithDisjunctionTestClassIJKSub
+    implements InheritanceClauseWithDisjunctionTestInterfaceI,
+        InheritanceClauseWithDisjunctionTestInterfaceJ,
+        InheritanceClauseWithDisjunctionTestInterfaceK {}
diff --git a/tools/archive.py b/tools/archive.py
index 687844a..25e6a4f 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -91,6 +91,10 @@
 def GetMavenUrl(is_master):
   return GetVersionDestination('http://storage.googleapis.com/', '', is_master)
 
+def SetRLimitToMax():
+  (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
+  resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard))
+
 def PrintResourceInfo():
   (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
   print('INFO: Open files soft limit: %s' % soft)
@@ -101,10 +105,8 @@
   if not utils.is_bot() and not options.dry_run:
     raise Exception('You are not a bot, don\'t archive builds')
 
-  if utils.is_old_bot():
-    print("Archiving is disabled on old bots.")
-    return
-
+  if utils.is_bot():
+    SetRLimitToMax()
   PrintResourceInfo()
   # Create maven release which uses a build that exclude dependencies.
   create_maven_release.main(["--out", utils.LIBS])
diff --git a/tools/create_jctf_tests.py b/tools/create_jctf_tests.py
index 28dde99..bc065d9 100755
--- a/tools/create_jctf_tests.py
+++ b/tools/create_jctf_tests.py
@@ -129,7 +129,6 @@
     relative_package = package[idx + len(dot_java_dot):]
 
     generate_test(class_name, 'd8', 'R8_AFTER_D8', relative_package)
-    generate_test(class_name, 'r8', 'R8', relative_package)
     generate_test(class_name, 'r8cf', 'R8CF', relative_package)
 
 
diff --git a/tools/internal_test.py b/tools/internal_test.py
index 032f1fe..a9a90eb 100755
--- a/tools/internal_test.py
+++ b/tools/internal_test.py
@@ -193,9 +193,6 @@
         print('\n\n%s had value:\n%s' % (to_print, value))
 
 def run_bot():
-  if utils.is_old_bot():
-    print('Not running on on old bot, please see: https://ci.chromium.org/p/r8')
-    return
   print_magic_file_state()
   # Ensure that there is nothing currently scheduled (broken/stopped run)
   for magic in ALL_MAGIC:
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index cc21bb3..6ab3546 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -584,7 +584,8 @@
 
   env_vars = {}
   env_vars['ANDROID_HOME'] = utils.getAndroidHome()
-  env_vars['JAVA_OPTS'] = '-ea:com.android.tools.r8...'
+  if not options.disable_assertions:
+    env_vars['JAVA_OPTS'] = '-ea:com.android.tools.r8...'
 
   releaseTarget = app.releaseTarget
   if not releaseTarget:
@@ -760,9 +761,16 @@
   # is 'r8'.
   entry_point = 'com.android.tools.r8.R8'
 
-  cmd = [jdk.GetJavaExecutable(), '-ea:com.android.tools.r8...', '-cp', r8_jar,
-      entry_point, '--release', '--min-api', str(min_sdk), '--pg-conf',
-      proguard_config_file, '--lib', android_jar, '--output', zip_dest, apk]
+  cmd = ([jdk.GetJavaExecutable()] +
+         (['-ea:com.android.tools.r8...']
+          if not options.disable_assertions
+          else []) +
+         ['-cp', r8_jar, entry_point,
+         '--release', '--min-api', str(min_sdk),
+         '--pg-conf', proguard_config_file,
+         '--lib', android_jar,
+         '--output', zip_dest,
+         apk])
 
   for android_optional_jar in utils.get_android_optional_jars(compile_sdk):
     cmd.append('--lib')
@@ -925,6 +933,10 @@
   result.add_option('--app',
                     help='What app to run on',
                     choices=GetAllAppNames())
+  result.add_option('--disable-assertions',
+                    help='Disable assertions when compiling',
+                    default=False,
+                    action='store_true')
   result.add_option('--download-only', '--download_only',
                     help='Whether to download apps without any compilation',
                     default=False,
diff --git a/tools/test.py b/tools/test.py
index 06aefca..6f664eb 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -141,9 +141,13 @@
 def Main():
   (options, args) = ParseOptions()
   if utils.is_bot():
-    gradle.RunGradle(['clean'])
+    gradle.RunGradle(['--no-daemon', 'clean'])
 
   gradle_args = ['--stacktrace']
+  if utils.is_bot():
+    # Bots don't like dangling processes
+    gradle_args.append('--no-daemon')
+
   # Set all necessary Gradle properties and options first.
   if options.shard_count:
     assert options.shard_number
@@ -253,12 +257,13 @@
 
   # Now run tests on selected runtime(s).
   vms_to_test = [options.dex_vm] if options.dex_vm != "all" else ALL_ART_VMS
-  # TODO(126683699): remove once we have single run
-  if options.dex_vm == 'all' and options.only_jctf:
-    vms_to_test = ["default",  "5.1.1"]
 
   # 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 ""
     return_code = gradle.RunGradle(
diff --git a/tools/utils.py b/tools/utils.py
index ec830f2..27df3b5 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -504,14 +504,8 @@
       android_optional_jar for android_optional_jar in android_optional_jars
       if os.path.isfile(android_optional_jar)]
 
-def is_new_bot():
-  return 'SWARMING_BOT_ID' in os.environ
-
-def is_old_bot():
-  return 'BUILDBOT_SLAVENAME' in os.environ
-
 def is_bot():
-  return 'USER' in os.environ and os.environ['USER'] == 'chrome-bot'
+  return 'SWARMING_BOT_ID' in os.environ
 
 def uncompressed_size(path):
   return sum(z.file_size for z in zipfile.ZipFile(path).infolist())