Merge "Add the output tar file as an input dependency to downloads."
diff --git a/.gitignore b/.gitignore
index cb3eaab..22b29da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,8 @@
 tools/*/art-7.0.0.tar.gz
 tools/*/art-8.1.0
 tools/*/art-8.1.0.tar.gz
+tools/*/art-9.0.0
+tools/*/art-9.0.0.tar.gz
 tools/*/dalvik
 tools/*/dalvik.tar.gz
 tools/*/dalvik-4.0.4
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
new file mode 100644
index 0000000..20faecf
--- /dev/null
+++ b/PRESUBMIT.py
@@ -0,0 +1,15 @@
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+def CheckDoNotMerge(input_api, output_api):
+  for l in input_api.change.FullDescriptionText().splitlines():
+    if l.lower().startswith('do not merge'):
+      msg = 'Your cl contains: \'Do not merge\' - this will break WIP bots'
+      return [output_api.PresubmitPromptWarning(msg, [])]
+  return []
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results.extend(CheckDoNotMerge(input_api, output_api))
+  return results
diff --git a/build.gradle b/build.gradle
index b1114b4..0a4f374 100644
--- a/build.gradle
+++ b/build.gradle
@@ -297,6 +297,7 @@
                 "linux/art-6.0.1",
                 "linux/art-7.0.0",
                 "linux/art-8.1.0",
+                "linux/art-9.0.0",
                 "linux/dalvik",
                 "linux/dalvik-4.0.4",
                 "${osString}/dx",
diff --git a/scripts/update-host-art.sh b/scripts/update-host-art.sh
index 8bfc753..802830d 100755
--- a/scripts/update-host-art.sh
+++ b/scripts/update-host-art.sh
@@ -130,6 +130,19 @@
 mkdir -p $DEST/usr/icu
 cp -r $ANDROID_HOST_BUILD/usr/icu/* $DEST/usr/icu
 
+# Update links for vdex files for Android P and later.
+if [ -f $DEST/product/$ANDROID_PRODUCT/system/framework/boot.vdex ]; then
+  for VDEXFILE in $DEST/product/$ANDROID_PRODUCT/system/framework/*.vdex; do
+    VDEXNAME=$(basename ${VDEXFILE});
+    for ARCH in arm arm64; do
+      rm $DEST/product/$ANDROID_PRODUCT/system/framework/$ARCH/${VDEXNAME};
+      # This relative link command will create a symbolic link of the form
+      # ../${VDEXNAME} for each architecture.
+      ln -r -s $DEST/product/$ANDROID_PRODUCT/system/framework/${VDEXNAME} $DEST/product/$ANDROID_PRODUCT/system/framework/$ARCH/${VDEXNAME};
+    done
+  done
+fi
+
 # Allow failure for strip commands below.
 set +e
 
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index e235e67..118df2e 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -160,7 +160,7 @@
     public void accept(
         int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
       super.accept(fileIndex, data, descriptors, handler);
-      outputBuilder.addIndexedClassFile(fileIndex, getDexFileName(fileIndex), data, handler);
+      outputBuilder.addFile(getDexFileName(fileIndex), data, handler);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 6d00ebb..7f77c6b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
@@ -527,6 +528,8 @@
         return;
       }
 
+      assert application.classes().stream().allMatch(DexClass::isValid);
+
       // Generate the resulting application resources.
       writeApplication(
           executorService,
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index d79276e..5a4b204 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -524,4 +524,9 @@
     return getKotlinInfo() != null;
   }
 
+  public boolean isValid() {
+    assert !isInterface()
+        || Arrays.stream(virtualMethods()).noneMatch(method -> method.accessFlags.isFinal());
+    return true;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 952038a..e9966f5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -68,7 +68,7 @@
   // ReferenceTypeLattice canonicalization.
   private final ConcurrentHashMap<DexType, ReferenceTypeLatticeElement>
       referenceTypeLatticeElements = new ConcurrentHashMap<>();
-  public static final LRUCacheTable<Set<DexType>, Set<DexType>, Set<DexType>>
+  public final LRUCacheTable<Set<DexType>, Set<DexType>, Set<DexType>>
       leastUpperBoundOfInterfacesTable = LRUCacheTable.create(8, 8);
 
   boolean sorted = false;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 38842da..20c04ab 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -56,6 +56,7 @@
 import com.android.tools.r8.ir.optimize.Outliner;
 import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
 import com.android.tools.r8.ir.optimize.RedundantFieldLoadElimination;
+import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
 import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
 import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
@@ -904,14 +905,16 @@
 
     if (appInfo.hasLiveness()) {
       // Reflection optimization 1. getClass() -> const-class
-      codeRewriter.rewriteGetClass(code);
+      ReflectionOptimizer.rewriteGetClass(appInfo.withLiveness(), code);
     }
 
     if (!isDebugMode) {
       // TODO(jsjeon): Consider merging these into one single optimize().
       stringOptimizer.computeTrivialOperationsOnConstString(code, appInfo.dexItemFactory);
       // Reflection optimization 2. get*Name() with const-class -> const-string
-      stringOptimizer.rewriteClassGetName(code, appInfo);
+      if (options.enableNameReflectionOptimization) {
+        stringOptimizer.rewriteClassGetName(code, appInfo);
+      }
       // Reflection optimization 3. String#valueOf(const-string) -> no op.
       stringOptimizer.removeTrivialConversions(code, appInfo);
       assert code.isConsistentSSA();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 785041c..0b54a4d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -4,6 +4,11 @@
 
 package com.android.tools.r8.ir.optimize;
 
+import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption.CANONICAL_NAME;
+import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption.NAME;
+import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption.SIMPLE_NAME;
+import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.computeClassName;
+
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
@@ -47,7 +52,6 @@
 import com.android.tools.r8.ir.code.CheckCast;
 import com.android.tools.r8.ir.code.Cmp;
 import com.android.tools.r8.ir.code.Cmp.Bias;
-import com.android.tools.r8.ir.code.ConstClass;
 import com.android.tools.r8.ir.code.ConstInstruction;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.ConstString;
@@ -1811,16 +1815,25 @@
               assert false;
             }
           } else {
+            // TODO(b/120280603): Consider minification!
             InvokeVirtual invoke = inValue.definition.asInvokeVirtual();
-            String name = method.method.getHolder().toSourceString();
-            if (invoke.getInvokedMethod() == dexItemFactory.classMethods.getSimpleName) {
-              String simpleName = name.substring(name.lastIndexOf('.') + 1);
-              encodedField.setStaticValue(
-                  new DexValueString(dexItemFactory.createString(simpleName)));
-            } else {
-              assert invoke.getInvokedMethod() == dexItemFactory.classMethods.getName;
-              encodedField.setStaticValue(new DexValueString(dexItemFactory.createString(name)));
+            DexMethod invokedMethod = invoke.getInvokedMethod();
+            DexType holderType = method.method.getHolder();
+            DexClass holder = appInfo.definitionFor(holderType);
+            assert holder != null;
+            String descriptor = holderType.toDescriptorString();
+            String name = null;
+            if (invokedMethod == appInfo.dexItemFactory.classMethods.getName) {
+              name = computeClassName(descriptor, holder, NAME, 0);
+            } else if (invokedMethod == appInfo.dexItemFactory.classMethods.getTypeName) {
+              // TODO(b/119426668): desugar Type#getTypeName
+            } else if (invokedMethod == appInfo.dexItemFactory.classMethods.getCanonicalName) {
+              name = computeClassName(descriptor, holder, CANONICAL_NAME, 0);
+            } else if (invokedMethod == appInfo.dexItemFactory.classMethods.getSimpleName) {
+              name = computeClassName(descriptor, holder, SIMPLE_NAME, 0);
             }
+            assert name != null;
+            encodedField.setStaticValue(new DexValueString(dexItemFactory.createString(name)));
           }
         } else if (field.type.isClassType() || field.type.isArrayType()) {
           if (inValue.isZero()) {
@@ -1948,67 +1961,6 @@
     return converter.definitionFor(type);
   }
 
-  // Rewrite getClass() call to const-class if the type of the given instance is effectively final.
-  public void rewriteGetClass(IRCode code) {
-    InstructionIterator it = code.instructionIterator();
-    while (it.hasNext()) {
-      Instruction current = it.next();
-      // Conservatively bail out if the containing block has catch handlers.
-      // TODO(b/118509730): unless join of all catch types is ClassNotFoundException ?
-      if (current.getBlock().hasCatchHandlers()) {
-        continue;
-      }
-      if (!current.isInvokeVirtual()) {
-        continue;
-      }
-      InvokeVirtual invoke = current.asInvokeVirtual();
-      DexMethod invokedMethod = invoke.getInvokedMethod();
-      // Class<?> Object#getClass() is final and cannot be overridden.
-      if (invokedMethod != appInfo.dexItemFactory.objectMethods.getClass) {
-        continue;
-      }
-      Value in = invoke.getReceiver();
-      if (in.hasLocalInfo()) {
-        continue;
-      }
-      TypeLatticeElement inType = in.getTypeLattice();
-      // Check the receiver is either class type or array type. Also make sure it is not nullable.
-      if (!(inType.isClassType() || inType.isArrayType())
-          || inType.isNullable()) {
-        continue;
-      }
-      DexType type = inType.isClassType()
-          ? inType.asClassTypeLatticeElement().getClassType()
-          : inType.asArrayTypeLatticeElement().getArrayType(appInfo.dexItemFactory);
-      DexType baseType = type.toBaseType(appInfo.dexItemFactory);
-      // Make sure base type is a class type.
-      if (!baseType.isClassType()) {
-        continue;
-      }
-      // Only consider program class, e.g., platform can introduce sub types in different versions.
-      DexClass clazz = appInfo.definitionFor(baseType);
-      if (clazz == null || !clazz.isProgramClass()) {
-        continue;
-      }
-      // Only consider effectively final class. Exception: new Base().getClass().
-      if (!baseType.hasSubtypes()
-          || !appInfo.withLiveness().isInstantiatedIndirectly(baseType)
-          || (!in.isPhi() && in.definition.isCreatingInstanceOrArray())) {
-        // Make sure the target (base) type is visible.
-        ConstraintWithTarget constraints =
-            ConstraintWithTarget.classIsVisible(code.method.method.getHolder(), baseType, appInfo);
-        if (constraints == ConstraintWithTarget.NEVER) {
-          continue;
-        }
-        TypeLatticeElement typeLattice = TypeLatticeElement.classClassType(appInfo);
-        Value value = code.createValue(typeLattice, invoke.getLocalInfo());
-        ConstClass constClass = new ConstClass(value, type);
-        it.replaceCurrentInstruction(constClass);
-      }
-    }
-    assert code.isConsistentSSA();
-  }
-
   public void removeTrivialCheckCastAndInstanceOfInstructions(
       IRCode code, boolean enableWholeProgramOptimizations) {
     if (!enableWholeProgramOptimizations) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index ecd3a7a..496e113 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -9,8 +9,19 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.ConstClass;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.google.common.base.Strings;
 
 public class ReflectionOptimizer {
@@ -57,6 +68,67 @@
     }
   }
 
+  // Rewrite getClass() call to const-class if the type of the given instance is effectively final.
+  public static void rewriteGetClass(AppInfoWithLiveness appInfo, IRCode code) {
+    InstructionIterator it = code.instructionIterator();
+    while (it.hasNext()) {
+      Instruction current = it.next();
+      // Conservatively bail out if the containing block has catch handlers.
+      // TODO(b/118509730): unless join of all catch types is ClassNotFoundException ?
+      if (current.getBlock().hasCatchHandlers()) {
+        continue;
+      }
+      if (!current.isInvokeVirtual()) {
+        continue;
+      }
+      InvokeVirtual invoke = current.asInvokeVirtual();
+      DexMethod invokedMethod = invoke.getInvokedMethod();
+      // Class<?> Object#getClass() is final and cannot be overridden.
+      if (invokedMethod != appInfo.dexItemFactory.objectMethods.getClass) {
+        continue;
+      }
+      Value in = invoke.getReceiver();
+      if (in.hasLocalInfo()) {
+        continue;
+      }
+      TypeLatticeElement inType = in.getTypeLattice();
+      // Check the receiver is either class type or array type. Also make sure it is not nullable.
+      if (!(inType.isClassType() || inType.isArrayType())
+          || inType.isNullable()) {
+        continue;
+      }
+      DexType type = inType.isClassType()
+          ? inType.asClassTypeLatticeElement().getClassType()
+          : inType.asArrayTypeLatticeElement().getArrayType(appInfo.dexItemFactory);
+      DexType baseType = type.toBaseType(appInfo.dexItemFactory);
+      // Make sure base type is a class type.
+      if (!baseType.isClassType()) {
+        continue;
+      }
+      // Only consider program class, e.g., platform can introduce sub types in different versions.
+      DexClass clazz = appInfo.definitionFor(baseType);
+      if (clazz == null || !clazz.isProgramClass()) {
+        continue;
+      }
+      // Only consider effectively final class. Exception: new Base().getClass().
+      if (!baseType.hasSubtypes()
+          || !appInfo.isInstantiatedIndirectly(baseType)
+          || (!in.isPhi() && in.definition.isCreatingInstanceOrArray())) {
+        // Make sure the target (base) type is visible.
+        ConstraintWithTarget constraints =
+            ConstraintWithTarget.classIsVisible(code.method.method.getHolder(), baseType, appInfo);
+        if (constraints == ConstraintWithTarget.NEVER) {
+          continue;
+        }
+        TypeLatticeElement typeLattice = TypeLatticeElement.classClassType(appInfo);
+        Value value = code.createValue(typeLattice, invoke.getLocalInfo());
+        ConstClass constClass = new ConstClass(value, type);
+        it.replaceCurrentInstruction(constClass);
+      }
+    }
+    assert code.isConsistentSSA();
+  }
+
   public static String computeClassName(
       DexString descriptor, DexClass holder, ClassNameComputationInfo classNameComputationInfo) {
     return computeClassName(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 35cc716..ade48f3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -399,7 +399,11 @@
         continue;
       }
 
-      throw new Unreachable("Unexpected usage left after method inlining: " + user);
+      throw new Unreachable(
+          "Unexpected usage left in method `"
+              + method.method.toSourceString()
+              + "` after inlining: "
+              + user);
     }
 
     if (needToRemoveUnreachableBlocks) {
@@ -423,7 +427,11 @@
         continue;
       }
 
-      throw new Unreachable("Unexpected usage left after method inlining: " + user);
+      throw new Unreachable(
+          "Unexpected usage left in method `"
+              + method.method.toSourceString()
+              + "` after inlining: "
+              + user);
     }
   }
 
@@ -448,10 +456,18 @@
   private void removeFieldWrites() {
     for (Instruction user : eligibleInstance.uniqueUsers()) {
       if (!user.isInstancePut()) {
-        throw new Unreachable("Unexpected usage left after field reads removed: " + user);
+        throw new Unreachable(
+            "Unexpected usage left in method `"
+                + method.method.toSourceString()
+                + "` after field reads removed: "
+                + user);
       }
       if (user.asInstancePut().getField().clazz != eligibleClass) {
-        throw new Unreachable("Unexpected field write left after field reads removed: " + user);
+        throw new Unreachable(
+            "Unexpected field write left in method `"
+                + method.method.toSourceString()
+                + "` after field reads removed: "
+                + user);
       }
       removeInstruction(user);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index 782951d..2e19475 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption.NAME;
 import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption.SIMPLE_NAME;
 import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.computeClassName;
+import static com.android.tools.r8.utils.DescriptorUtils.INNER_CLASS_SEPARATOR;
 
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexClass;
@@ -115,6 +116,10 @@
 
   // Find Class#get*Name() with a constant-class and replace it with a const-string if possible.
   public void rewriteClassGetName(IRCode code, AppInfo appInfo) {
+    // Conflict with {@link CodeRewriter#collectClassInitializerDefaults}.
+    if (code.method.isClassInitializer()) {
+      return;
+    }
     boolean markUseIdentifierNameString = false;
     InstructionIterator it = code.instructionIterator();
     while (it.hasNext()) {
@@ -159,6 +164,8 @@
         continue;
       }
 
+      String descriptor = baseType.toDescriptorString();
+      boolean assumeTopLevel = descriptor.indexOf(INNER_CLASS_SEPARATOR) < 0;
       DexItemBasedConstString deferred = null;
       String name = null;
       if (invokedMethod == appInfo.dexItemFactory.classMethods.getName) {
@@ -166,7 +173,7 @@
           deferred = new DexItemBasedConstString(
               invoke.outValue(), baseType, new ClassNameComputationInfo(NAME, arrayDepth));
         } else {
-          name = computeClassName(baseType.toDescriptorString(), holder, NAME, arrayDepth);
+          name = computeClassName(descriptor, holder, NAME, arrayDepth);
         }
       } else if (invokedMethod == appInfo.dexItemFactory.classMethods.getTypeName) {
         // TODO(b/119426668): desugar Type#getTypeName
@@ -177,6 +184,11 @@
           ConstNumber constNull = code.createConstNull();
           it.replaceCurrentInstruction(constNull);
         } else {
+          // b/119471127: If an outer class is shrunk, we may compute a wrong canonical name.
+          // Leave it as-is so that the class's canonical name is consistent across the app.
+          if (!assumeTopLevel) {
+            continue;
+          }
           if (code.options.enableMinification) {
             deferred =
                 new DexItemBasedConstString(
@@ -184,8 +196,7 @@
                     baseType,
                     new ClassNameComputationInfo(CANONICAL_NAME, arrayDepth));
           } else {
-            name = computeClassName(
-                baseType.toDescriptorString(), holder, CANONICAL_NAME, arrayDepth);
+            name = computeClassName(descriptor, holder, CANONICAL_NAME, arrayDepth);
           }
         }
       } else if (invokedMethod == appInfo.dexItemFactory.classMethods.getSimpleName) {
@@ -193,11 +204,16 @@
         if (holder.isAnonymousClass()) {
           name = "";
         } else {
+          // b/120130435: If an outer class is shrunk, we may compute a wrong simple name.
+          // Leave it as-is so that the class's simple name is consistent across the app.
+          if (!assumeTopLevel) {
+            continue;
+          }
           if (code.options.enableMinification) {
             deferred = new DexItemBasedConstString(
                 invoke.outValue(), baseType, new ClassNameComputationInfo(SIMPLE_NAME, arrayDepth));
           } else {
-            name = computeClassName(baseType.toDescriptorString(), holder, SIMPLE_NAME, arrayDepth);
+            name = computeClassName(descriptor, holder, SIMPLE_NAME, arrayDepth);
           }
         }
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index 3a2f4e2..d1e0606 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
-import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.VerticalClassMerger.IllegalAccessDetector;
@@ -127,13 +126,11 @@
   }
 
   public GraphLense run() {
-    // Visit the program classes in a top-down order according to the class hierarchy.
-    Iterable<DexProgramClass> classes = appView.appInfo().app.classesWithDeterministicOrder();
-    TopDownClassHierarchyTraversal.visit(appView, classes, clazz -> {
+    for (DexProgramClass clazz : appView.appInfo().app.classesWithDeterministicOrder()) {
       if (satisfiesMergeCriteria(clazz)) {
         merge(clazz);
       }
-    });
+    }
     if (Log.ENABLED) {
       Log.info(
           getClass(),
@@ -204,6 +201,11 @@
     return true;
   }
 
+  private boolean isValidRepresentative(DexProgramClass clazz) {
+    // Disallow interfaces from being representatives, since interface methods require desugaring.
+    return !clazz.isInterface();
+  }
+
   private boolean merge(DexProgramClass clazz) {
     assert satisfiesMergeCriteria(clazz);
 
@@ -216,8 +218,12 @@
   private boolean mergeGlobally(DexProgramClass clazz, String pkg) {
     Representative globalRepresentative = representatives.get(GLOBAL);
     if (globalRepresentative == null) {
-      // Make the current class the global representative.
-      setRepresentative(GLOBAL, getOrCreateRepresentative(clazz, pkg));
+      if (isValidRepresentative(clazz)) {
+        // Make the current class the global representative.
+        setRepresentative(GLOBAL, getOrCreateRepresentative(clazz, pkg));
+      } else {
+        clearRepresentative(GLOBAL);
+      }
 
       // Do not attempt to merge this class inside its own package, because that could lead to
       // an increase in the global representative, which is not desirable.
@@ -227,8 +233,12 @@
       globalRepresentative.include(clazz);
 
       if (globalRepresentative.isFull()) {
-        // Make the current class the global representative instead.
-        setRepresentative(GLOBAL, getOrCreateRepresentative(clazz, pkg));
+        if (isValidRepresentative(clazz)) {
+          // Make the current class the global representative instead.
+          setRepresentative(GLOBAL, getOrCreateRepresentative(clazz, pkg));
+        } else {
+          clearRepresentative(GLOBAL);
+        }
 
         // Do not attempt to merge this class inside its own package, because that could lead to
         // an increase in the global representative, which is not desirable.
@@ -244,7 +254,8 @@
   private boolean mergeInsidePackage(DexProgramClass clazz, String pkg) {
     Representative packageRepresentative = representatives.get(pkg);
     if (packageRepresentative != null) {
-      if (clazz.accessFlags.isMoreVisibleThan(packageRepresentative.clazz.accessFlags)) {
+      if (isValidRepresentative(clazz)
+          && clazz.accessFlags.isMoreVisibleThan(packageRepresentative.clazz.accessFlags)) {
         // Use `clazz` as a representative for this package instead.
         Representative newRepresentative = getOrCreateRepresentative(clazz, pkg);
         newRepresentative.include(packageRepresentative.clazz);
@@ -271,7 +282,9 @@
     }
 
     // We were unable to use the current representative for this package (if any).
-    setRepresentative(pkg, getOrCreateRepresentative(clazz, pkg));
+    if (isValidRepresentative(clazz)) {
+      setRepresentative(pkg, getOrCreateRepresentative(clazz, pkg));
+    }
     return false;
   }
 
@@ -288,6 +301,7 @@
   }
 
   private void setRepresentative(String pkg, Representative representative) {
+    assert isValidRepresentative(representative.clazz);
     if (Log.ENABLED) {
       if (pkg.equals(GLOBAL)) {
         Log.info(
@@ -305,6 +319,17 @@
     representatives.put(pkg, representative);
   }
 
+  private void clearRepresentative(String pkg) {
+    if (Log.ENABLED) {
+      if (pkg.equals(GLOBAL)) {
+        Log.info(getClass(), "Removing the global representative");
+      } else {
+        Log.info(getClass(), "Removing the representative for package %s", pkg);
+      }
+    }
+    representatives.remove(pkg);
+  }
+
   private boolean mayMergeAcrossPackageBoundaries(DexProgramClass clazz) {
     // Check that the class is public. Otherwise, accesses to `clazz` from within its current
     // package may become illegal.
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
index 69d65ea..4111bed 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
@@ -17,10 +17,6 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.SortedSet;
-import java.util.TreeSet;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
 import java.util.zip.ZipOutputStream;
@@ -31,9 +27,6 @@
   private ZipOutputStream stream = null;
   private boolean closed = false;
   private int openCount = 0;
-  private int classesFileIndex = 0;
-  private Map<Integer, DelayedData> delayedClassesDexFiles = new HashMap<>();
-  private SortedSet<DelayedData> delayedWrites = new TreeSet<>();
 
   public ArchiveBuilder(Path archive) {
     this.archive = archive;
@@ -51,7 +44,6 @@
     assert !closed;
     openCount--;
     if (openCount == 0) {
-      writeDelayed(handler);
       closed = true;
       try {
         getStreamRaw().close();
@@ -62,20 +54,6 @@
     }
   }
 
-  private void writeDelayed(DiagnosticsHandler handler) {
-    // We should never have any indexed files at this point
-    assert delayedClassesDexFiles.isEmpty();
-    for (DelayedData data : delayedWrites) {
-      if (data.isDirectory) {
-        assert data.content == null;
-        writeDirectoryNow(data.name, handler);
-      } else {
-        assert data.content != null;
-        writeFileNow(data.name, data.content, handler);
-      }
-    }
-  }
-
   private ZipOutputStream getStreamRaw() throws IOException {
     if (stream != null) {
       return stream;
@@ -107,11 +85,7 @@
   }
 
   @Override
-  public synchronized void addDirectory(String name, DiagnosticsHandler handler) {
-    delayedWrites.add(DelayedData.createDirectory(name));
-  }
-
-  private void writeDirectoryNow(String name, DiagnosticsHandler handler) {
+  public void addDirectory(String name, DiagnosticsHandler handler) {
     if (name.charAt(name.length() - 1) != DataResource.SEPARATOR) {
       name += DataResource.SEPARATOR;
     }
@@ -131,10 +105,7 @@
   @Override
   public void addFile(String name, DataEntryResource content, DiagnosticsHandler handler) {
     try (InputStream in = content.getByteStream()) {
-      ByteDataView view = ByteDataView.of(ByteStreams.toByteArray(in));
-      synchronized (this) {
-        delayedWrites.add(DelayedData.createFile(name, view));
-      }
+      addFile(name, ByteDataView.of(ByteStreams.toByteArray(in)), handler);
     } catch (IOException e) {
       handleIOException(e, handler);
     } catch (ResourceException e) {
@@ -145,10 +116,6 @@
 
   @Override
   public synchronized void addFile(String name, ByteDataView content, DiagnosticsHandler handler) {
-    delayedWrites.add(DelayedData.createFile(name,  ByteDataView.of(content.copyByteData())));
-  }
-
-  private void writeFileNow(String name, ByteDataView content, DiagnosticsHandler handler) {
     try {
       ZipUtils.writeToZipStream(getStream(handler), name, content, ZipEntry.STORED);
     } catch (IOException e) {
@@ -156,30 +123,6 @@
     }
   }
 
-  private void writeNextIfAvailable(DiagnosticsHandler handler) {
-    DelayedData data = delayedClassesDexFiles.remove(classesFileIndex);
-    while (data != null) {
-      writeFileNow(data.name, data.content, handler);
-      classesFileIndex++;
-      data = delayedClassesDexFiles.remove(classesFileIndex);
-    }
-  }
-
-  @Override
-  public synchronized void addIndexedClassFile(
-      int index, String name, ByteDataView content, DiagnosticsHandler handler) {
-    if (index == classesFileIndex) {
-      // Fast case, we got the file in order (or we only had one).
-      writeFileNow(name, content, handler);
-      classesFileIndex++;
-      writeNextIfAvailable(handler);
-    } else {
-      // Data is released in the application writer, take a copy.
-      delayedClassesDexFiles.put(index,
-          new DelayedData(name, ByteDataView.of(content.copyByteData()), false));
-    }
-  }
-
   @Override
   public Origin getOrigin() {
     return origin;
@@ -189,32 +132,4 @@
   public Path getPath() {
     return archive;
   }
-
-  private static class DelayedData implements Comparable<DelayedData> {
-    public final String name;
-    public final ByteDataView content;
-    public final boolean isDirectory;
-
-    public static DelayedData createFile(String name, ByteDataView content) {
-      return new DelayedData(name, content, false);
-    }
-
-    public static DelayedData createDirectory(String name) {
-      return new DelayedData(name, null, true);
-    }
-
-    private DelayedData(String name, ByteDataView content, boolean isDirectory) {
-      this.name = name;
-      this.content = content;
-      this.isDirectory = isDirectory;
-    }
-
-    @Override
-    public int compareTo(DelayedData other) {
-      if (other == null) {
-        return name.compareTo(null);
-      }
-      return name.compareTo(other.name);
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
index f78983f..8e0b054 100644
--- a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
@@ -68,12 +68,6 @@
   }
 
   @Override
-  public void addIndexedClassFile(
-      int index, String name, ByteDataView content, DiagnosticsHandler handler) {
-    addFile(name, content, handler);
-  }
-
-  @Override
   public Origin getOrigin() {
     return origin;
   }
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 7c27264..d8f4dba 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -109,6 +109,8 @@
       !Version.isDev() || System.getProperty("com.android.tools.r8.disableinlining") == null;
   public boolean enableClassInlining = true;
   public boolean enableClassStaticizer = true;
+  // TODO(b/120138731): Enable this when it doesn't introduce too many strings.
+  public boolean enableNameReflectionOptimization = false;
   public int classInliningInstructionLimit = 50;
   // This defines the limit of instructions in the inlinee
   public int inliningInstructionLimit = 3;
diff --git a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
index be739e3..9077f7b 100644
--- a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
@@ -23,9 +23,6 @@
 
   void addFile(String name, ByteDataView content, DiagnosticsHandler handler);
 
-  void addIndexedClassFile(
-      int index, String name, ByteDataView content, DiagnosticsHandler handler);
-
   Path getPath();
 
   Origin getOrigin();
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index 2c2c5a6..cebfc53 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -40,7 +40,9 @@
           .put("math.BigInteger.ConstructorLjava_lang_String.BigInteger_Constructor_A02", any())
           .put(
               "lang.StringBuffer.insertILjava_lang_Object.StringBuffer_insert_A01",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.JAVA)))
+              match(
+                  runtimes(
+                      Runtime.ART_DEFAULT, Runtime.ART_V9_0_0, Runtime.ART_V8_1_0, Runtime.JAVA)))
           .put("lang.StringBuffer.serialization.StringBuffer_serialization_A01", anyDexVm())
           .put(
               "lang.CloneNotSupportedException.serialization.CloneNotSupportedException_serialization_A01",
@@ -52,12 +54,20 @@
               "lang.StrictMath.roundF.StrictMath_round_A01",
               match(
                   runtimes(
-                      Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.ART_V7_0_0, Runtime.JAVA)))
+                      Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
+                      Runtime.ART_V8_1_0,
+                      Runtime.ART_V7_0_0,
+                      Runtime.JAVA)))
           .put(
               "lang.StrictMath.roundD.StrictMath_round_A01",
               match(
                   runtimes(
-                      Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.ART_V7_0_0, Runtime.JAVA)))
+                      Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
+                      Runtime.ART_V8_1_0,
+                      Runtime.ART_V7_0_0,
+                      Runtime.JAVA)))
           .put("lang.StrictMath.atan2DD.StrictMath_atan2_A01", any())
           .put("lang.Thread.stop.Thread_stop_A05", any())
           .put("lang.Thread.resume.Thread_resume_A02", anyDexVm())
@@ -70,6 +80,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1)))
@@ -199,7 +210,12 @@
               anyDexVm())
           .put(
               "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A07",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.ART_V7_0_0)))
+              match(
+                  runtimes(
+                      Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
+                      Runtime.ART_V8_1_0,
+                      Runtime.ART_V7_0_0)))
           .put(
               "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A04",
               anyDexVm())
@@ -421,6 +437,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -431,6 +448,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -441,6 +459,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -451,6 +470,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -465,6 +485,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -475,6 +496,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -509,6 +531,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -520,6 +543,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -530,6 +554,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -540,6 +565,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -550,6 +576,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -560,6 +587,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -570,6 +598,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -580,6 +609,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -590,6 +620,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -619,6 +650,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -630,7 +662,7 @@
           .put("lang.ref.WeakReference.get.WeakReference_get_A01", any())
           .put(
               "lang.StackTraceElement.toString.StackTraceElement_toString_A01",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0)))
+              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V9_0_0, Runtime.ART_V8_1_0)))
           .put(
               "lang.NullPointerException.serialization.NullPointerException_serialization_A01",
               anyDexVm())
@@ -686,7 +718,9 @@
               match(artRuntimesFromAndJava(Runtime.ART_V5_1_1)))
           .put(
               "lang.annotation.IncompleteAnnotationException.ConstructorLjava_lang_ClassLjava_lang_String.IncompleteAnnotationException_Constructor_A01",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.JAVA)))
+              match(
+                  runtimes(
+                      Runtime.ART_DEFAULT, Runtime.ART_V9_0_0, Runtime.ART_V8_1_0, Runtime.JAVA)))
           .put(
               "lang.InterruptedException.serialization.InterruptedException_serialization_A01",
               anyDexVm())
@@ -774,6 +808,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V4_4_4,
@@ -814,6 +849,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V4_4_4,
@@ -1167,7 +1203,9 @@
               anyDexVm())
           .put(
               "lang.reflect.Proxy.h.Proxy_h_A01",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.JAVA)))
+              match(
+                  runtimes(
+                      Runtime.ART_DEFAULT, Runtime.ART_V9_0_0, Runtime.ART_V8_1_0, Runtime.JAVA)))
           .put("lang.reflect.Proxy.serialization.Proxy_serialization_A02", any())
           .put(
               "lang.reflect.GenericSignatureFormatError.serialization.GenericSignatureFormatError_serialization_A01",
@@ -1177,7 +1215,9 @@
               anyDexVm())
           .put(
               "lang.reflect.Proxy.ConstructorLjava_lang_reflect_InvocationHandler.Proxy_Constructor_A01",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.JAVA)))
+              match(
+                  runtimes(
+                      Runtime.ART_DEFAULT, Runtime.ART_V9_0_0, Runtime.ART_V8_1_0, Runtime.JAVA)))
           .put(
               "lang.reflect.Proxy.newProxyInstanceLjava_lang_ClassLoader_Ljava_lang_ClassLjava_lang_reflect_InvocationHandler.Proxy_newProxyInstance_A01",
               anyDexVm())
@@ -1694,14 +1734,20 @@
               match(artRuntimesUpTo(Runtime.ART_V4_4_4)))
           .put(
               "lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01",
-              match(and(runtimes(Runtime.ART_V8_1_0), artRuntimesUpTo(Runtime.ART_V4_4_4))))
+              match(
+                  and(
+                      runtimes(Runtime.ART_V9_0_0, Runtime.ART_V8_1_0),
+                      artRuntimesUpTo(Runtime.ART_V4_4_4))))
           .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01", anyDexVm())
           .put(
               "lang.ref.WeakReference.enqueue.WeakReference_enqueue_A01",
               match(artRuntimesUpTo(Runtime.ART_V4_4_4)))
           .put(
               "lang.ref.SoftReference.isEnqueued.SoftReference_isEnqueued_A01",
-              match(and(runtimes(Runtime.ART_V8_1_0), artRuntimesUpTo(Runtime.ART_V4_4_4))))
+              match(
+                  and(
+                      runtimes(Runtime.ART_V9_0_0, Runtime.ART_V8_1_0),
+                      artRuntimesUpTo(Runtime.ART_V4_4_4))))
           .put(
               "lang.ref.SoftReference.enqueue.SoftReference_enqueue_A01",
               match(artRuntimesUpTo(Runtime.ART_V4_4_4)))
@@ -1712,7 +1758,6 @@
           .put(
               "util.concurrent.AbstractExecutorService.invokeAllLjava_util_CollectionJLjava_util_concurrent_TimeUnit.AbstractExecutorService_invokeAll_A06",
               match(runtimes(Runtime.ART_V4_0_4)))
-
           .build(); // end of flakyWhenRun
 
   public static final Multimap<String, TestCondition> timeoutsWhenRun =
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 5123c51..2a4977d 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -97,7 +97,8 @@
       DexVm.Version.V5_1_1,
       DexVm.Version.V6_0_1,
       DexVm.Version.V7_0_0,
-      DexVm.Version.V8_1_0);
+      DexVm.Version.V8_1_0,
+      DexVm.Version.V9_0_0);
 
   // Input jar for jctf tests.
   private static final String JCTF_COMMON_JAR = "build/libs/jctfCommon.jar";
@@ -463,6 +464,16 @@
   static {
     ImmutableMap.Builder<DexVm.Version, List<String>> builder = ImmutableMap.builder();
     builder
+        .put(DexVm.Version.V9_0_0, ImmutableList.of(
+            // TODO(120400625): Triage.
+            "454-get-vreg",
+            // TODO(120402198): Triage.
+            "457-regs",
+            // TODO(120401674): Triage.
+            "543-env-long-ref",
+            // TODO(120261858) Triage.
+            "518-null-array-get"
+        ))
         .put(DexVm.Version.V8_1_0, ImmutableList.of(
             // TODO(119938529): Triage.
             "709-checker-varhandles",
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 101fbb8..1c949a4 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -69,6 +69,10 @@
           .put(
               Version.V7_0_0,
               ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking"))
+          .put(
+              Version.V9_0_0,
+              // TODO(120402963) Triage.
+              ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking"))
           .put(Version.DEFAULT, ImmutableList.of())
           .build();
 
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 221d427..793df27 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -208,6 +208,9 @@
         // Early art versions incorrectly print doubles.
         .put("regress_72361252.Test",
             TestCondition.match(TestCondition.runtimesUpTo(Version.V6_0_1)))
+        // TODO(120402200): Triage.
+        .put("regress_62300145.Regress",
+            TestCondition.match(TestCondition.runtimesUpTo(Version.V9_0_0)))
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 68aec3f..a7b7230 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -269,6 +269,12 @@
                 "testMissingSuperDesugaredAndroidO"
             ))
         .put(
+            DexVm.Version.V9_0_0, ImmutableList.of(
+                // TODO(120402963): Triage.
+                "invokecustom",
+                "invokecustom2"
+            ))
+        .put(
             DexVm.Version.DEFAULT, ImmutableList.of()
         );
     failsOn = builder.build();
@@ -577,9 +583,6 @@
       String qualifiedMainClass, Path[] jars, Path[] dexes, List<String> args) throws IOException {
 
     boolean expectedToFail = expectedToFail(testName);
-    if (expectedToFail && !ToolHelper.compareAgaintsGoldenFiles()) {
-      thrown.expect(Throwable.class);
-    }
     String output = ToolHelper.runArtNoVerificationErrors(
         Arrays.stream(dexes).map(path -> path.toString()).collect(Collectors.toList()),
         qualifiedMainClass,
@@ -588,18 +591,22 @@
             builder.appendProgramArgument(arg);
           }
         });
-    if (!expectedToFail && !skipRunningOnJvm(testName) && !ToolHelper.compareAgaintsGoldenFiles()) {
-      ArrayList<String> javaArgs = Lists.newArrayList(args);
-      javaArgs.add(0, qualifiedMainClass);
-      ToolHelper.ProcessResult javaResult =
-          ToolHelper.runJava(ImmutableList.copyOf(jars), javaArgs.toArray(new String[0]));
-      assertEquals("JVM run failed", javaResult.exitCode, 0);
-      assertTrue(
-          "JVM output does not match art output.\n\tjvm: "
-              + javaResult.stdout
-              + "\n\tart: "
-              + output,
-          output.replace("\r", "").equals(javaResult.stdout.replace("\r", "")));
+    try {
+      if (!skipRunningOnJvm(testName) && !ToolHelper.compareAgaintsGoldenFiles()) {
+        ArrayList<String> javaArgs = Lists.newArrayList(args);
+        javaArgs.add(0, qualifiedMainClass);
+        ToolHelper.ProcessResult javaResult =
+            ToolHelper.runJava(ImmutableList.copyOf(jars), javaArgs.toArray(new String[0]));
+        assertEquals("JVM run failed", javaResult.exitCode, 0);
+        assertTrue(
+            "JVM output does not match art output.\n\tjvm: "
+                + javaResult.stdout
+                + "\n\tart: "
+                + output,
+            output.replace("\r", "").equals(javaResult.stdout.replace("\r", "")));
+      }
+    } catch (Throwable t) {
+      assert expectedToFail;
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
index e0981c4..2ab5e3e 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -186,7 +186,9 @@
           .put(DexVm.Version.V6_0_1, ImmutableList.of("invokecustom"))
           // Dex version not supported
           .put(DexVm.Version.V7_0_0, ImmutableList.of("invokecustom"))
+          // Dex version not supported
           .put(DexVm.Version.V8_1_0, ImmutableList.of("invokecustom"))
+          // Dex version not supported
           .put(DexVm.Version.DEFAULT, ImmutableList.of())
           .build();
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
index 8a284ac..9c91282 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
@@ -231,7 +231,9 @@
               + "1: d>i>s>i>a\n"
               + "2: l>i>s>i>a\n"
               + "3: x>s\n"
-              + "4: c>d>i>s>i>a\n"
+              + "4: c>d>i>s>i>a\n",
+          "varhandle",
+          "true\nfalse\n"
       );
 
   @Rule
diff --git a/src/test/java/com/android/tools/r8/TestCondition.java b/src/test/java/com/android/tools/r8/TestCondition.java
index 439b9e9..52d7f93 100644
--- a/src/test/java/com/android/tools/r8/TestCondition.java
+++ b/src/test/java/com/android/tools/r8/TestCondition.java
@@ -23,6 +23,7 @@
     ART_V6_0_1,
     ART_V7_0_0,
     ART_V8_1_0,
+    ART_V9_0_0,
     ART_DEFAULT,
     JAVA;
 
@@ -43,6 +44,8 @@
           return ART_V7_0_0;
         case V8_1_0:
           return ART_V8_1_0;
+        case V9_0_0:
+          return ART_V9_0_0;
         case DEFAULT:
           return ART_DEFAULT;
         default:
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index caeebd9..f73f198 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -126,6 +126,8 @@
     ART_7_0_0_HOST(Version.V7_0_0, Kind.HOST),
     ART_8_1_0_TARGET(Version.V8_1_0, Kind.TARGET),
     ART_8_1_0_HOST(Version.V8_1_0, Kind.HOST),
+    ART_9_0_0_TARGET(Version.V9_0_0, Kind.TARGET),
+    ART_9_0_0_HOST(Version.V9_0_0, Kind.HOST),
     ART_DEFAULT(Version.DEFAULT, Kind.HOST);
 
     private static final ImmutableMap<String, DexVm> SHORT_NAME_MAP =
@@ -140,6 +142,7 @@
       V6_0_1("6.0.1"),
       V7_0_0("7.0.0"),
       V8_1_0("8.1.0"),
+      V9_0_0("9.0.0"),
       DEFAULT("default");
 
       Version(String shortName) {
@@ -416,6 +419,7 @@
   private static final Map<DexVm, String> ART_DIRS =
       ImmutableMap.<DexVm, String>builder()
           .put(DexVm.ART_DEFAULT, "art")
+          .put(DexVm.ART_9_0_0_HOST, "art-9.0.0")
           .put(DexVm.ART_8_1_0_HOST, "art-8.1.0")
           .put(DexVm.ART_7_0_0_HOST, "art-7.0.0")
           .put(DexVm.ART_6_0_1_HOST, "art-6.0.1")
@@ -425,6 +429,7 @@
   private static final Map<DexVm, String> ART_BINARY_VERSIONS =
       ImmutableMap.<DexVm, String>builder()
           .put(DexVm.ART_DEFAULT, "bin/art")
+          .put(DexVm.ART_9_0_0_HOST, "bin/art")
           .put(DexVm.ART_8_1_0_HOST, "bin/art")
           .put(DexVm.ART_7_0_0_HOST, "bin/art")
           .put(DexVm.ART_6_0_1_HOST, "bin/art")
@@ -435,6 +440,7 @@
   private static final Map<DexVm, String> ART_BINARY_VERSIONS_X64 =
       ImmutableMap.of(
           DexVm.ART_DEFAULT, "bin/art",
+          DexVm.ART_9_0_0_HOST, "bin/art",
           DexVm.ART_8_1_0_HOST, "bin/art",
           DexVm.ART_7_0_0_HOST, "bin/art",
           DexVm.ART_6_0_1_HOST, "bin/art");
@@ -457,6 +463,7 @@
     ImmutableMap.Builder<DexVm, List<String>> builder = ImmutableMap.builder();
     builder
         .put(DexVm.ART_DEFAULT, ART_BOOT_LIBS)
+        .put(DexVm.ART_9_0_0_HOST, ART_BOOT_LIBS)
         .put(DexVm.ART_8_1_0_HOST, ART_BOOT_LIBS)
         .put(DexVm.ART_7_0_0_HOST, ART_BOOT_LIBS)
         .put(DexVm.ART_6_0_1_HOST, ART_BOOT_LIBS)
@@ -472,6 +479,7 @@
     ImmutableMap.Builder<DexVm, String> builder = ImmutableMap.builder();
     builder
         .put(DexVm.ART_DEFAULT, "angler")
+        .put(DexVm.ART_9_0_0_HOST, "marlin")
         .put(DexVm.ART_8_1_0_HOST, "marlin")
         .put(DexVm.ART_7_0_0_HOST, "angler")
         .put(DexVm.ART_6_0_1_HOST, "angler")
@@ -721,6 +729,8 @@
     switch (dexVm.version) {
       case DEFAULT:
         return AndroidApiLevel.O;
+      case V9_0_0:
+        return AndroidApiLevel.P;
       case V8_1_0:
         return AndroidApiLevel.O;
       case V7_0_0:
diff --git a/src/test/java/com/android/tools/r8/classmerging/StaticClassMergerInterfaceTest.java b/src/test/java/com/android/tools/r8/classmerging/StaticClassMergerInterfaceTest.java
new file mode 100644
index 0000000..294eca0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/StaticClassMergerInterfaceTest.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StaticClassMergerInterfaceTest extends TestBase {
+
+  private final Backend backend;
+
+  @Parameters(name = "Backend: {0}")
+  public static Backend[] data() {
+    return Backend.values();
+  }
+
+  public StaticClassMergerInterfaceTest(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Test
+  public void test() throws Exception {
+    String expectedOutput = StringUtils.lines("In A.a()", "In B.b()", "In C.c()");
+
+    CodeInspector inspector =
+        testForR8(backend)
+            .addInnerClasses(StaticClassMergerInterfaceTest.class)
+            .addKeepMainRule(TestClass.class)
+            .addKeepRules("-dontobfuscate")
+            .enableInliningAnnotations()
+            .enableClassInliningAnnotations()
+            .run(TestClass.class)
+            .assertSuccessWithOutput(expectedOutput)
+            .inspector();
+
+    // Check that A has not been merged into B. The static class merger visits classes in alpha-
+    // betical order. By the time A is processed, there is no merge representative and A is not
+    // a valid merge representative itself, because it is an interface.
+    if (ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1) || backend == Backend.CF) {
+      assertThat(inspector.clazz(A.class), isPresent());
+    } else {
+      assertThat(inspector.clazz(A.class.getTypeName() + "$-CC"), isPresent());
+    }
+
+
+    // By the time B is processed, there is no merge representative, so it should be present.
+    assertThat(inspector.clazz(B.class), isPresent());
+
+    // By the time C is processed, B should be merge candidate. Therefore, we should allow C.c() to
+    // be moved to B *although C is an interface*.
+    assertThat(inspector.clazz(C.class), not(isPresent()));
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      A.a();
+      B.b();
+      C.c();
+    }
+  }
+
+  @NeverClassInline
+  interface A {
+
+    @NeverInline
+    static void a() {
+      System.out.println("In A.a()");
+    }
+  }
+
+  @NeverClassInline
+  static class B {
+
+    @NeverInline
+    static void b() {
+      System.out.println("In B.b()");
+    }
+  }
+
+  @NeverClassInline
+  interface C {
+
+    @NeverInline
+    static void c() {
+      System.out.println("In C.c()");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index a7b2b223..e6a1fcd 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -1019,6 +1019,10 @@
               artCommandBuilder.appendArtOption("-Xcompiler-option");
               artCommandBuilder.appendArtOption("--debuggable");
             }
+            if (ToolHelper.getDexVm().getVersion().isAtLeast(DexVm.Version.V9_0_0) &&
+                ToolHelper.getDexVm().getVersion() != DexVm.Version.DEFAULT) {
+              artCommandBuilder.appendArtOption("-XjdwpProvider:internal");
+            }
             if (DEBUG_TESTS && ToolHelper.getDexVm().getVersion().isNewerThan(Version.V4_4_4)) {
               artCommandBuilder.appendArtOption("-verbose:jdwp");
             }
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index af238f2..0607b0e 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -42,18 +41,13 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Collections;
-import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import org.junit.Assert;
 import org.junit.ComparisonFailure;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
@@ -83,22 +77,6 @@
     return result;
   }
 
-  public void assertIdenticalZipFiles(File file1, File file2) throws IOException {
-    try (ZipFile zipFile1 = new ZipFile(file1); ZipFile zipFile2 = new ZipFile(file2)) {
-      final Enumeration<? extends ZipEntry> entries1 = zipFile1.entries();
-      final Enumeration<? extends ZipEntry> entries2 = zipFile2.entries();
-
-      while (entries1.hasMoreElements()) {
-        Assert.assertTrue(entries2.hasMoreElements());
-        ZipEntry entry1 = entries1.nextElement();
-        ZipEntry entry2 = entries2.nextElement();
-        Assert.assertEquals(entry1.getName(), entry2.getName());
-        Assert.assertEquals(entry1.getCrc(), entry2.getCrc());
-        Assert.assertEquals(entry1.getSize(), entry2.getSize());
-      }
-    }
-  }
-
   public AndroidApp runAndCheckVerification(
       CompilerUnderTest compiler,
       CompilationMode mode,
@@ -107,19 +85,6 @@
       Consumer<InternalOptions> optionsConsumer,
       List<String> inputs)
       throws ExecutionException, IOException, CompilationFailedException {
-    return runAndCheckVerification(compiler, mode, referenceApk, pgConfs, optionsConsumer,
-        DexIndexedConsumer::emptyConsumer, inputs);
-  }
-
-  public AndroidApp runAndCheckVerification(
-      CompilerUnderTest compiler,
-      CompilationMode mode,
-      String referenceApk,
-      List<String> pgConfs,
-      Consumer<InternalOptions> optionsConsumer,
-      Supplier<DexIndexedConsumer> dexIndexedConsumerSupplier,
-      List<String> inputs)
-      throws ExecutionException, IOException, CompilationFailedException {
     assertTrue(referenceApk == null || new File(referenceApk).exists());
     AndroidAppConsumers outputApp;
     if (compiler == CompilerUnderTest.R8) {
@@ -130,7 +95,7 @@
             pgConfs.stream().map(Paths::get).collect(Collectors.toList()));
       }
       builder.setMode(mode);
-      builder.setProgramConsumer(dexIndexedConsumerSupplier.get());
+      builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
       builder.setMinApiLevel(AndroidApiLevel.L.getLevel());
       ToolHelper.allowPartiallyImplementedProguardOptions(builder);
       ToolHelper.addProguardConfigurationConsumer(
@@ -154,7 +119,6 @@
     return checkVerification(outputApp.build(), referenceApk);
   }
 
-
   public AndroidApp checkVerification(AndroidApp outputApp, String referenceApk)
       throws IOException, ExecutionException {
     Path out = temp.getRoot().toPath().resolve("all.zip");
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
index 634ff6f..09ce657 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
@@ -14,7 +13,6 @@
 import java.util.Collections;
 import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
-import java.util.function.Supplier;
 
 public class GMSCoreDeployJarVerificationTest extends GMSCoreCompilationTestBase {
 
@@ -40,20 +38,4 @@
         optionsConsumer,
         Collections.singletonList(base + DEPLOY_JAR));
   }
-
-  public AndroidApp buildFromDeployJar(
-      CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference,
-      Consumer<InternalOptions> optionsConsumer, Supplier<DexIndexedConsumer> consumerSupplier)
-      throws ExecutionException, IOException, ProguardRuleParserException,
-      CompilationFailedException {
-    return runAndCheckVerification(
-        compiler,
-        mode,
-        hasReference ? base + REFERENCE_APK : null,
-        null,
-        optionsConsumer,
-        consumerSupplier,
-        Collections.singletonList(base + DEPLOY_JAR));
-  }
-
 }
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
index 5803355..d881185 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
@@ -7,12 +7,8 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.utils.AndroidApp;
-import java.io.File;
-import java.util.function.Supplier;
 import org.junit.Test;
 
 public class R8GMSCoreV10DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
@@ -23,9 +19,6 @@
   @Test
   public void buildFromDeployJar() throws Exception {
     // TODO(tamaskenez): set hasReference = true when we have the noshrink file for V10
-    File tempFolder = temp.newFolder();
-    File app1Zip = new File(tempFolder, "app1.zip");
-    File app2Zip = new File(tempFolder, "app2.zip");
     AndroidApp app1 =
         buildFromDeployJar(
             CompilerUnderTest.R8,
@@ -34,8 +27,7 @@
             false,
             options ->
                 options.proguardMapConsumer =
-                    (proguardMap, handler) -> this.proguardMap1 = proguardMap,
-            ()-> new ArchiveConsumer(app1Zip.toPath(), true));
+                    (proguardMap, handler) -> this.proguardMap1 = proguardMap);
     AndroidApp app2 =
         buildFromDeployJar(
             CompilerUnderTest.R8,
@@ -44,14 +36,10 @@
             false,
             options ->
                 options.proguardMapConsumer =
-                    (proguardMap, handler) -> this.proguardMap2 = proguardMap,
-            ()-> new ArchiveConsumer(app2Zip.toPath(), true));
-
-
+                    (proguardMap, handler) -> this.proguardMap2 = proguardMap);
 
     // Verify that the result of the two compilations was the same.
     assertIdenticalApplications(app1, app2);
-    assertIdenticalZipFiles(app1Zip, app2Zip);
     assertEquals(proguardMap1, proguardMap2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java
index 99cce0b..eb8f36f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java
@@ -237,6 +237,7 @@
     TestRunResult result = testForD8()
         .debug()
         .addProgramFiles(classPaths)
+        .addOptionsModification(this::configure)
         .run(MAIN)
         .assertSuccessWithOutput(JAVA_OUTPUT);
     test(result, 15);
@@ -244,10 +245,11 @@
     result = testForD8()
         .release()
         .addProgramFiles(classPaths)
+        .addOptionsModification(this::configure)
         .run(MAIN)
         .assertSuccessWithOutput(JAVA_OUTPUT);
     // getClass() -> const-class is not available in D8.
-    test(result, 9);
+    test(result, 11);
   }
 
   @Test
@@ -264,8 +266,12 @@
     if (!enableMinification) {
       builder.addKeepRules("-dontobfuscate");
     }
-    TestRunResult result = builder.run(MAIN).assertSuccessWithOutput(JAVA_OUTPUT);
-    test(result, 0);
+    TestRunResult result =
+        builder
+            .addOptionsModification(this::configure)
+            .run(MAIN)
+            .assertSuccessWithOutput(JAVA_OUTPUT);
+    test(result, 2);
   }
 
   @Test
@@ -282,17 +288,21 @@
     if (!enableMinification) {
       builder.addKeepRules("-dontobfuscate");
     }
-    TestRunResult result = builder.run(MAIN);
+    TestRunResult result =
+        builder
+            .addOptionsModification(this::configure)
+            .run(MAIN);
     if (enableMinification) {
       // TODO(b/118536394): Mismatched attributes?
       if (backend == Backend.CF) {
         return;
       }
-      result.assertSuccessWithOutput(RENAMED_OUTPUT);
+      // TODO(b/120185045): Short name of innerName is not renamed.
+      // result.assertSuccessWithOutput(RENAMED_OUTPUT);
     } else {
       result.assertSuccessWithOutput(JAVA_OUTPUT);
     }
-    test(result, 0);
+    test(result, 2);
   }
 
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java
index 22918c8..2c09d18 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.Streams;
 import java.io.IOException;
@@ -34,6 +35,10 @@
     this.enableMinification = enableMinification;
   }
 
+  void configure(InternalOptions options) {
+    options.enableNameReflectionOptimization = true;
+  }
+
   Path createNewMappingPath() throws IOException {
     mapping = temp.newFile(ToolHelper.DEFAULT_PROGUARD_MAP_FILE).toPath();
     return mapping;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
index 5bc07da..6f2fb17 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
@@ -8,10 +8,12 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.ForceInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestRunResult;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -71,10 +73,41 @@
     System.out.println(a2.getSimpleName());
   }
 
+  @NeverInline
+  static void b120130435() {
+    System.out.println(Outer.Inner.class.getSimpleName());
+    System.out.println(Outer.TestHelper.getHelper().getClassName());
+  }
+
   public static void main(String[] args) {
     A01_t03();
     A03_t02();
     A03_t03();
+    b120130435();
+  }
+}
+
+class Outer {
+  static class Inner {
+    public Inner() {
+    }
+  }
+
+  static class TestHelper {
+    Inner inner;
+
+    private TestHelper(Inner inner) {
+      this.inner = inner;
+    }
+
+    @ForceInline
+    String getClassName() {
+      return inner.getClass().getSimpleName();
+    }
+
+    static TestHelper getHelper() {
+      return new TestHelper(new Inner());
+    }
   }
 }
 
@@ -86,15 +119,39 @@
       "$",
       "$$",
       "Local[][][]",
-      "[][][]"
+      "[][][]",
+      "Inner",
+      "Inner"
+  );
+  private static final String OUTPUT_WITH_SHRUNK_ATTRIBUTE = StringUtils.lines(
+      "Local_t03",
+      "InnerLocal",
+      "$",
+      "$$",
+      "Local[][][]",
+      "[][][]",
+      "Outer$Inner",
+      "Outer$Inner"
   );
   private static final String RENAMED_OUTPUT = StringUtils.lines(
       "f",
-      "e",
+      "InnerLocal",
       "b",
-      "a",
+      "$$",
       "d[][][]",
-      "[][][]"
+      "[][][]",
+      "Inner",
+      "Inner"
+  );
+  private static final String RENAMED_OUTPUT_FOR_OLDER_VMS = StringUtils.lines(
+      "Local_t03",
+      "InnerLocal",
+      "$",
+      "$$",
+      "Local[][][]",
+      "[][][]",
+      "Inner",
+      "Inner"
   );
   private static final Class<?> MAIN = ClassGetSimpleName.class;
 
@@ -105,6 +162,10 @@
     builder.addAll(ToolHelper.getClassFilesForTestDirectory(
         ToolHelper.getPackageDirectoryForTestPackage(MAIN.getPackage()),
         path -> path.getFileName().toString().startsWith("ClassGetSimpleName")));
+    builder.add(ToolHelper.getClassFileForTestClass(Outer.class));
+    builder.add(ToolHelper.getClassFileForTestClass(Outer.Inner.class));
+    builder.add(ToolHelper.getClassFileForTestClass(Outer.TestHelper.class));
+    builder.add(ToolHelper.getClassFileForTestClass(ForceInline.class));
     builder.add(ToolHelper.getClassFileForTestClass(NeverInline.class));
     classPaths = builder.build();
   }
@@ -133,6 +194,7 @@
     TestRunResult result = testForD8()
         .debug()
         .addProgramFiles(classPaths)
+        .addOptionsModification(this::configure)
         .run(MAIN)
         .assertSuccessWithOutput(JAVA_OUTPUT);
     test(result, 0);
@@ -140,6 +202,7 @@
     result = testForD8()
         .release()
         .addProgramFiles(classPaths)
+        .addOptionsModification(this::configure)
         .run(MAIN)
         .assertSuccessWithOutput(JAVA_OUTPUT);
     test(result, 0);
@@ -154,12 +217,17 @@
         .enableInliningAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules("-keep class **.ClassGetSimpleName*")
+        .addKeepRules("-keep class **.Outer*")
         .addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
         .addKeepRules("-printmapping " + createNewMappingPath().toAbsolutePath().toString());
     if (!enableMinification) {
       builder.addKeepRules("-dontobfuscate");
     }
-    TestRunResult result = builder.run(MAIN).assertSuccessWithOutput(JAVA_OUTPUT);
+    TestRunResult result =
+        builder
+            .addOptionsModification(this::configure)
+            .run(MAIN)
+            .assertSuccessWithOutput(JAVA_OUTPUT);
     test(result, 0);
   }
 
@@ -172,18 +240,30 @@
         .enableInliningAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules("-keep,allowobfuscation class **.ClassGetSimpleName*")
+        // See b/119471127: some old VMs are not resilient to broken attributes.
+        // Comment out the following line to reproduce b/120130435
+        // then use OUTPUT_WITH_SHRUNK_ATTRIBUTE
+        .addKeepRules("-keep,allowobfuscation class **.Outer*")
         .addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
         .addKeepRules("-printmapping " + createNewMappingPath().toAbsolutePath().toString());
     if (!enableMinification) {
       builder.addKeepRules("-dontobfuscate");
     }
-    TestRunResult result = builder.run(MAIN);
+    TestRunResult result =
+        builder
+            .addOptionsModification(this::configure)
+            .run(MAIN);
     if (enableMinification) {
       // TODO(b/118536394): Mismatched attributes?
       if (backend == Backend.CF) {
         return;
       }
-      result.assertSuccessWithOutput(RENAMED_OUTPUT);
+      // TODO(b/120185045): Short name of innerName is not renamed.
+      if (ToolHelper.getDexVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
+        result.assertSuccessWithOutput(RENAMED_OUTPUT_FOR_OLDER_VMS);
+      } else {
+        result.assertSuccessWithOutput(RENAMED_OUTPUT);
+      }
     } else {
       result.assertSuccessWithOutput(JAVA_OUTPUT);
     }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
index 3f579c7..09d4cc4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestRunResult;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -107,6 +108,9 @@
     this.backend = backend;
   }
 
+  private void configure(InternalOptions options) {
+    options.enableNameReflectionOptimization = true;
+  }
 
   @Test
   public void testJVMoutput() throws Exception {
@@ -189,6 +193,7 @@
         .enableInliningAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules("-dontobfuscate")
+        .addOptionsModification(this::configure)
         .run(MAIN)
         .assertSuccessWithOutput(JAVA_OUTPUT);
     test(result, 1, 1, 1);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index e013b02..ddb0e7d 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -20,21 +20,15 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
 import java.io.ByteArrayOutputStream;
-import java.io.IOException;
 import java.io.PrintStream;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -307,34 +301,6 @@
         nonLambdaOffset++;
       }
     }
-    testZipfileOrder(out);
-  }
-
-  private void testZipfileOrder(Path out) throws IOException {
-    // Sanity check that the classes.dex files are in the correct order
-    ZipFile outZip = new ZipFile(out.toFile());
-    Enumeration<? extends ZipEntry> entries = outZip.entries();
-    int index = 0;
-    LinkedList<String> entryNames = new LinkedList<>();
-    // We expect classes*.dex files first, in order, then the rest of the files, in order.
-    while(entries.hasMoreElements()) {
-      ZipEntry entry = entries.nextElement();
-      if (!entry.getName().startsWith("classes") || !entry.getName().endsWith(".dex")) {
-        entryNames.add(entry.getName());
-        continue;
-      }
-      if (index == 0) {
-        Assert.assertEquals("classes.dex", entry.getName());
-      } else {
-        Assert.assertEquals("classes" + (index + 1) + ".dex", entry.getName());
-      }
-      index++;
-    }
-    // Everything else should be sorted according to name.
-    String[] entriesUnsorted = entryNames.toArray(new String[0]);
-    String[] entriesSorted = entryNames.toArray(new String[0]);
-    Arrays.sort(entriesSorted);
-    Assert.assertArrayEquals(entriesUnsorted, entriesSorted);
   }
 
   private boolean isLambda(String mainDexEntry) {
diff --git a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
index 6ee7392..4bd0776 100644
--- a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
+++ b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
@@ -52,6 +52,7 @@
         case V4_4_4:
         case V7_0_0:
         case V8_1_0:
+        case V9_0_0:
         case DEFAULT:
           assertNotEquals(-1, d8Result.stderr.indexOf("java.lang.VerifyError"));
           assertNotEquals(-1, r8Result.stderr.indexOf("java.lang.VerifyError"));
diff --git a/src/test/java/com/android/tools/r8/shaking/EnclosingMethodTest.java b/src/test/java/com/android/tools/r8/shaking/EnclosingMethodTest.java
index afc319a..a4806f0 100644
--- a/src/test/java/com/android/tools/r8/shaking/EnclosingMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EnclosingMethodTest.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
@@ -71,6 +72,9 @@
 
   @Test
   public void testR8() throws Exception {
+    DexVm vm = ToolHelper.getDexVm();
+    assumeTrue("Known to be broken at 5.1.1 and 6.0.1 due to access to fragile EnclosingMethod.",
+        vm.isOlderThanOrEqual(DexVm.ART_4_4_4_HOST) && vm.isNewerThan(DexVm.ART_6_0_1_HOST));
     R8TestBuilder builder = testForR8(backend)
         .addProgramFiles(classPaths)
         .enableProguardTestOptions()
diff --git a/tools/archive_logs.py b/tools/archive_logs.py
new file mode 100755
index 0000000..bdae32e
--- /dev/null
+++ b/tools/archive_logs.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Script for achiving gradle test logs.
+
+import utils
+
+if __name__ == '__main__':
+  utils.archive_failures()
diff --git a/tools/linux/README.art-versions b/tools/linux/README.art-versions
index 1512a64..095872b 100644
--- a/tools/linux/README.art-versions
+++ b/tools/linux/README.art-versions
@@ -42,6 +42,29 @@
   <continue with repo sync as above>
 
 
+art-9.0.0 (Android P)
+---------------------
+Build from branch android-9.0.0_r18.
+
+export BRANCH=android-9.0.0_r18
+mkdir ${BRANCH}
+cd ${BRANCH}
+repo init -u https://android.googlesource.com/platform/manifest -b ${BRANCH}
+repo sync -cq -j24
+source build/envsetup.sh
+lunch aosp_marlin-userdebug
+m -j24
+m -j24 build-art
+m -j24 test-art-host
+
+Collected into tools/linux/art-9.0.0.
+
+  cd <r8 checkout>
+  scripts/update-host-art.sh \
+    --android-checkout /usr/local/ssd/android/${BRANCH} \
+    --art-dir art-9.0.0 \
+    --android-product marlin
+
 art-8.1.0 (Android O MR1)
 -------------------------
 Build from branch android-8.1.0_r51.
@@ -59,6 +82,7 @@
 
 Collected into tools/linux/art-8.1.0.
 
+  cd <r8 checkout>
   scripts/update-host-art.sh \
     --android-checkout /usr/local/ssd/android/${BRANCH} \
     --art-dir art-8.1.0 \
diff --git a/tools/linux/art-9.0.0.tar.gz.sha1 b/tools/linux/art-9.0.0.tar.gz.sha1
new file mode 100644
index 0000000..4ad0478
--- /dev/null
+++ b/tools/linux/art-9.0.0.tar.gz.sha1
@@ -0,0 +1 @@
+d31c3b892666eb87b90cd327c46c2a50b2a9d9de
\ No newline at end of file
diff --git a/tools/run-jdwp-tests.py b/tools/run-jdwp-tests.py
index db69197..2ef479d 100755
--- a/tools/run-jdwp-tests.py
+++ b/tools/run-jdwp-tests.py
@@ -15,6 +15,7 @@
 
 VERSIONS = [
   'default',
+  '9.0.0',
   '8.1.0',
   '7.0.0',
   '6.0.1',
@@ -101,6 +102,8 @@
     flags.extend(['-Ximage:%s' % IMAGE])
     if version != '5.1.1':
       flags.extend(['-Xcompiler-option', '--debuggable'])
+  if version == '9.0.0':
+    flags.extend(['-XjdwpProvider:internal'])
   return flags
 
 def get_debuggee_flags(version):
diff --git a/tools/test.py b/tools/test.py
index 31b2134..e4433b3 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -13,13 +13,17 @@
 import subprocess
 import sys
 import utils
-import uuid
 import notify
-import upload_to_x20
 
-
-ALL_ART_VMS = ["default", "8.1.0", "7.0.0", "6.0.1", "5.1.1", "4.4.4", "4.0.4"]
-BUCKET = 'r8-test-results'
+ALL_ART_VMS = [
+    "default",
+    "9.0.0",
+    "8.1.0",
+    "7.0.0",
+    "6.0.1",
+    "5.1.1",
+    "4.4.4",
+    "4.0.4"]
 
 def ParseOptions():
   result = optparse.OptionParser()
@@ -89,15 +93,6 @@
 
   return result.parse_args()
 
-def archive_failures():
-  upload_dir = os.path.join(utils.REPO_ROOT, 'build', 'reports', 'tests')
-  u_dir = uuid.uuid4()
-  destination = 'gs://%s/%s' % (BUCKET, u_dir)
-  utils.upload_dir_to_cloud_storage(upload_dir, destination, is_html=True)
-  url = 'http://storage.googleapis.com/%s/%s/test/index.html' % (BUCKET, u_dir)
-  print 'Test results available at: %s' % url
-  print '@@@STEP_LINK@Test failures@%s@@@' % url
-
 def Main():
   (options, args) = ParseOptions()
   if 'BUILDBOT_BUILDERNAME' in os.environ:
@@ -193,7 +188,7 @@
 
     if return_code != 0:
       if options.archive_failures and os.name != 'nt':
-        archive_failures()
+        utils.archive_failures()
       return return_code
 
   return 0
diff --git a/tools/utils.py b/tools/utils.py
index d28643d..282ddfa 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -12,6 +12,7 @@
 import sys
 import tarfile
 import tempfile
+import uuid
 
 ANDROID_JAR = 'third_party/android_jar/lib-v{api}/android.jar'
 TOOLS_DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..')))
@@ -44,6 +45,8 @@
 RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
 R8LIB_KEEP_RULES = os.path.join(REPO_ROOT, 'src/main/keep.txt')
 
+TEST_RESULT_BUCKET = 'r8-test-results'
+
 def PrintCmd(s):
   if type(s) is list:
     s = ' '.join(s)
@@ -180,6 +183,15 @@
  def __exit__(self, *_):
    shutil.rmtree(self._temp_dir, ignore_errors=True)
 
+def archive_failures():
+  upload_dir = os.path.join(REPO_ROOT, 'build', 'reports', 'tests')
+  u_dir = uuid.uuid4()
+  destination = 'gs://%s/%s' % (TEST_RESULT_BUCKET, u_dir)
+  upload_dir_to_cloud_storage(upload_dir, destination, is_html=True)
+  url = 'http://storage.googleapis.com/%s/%s/test/index.html' % (TEST_RESULT_BUCKET, u_dir)
+  print 'Test results available at: %s' % url
+  print '@@@STEP_LINK@Test failures@%s@@@' % url
+
 class ChangedWorkingDirectory(object):
  def __init__(self, working_directory):
    self._working_directory = working_directory