Merge "Add com.numix.calculator to run_on_as_app.py"
diff --git a/build.gradle b/build.gradle
index 6afc31a..d9fd85d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -471,6 +471,8 @@
         // Javac often runs out of stack space when compiling the tests.
         // Increase the stack size for the javac process.
         options.forkOptions.jvmArgs << "-Xss4m"
+        // Test compilation is sometimes hitting the default limit at 1g, increase it.
+        options.forkOptions.jvmArgs << "-Xmx2g"
         // Set the bootclass path so compilation is consistent with 1.8 target compatibility.
         options.forkOptions.jvmArgs << "-Xbootclasspath/a:third_party/openjdk/openjdk-rt-1.8/rt.jar"
     }
@@ -1508,7 +1510,7 @@
     if (project.hasProperty('r8lib') || project.hasProperty('r8lib_no_deps')) {
         def out = new StringBuffer()
         def err = new StringBuffer()
-        def command = "tools/retrace.py"
+        def command = "python tools/retrace.py"
         def header = "RETRACED STACKTRACE";
         if (System.getenv('BUILDBOT_BUILDERNAME') != null
                 && !System.getenv('BUILDBOT_BUILDERNAME').endsWith("_release")) {
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 5131b19..6705f0a 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -85,6 +85,7 @@
 
     builders {
       name: "archive"
+      priority: 25
       mixins: "linux"
       execution_timeout_secs: 1800  # 1/2h
       recipe {
@@ -93,6 +94,7 @@
     }
     builders {
       name: "archive_release"
+      priority: 25
       mixins: "linux"
       execution_timeout_secs: 1800  # 1/2h
       recipe {
diff --git a/infra/config/global/luci-notify.cfg b/infra/config/global/luci-notify.cfg
new file mode 100644
index 0000000..b8d8a32
--- /dev/null
+++ b/infra/config/global/luci-notify.cfg
@@ -0,0 +1,161 @@
+# Defines email notifications for builders.
+# See schema at
+# https://chromium.googlesource.com/infra/luci/luci-go/+/master/luci_notify/api/config/notify.proto
+#
+
+notifiers {
+  name: "r8-failures"
+  notifications {
+    on_change: false
+    on_success: false
+    on_failure: false
+    on_new_failure: true
+    email {
+      # Temporary, to ensure that it works.
+      recipients: "ricow@google.com"
+    }
+    # This means send to all people on the blamelist!
+    notify_blamelist {}
+  }
+  builders {
+    name: "archive"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "archive_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-4.0.4"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-4.0.4_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-4.4.4"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-4.4.4_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-5.1.1"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-5.1.1_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-6.0.1"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-6.0.1_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-7.0.0"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-7.0.0_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-jctf"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-jctf_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-4.0.4"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-4.0.4_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-4.4.4"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-4.4.4_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-5.1.1"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-5.1.1_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-6.0.1"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-6.0.1_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-7.0.0"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-7.0.0_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-internal"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-internal_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-jctf"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-jctf_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "r8cf-linux-jctf"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "windows"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "windows_release"
+    bucket: "luci.r8.ci"
+  }
+}
+
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 2b67476..4de5097 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -74,6 +74,9 @@
 job {
   id: "archive"
   acl_sets: "default"
+  triggering_policy: {
+    max_concurrent_invocations: 3
+  }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.r8.ci"
@@ -403,6 +406,9 @@
 
 job {
   id: "windows"
+  triggering_policy: {
+    max_concurrent_invocations: 3
+  }
   acl_sets: "default"
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -414,6 +420,9 @@
 job {
   id: "windows_release"
   acl_sets: "default"
+  triggering_policy: {
+    max_concurrent_invocations: 3
+  }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.r8.ci"
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index f6454f9..ad6c7d2 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -215,7 +215,9 @@
         int computedMinApiLevel = options.minApiLevel;
         for (ProgramResource input : dexSources) {
           DexReader dexReader = new DexReader(input);
-          computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
+          if (options.passthroughDexCode) {
+            computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
+          }
           dexParsers.add(new DexParser(dexReader, classKind, itemFactory, options.reporter));
         }
         options.minApiLevel = computedMinApiLevel;
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 d7426fc..92c07d1 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
@@ -25,8 +24,6 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
@@ -44,7 +41,9 @@
 
   private final AppView<AppInfoWithLiveness> appView;
   private final AppInfoWithLiveness appInfo;
-  private final Reporter reporter;
+  private final ClassNamingStrategy classNamingStrategy;
+  private final PackageNamingStrategy packageNamingStrategy;
+  private final Iterable<? extends DexClass> classes;
   private final PackageObfuscationMode packageObfuscationMode;
   private final boolean isAccessModificationAllowed;
   private final Set<String> noObfuscationPrefixes = Sets.newHashSet();
@@ -62,11 +61,18 @@
 
   private final Namespace topLevelState;
 
-  ClassNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+  ClassNameMinifier(
+      AppView<AppInfoWithLiveness> appView,
+      RootSet rootSet,
+      ClassNamingStrategy classNamingStrategy,
+      PackageNamingStrategy packageNamingStrategy,
+      Iterable<? extends DexClass> classes) {
     this.appView = appView;
     this.appInfo = appView.appInfo();
+    this.classNamingStrategy = classNamingStrategy;
+    this.packageNamingStrategy = packageNamingStrategy;
+    this.classes = classes;
     InternalOptions options = appView.options();
-    this.reporter = options.reporter;
     this.packageObfuscationMode = options.getProguardConfiguration().getPackageObfuscationMode();
     this.isAccessModificationAllowed =
         options.getProguardConfiguration().isAccessModificationAllowed();
@@ -99,8 +105,6 @@
   }
 
   ClassRenaming computeRenaming(Timing timing) {
-    // Use deterministic class order to make sure renaming is deterministic.
-    Iterable<DexProgramClass> classes = appInfo.classesWithDeterministicOrder();
     // Collect names we have to keep.
     timing.begin("reserve");
     for (DexClass clazz : classes) {
@@ -175,7 +179,7 @@
       // 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;
-      renaming.put(type, topLevelState.nextTypeName());
+      renaming.put(type, topLevelState.nextTypeName(type));
     }
   }
 
@@ -250,7 +254,7 @@
     if (state == null) {
       state = getStateForClass(type);
     }
-    return state.nextTypeName();
+    return state.nextTypeName(type);
   }
 
   private Namespace getStateForClass(DexType type) {
@@ -344,12 +348,10 @@
     }
   }
 
-  private class Namespace {
+  protected class Namespace {
 
     private final String packageName;
     private final char[] packagePrefix;
-    private int typeCounter = 1;
-    private int packageCounter = 1;
     private final Iterator<String> packageDictionaryIterator;
     private final Iterator<String> classDictionaryIterator;
 
@@ -376,37 +378,37 @@
       return packageName;
     }
 
-    private String nextSuggestedNameForClass() {
+    private DexString nextSuggestedNameForClass(DexType type) {
       StringBuilder nextName = new StringBuilder();
-      if (classDictionaryIterator.hasNext()) {
+      if (!classNamingStrategy.bypassDictionary() && classDictionaryIterator.hasNext()) {
         nextName.append(packagePrefix).append(classDictionaryIterator.next()).append(';');
-        return nextName.toString();
+        return appInfo.dexItemFactory.createString(nextName.toString());
       } else {
-        return StringUtils.numberToIdentifier(packagePrefix, typeCounter++, true);
+        return classNamingStrategy.next(this, type, packagePrefix);
       }
     }
 
-    DexString nextTypeName() {
+    DexString nextTypeName(DexType type) {
       DexString candidate;
       do {
-        candidate = appInfo.dexItemFactory.createString(nextSuggestedNameForClass());
+        candidate = nextSuggestedNameForClass(type);
       } while (usedTypeNames.contains(candidate));
       usedTypeNames.add(candidate);
       return candidate;
     }
 
     private String nextSuggestedNameForSubpackage() {
-      StringBuilder nextName = new StringBuilder();
       // Note that the differences between this method and the other variant for class renaming are
       // 1) this one uses the different dictionary and counter,
       // 2) this one does not append ';' at the end, and
       // 3) this one removes 'L' at the beginning to make the return value a binary form.
-      if (packageDictionaryIterator.hasNext()) {
+      if (!packageNamingStrategy.bypassDictionary() && packageDictionaryIterator.hasNext()) {
+        StringBuilder nextName = new StringBuilder();
         nextName.append(packagePrefix).append(packageDictionaryIterator.next());
+        return nextName.toString().substring(1);
       } else {
-        nextName.append(StringUtils.numberToIdentifier(packagePrefix, packageCounter++, false));
+        return packageNamingStrategy.next(this, packagePrefix);
       }
-      return nextName.toString().substring(1);
     }
 
     String nextPackagePrefix() {
@@ -419,6 +421,20 @@
     }
   }
 
+  protected interface ClassNamingStrategy {
+
+    DexString next(Namespace namespace, DexType type, char[] packagePrefix);
+
+    boolean bypassDictionary();
+  }
+
+  protected interface PackageNamingStrategy {
+
+    String next(Namespace namespace, char[] packagePrefix);
+
+    boolean bypassDictionary();
+  }
+
   /**
    * Compute parent package prefix from the given package prefix.
    *
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 e2127dd..e419d5a 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -19,8 +19,9 @@
 
 class FieldNameMinifier extends MemberNameMinifier<DexField, DexType> {
 
-  FieldNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
-    super(appView, rootSet);
+  FieldNameMinifier(
+      AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
+    super(appView, rootSet, strategy);
   }
 
   @Override
@@ -106,7 +107,8 @@
   private void renameField(DexEncodedField encodedField, NamingState<DexType, ?> state) {
     DexField field = encodedField.field;
     if (!state.isReserved(field.name, field.type)) {
-      renaming.put(field, state.assignNewNameFor(field.name, field.type, useUniqueMemberNames));
+      renaming.put(
+          field, state.assignNewNameFor(field, field.name, field.type, useUniqueMemberNames));
     }
   }
 
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 b643be3..6c46119 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -234,7 +234,8 @@
       sourceMethods.addAll(sourceMethodsMap.get(k));
       for (NamingState<DexProto, ?> namingState : globalStateMap.get(k)) {
         collectedStates.add(
-            new MethodNamingState(namingState, unifiedMethod.name, unifiedMethod.proto));
+            new MethodNamingState(
+                namingState, unifiedMethod, unifiedMethod.name, unifiedMethod.proto));
       }
     }
 
@@ -249,7 +250,7 @@
     }
 
     MethodNamingState originState =
-        new MethodNamingState(originStates.get(key), method.name, method.proto);
+        new MethodNamingState(originStates.get(key), method, method.name, method.proto);
     assignNameForInterfaceMethodInAllStates(collectedStates, sourceMethods, originState);
   }
 
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 53957e4..d60de88 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -5,8 +5,10 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CachedHashValueDexItem;
+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.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;
@@ -36,7 +38,8 @@
   // which is useful for debugging.
   private final BiMap<DexType, NamingState<StateType, ?>> states = HashBiMap.create();
 
-  MemberNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+  MemberNameMinifier(
+      AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
     this.appView = appView;
     this.appInfo = appView.appInfo();
     this.rootSet = rootSet;
@@ -45,8 +48,9 @@
     this.useUniqueMemberNames = options.getProguardConfiguration().isUseUniqueClassMemberNames();
     this.overloadAggressively =
         options.getProguardConfiguration().isOverloadAggressivelyWithoutUseUniqueClassMemberNames();
-    this.globalState = NamingState.createRoot(
-        appInfo.dexItemFactory, dictionary, getKeyTransform(), useUniqueMemberNames);
+    this.globalState =
+        NamingState.createRoot(
+            appInfo.dexItemFactory, dictionary, getKeyTransform(), strategy, useUniqueMemberNames);
   }
 
   abstract Function<StateType, ?> getKeyTransform();
@@ -88,4 +92,10 @@
       return useUniqueMemberNames;
     }
   }
+
+  interface MemberNamingStrategy {
+    DexString next(DexReference source, InternalState internalState);
+
+    boolean bypassDictionary();
+  }
 }
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 2cd8712..e97910f 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -91,9 +91,12 @@
   private final Equivalence<DexMethod> equivalence;
 
   private final FrontierState frontierState = new FrontierState();
+  private final MemberNamingStrategy strategy;
 
-  MethodNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
-    super(appView, rootSet);
+  MethodNameMinifier(
+      AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
+    super(appView, rootSet, strategy);
+    this.strategy = strategy;
     equivalence =
         overloadAggressively
             ? MethodSignatureEquivalence.get()
@@ -188,7 +191,8 @@
       DexString renamedName =
           renamingAtThisLevel.computeIfAbsent(
               equivalence.wrap(method),
-              key -> state.assignNewNameFor(method.name, method.proto, useUniqueMemberNames));
+              key ->
+                  state.assignNewNameFor(method, method.name, method.proto, useUniqueMemberNames));
       renaming.put(method, renamedName);
     }
   }
@@ -233,6 +237,7 @@
                           appInfo.dexItemFactory,
                           dictionary,
                           getKeyTransform(),
+                          strategy,
                           useUniqueMemberNames)
                       : parent.createChild());
 
@@ -276,8 +281,11 @@
     private final NamingState<DexProto, ?> parent;
     private final DexString name;
     private final DexProto proto;
+    private final DexMethod method;
 
-    MethodNamingState(NamingState<DexProto, ?> parent, DexString name, DexProto proto) {
+    MethodNamingState(
+        NamingState<DexProto, ?> parent, DexMethod method, DexString name, DexProto proto) {
+      this.method = method;
       assert parent != null;
       this.parent = parent;
       this.name = name;
@@ -285,11 +293,7 @@
     }
 
     DexString assignNewName() {
-      return parent.assignNewNameFor(name, proto, false);
-    }
-
-    void reserveName() {
-      parent.reserveName(name, proto);
+      return parent.assignNewNameFor(method, name, proto, false);
     }
 
     boolean isReserved() {
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
new file mode 100644
index 0000000..f72efe1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+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.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
+import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
+import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
+import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+class MinifiedRenaming extends NamingLens {
+
+  private final AppInfo appInfo;
+  private final Map<String, String> packageRenaming;
+  private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
+
+  MinifiedRenaming(
+      ClassRenaming classRenaming,
+      MethodRenaming methodRenaming,
+      FieldRenaming fieldRenaming,
+      AppInfo appInfo) {
+    this.appInfo = appInfo;
+    this.packageRenaming = classRenaming.packageRenaming;
+    renaming.putAll(classRenaming.classRenaming);
+    renaming.putAll(methodRenaming.renaming);
+    renaming.putAll(methodRenaming.callSiteRenaming);
+    renaming.putAll(fieldRenaming.renaming);
+  }
+
+  @Override
+  public String lookupPackageName(String packageName) {
+    return packageRenaming.getOrDefault(packageName, packageName);
+  }
+
+  @Override
+  public DexString lookupDescriptor(DexType type) {
+    return renaming.getOrDefault(type, type.descriptor);
+  }
+
+  @Override
+  public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
+    if (attribute.getInnerName() == null) {
+      return null;
+    }
+    // The Java reflection library assumes that that inner-class names are separated by a $ and
+    // thus we allow the mapping of an inner name to rely on that too. If the dollar is not
+    // present after pulling off the original inner-name, then we revert to using the simple name
+    // of the inner class as its name.
+    DexType innerType = attribute.getInner();
+    String inner = DescriptorUtils.descriptorToInternalName(innerType.descriptor.toString());
+    String innerName = attribute.getInnerName().toString();
+    int lengthOfPrefix = inner.length() - innerName.length();
+    if (lengthOfPrefix < 0
+        || inner.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR, lengthOfPrefix - 1) < 0
+        || !inner.endsWith(innerName)) {
+      return lookupSimpleName(innerType, options.itemFactory);
+    }
+
+    // At this point we assume the input was of the form: <OuterType>$<index><InnerName>
+    // Find the mapped type and if it remains the same return that, otherwise split at $.
+    String innerTypeMapped =
+        DescriptorUtils.descriptorToInternalName(lookupDescriptor(innerType).toString());
+    if (inner.equals(innerTypeMapped)) {
+      return attribute.getInnerName();
+    }
+    int index = innerTypeMapped.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
+    if (index < 0) {
+      // TODO(b/120639028): Replace this by "assert false" and remove the testing option.
+      // Hitting means we have converted a proper Outer$Inner relationship to an invalid one.
+      assert !options.testing.allowFailureOnInnerClassErrors
+          : "Outer$Inner class was remapped without keeping the dollar separator";
+      return lookupSimpleName(innerType, options.itemFactory);
+    }
+    return options.itemFactory.createString(innerTypeMapped.substring(index + 1));
+  }
+
+  @Override
+  public DexString lookupName(DexMethod method) {
+    return renaming.getOrDefault(method, method.name);
+  }
+
+  @Override
+  public DexString lookupMethodName(DexCallSite callSite) {
+    return renaming.getOrDefault(callSite, callSite.methodName);
+  }
+
+  @Override
+  public DexString lookupName(DexField field) {
+    return renaming.getOrDefault(field, field.name);
+  }
+
+  @Override
+  void forAllRenamedTypes(Consumer<DexType> consumer) {
+    DexReference.filterDexType(DexReference.filterDexReference(renaming.keySet().stream()))
+        .forEach(consumer);
+  }
+
+  @Override
+  <T extends DexItem> Map<String, T> getRenamedItems(
+      Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
+    return renaming.keySet().stream()
+        .filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
+        .map(clazz::cast)
+        .collect(ImmutableMap.toImmutableMap(namer, i -> i));
+  }
+
+  /**
+   * Checks whether the target is precise enough to be translated,
+   *
+   * <p>We only track the renaming of actual definitions, Thus, if we encounter a method id that
+   * does not directly point at a definition, we won't find the actual renaming. To avoid
+   * dispatching on every lookup, we assume that the tree has been fully dispatched by {@link
+   * MemberRebindingAnalysis}.
+   *
+   * <p>Library methods are excluded from this check, as those are never renamed.
+   */
+  @Override
+  public boolean checkTargetCanBeTranslated(DexMethod item) {
+    if (item.holder.isArrayType()) {
+      // Array methods are never renamed, so do not bother to check.
+      return true;
+    }
+    DexClass holder = appInfo.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);
+    DexClass staticTargetHolder =
+        staticTarget != null ? appInfo.definitionFor(staticTarget.method.getHolder()) : null;
+    DexClass directTargetHolder =
+        directTarget != null ? appInfo.definitionFor(directTarget.method.getHolder()) : null;
+    DexClass virtualTargetHolder =
+        virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.getHolder()) : null;
+    return (directTarget == null && staticTarget == null && virtualTarget == null)
+        || (virtualTarget != null && virtualTarget.method == item)
+        || (directTarget != null && directTarget.method == item)
+        || (staticTarget != null && staticTarget.method == item)
+        || (directTargetHolder != null && directTargetHolder.isLibraryClass())
+        || (virtualTargetHolder != null && virtualTargetHolder.isLibraryClass())
+        || (staticTargetHolder != null && staticTargetHolder.isLibraryClass());
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    renaming.forEach(
+        (item, str) -> {
+          if (item instanceof DexType) {
+            builder.append("[c] ");
+          } else if (item instanceof DexMethod) {
+            builder.append("[m] ");
+          } else if (item instanceof DexField) {
+            builder.append("[f] ");
+          }
+          builder.append(item.toSourceString());
+          builder.append(" -> ");
+          builder.append(str.toSourceString());
+          builder.append('\n');
+        });
+    return builder.toString();
+  }
+}
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 be4fd39..9aa8502 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -3,34 +3,28 @@
 // BSD-style license that can be found in the LICENSE file.
 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;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItem;
-import com.android.tools.r8.graph.DexMethod;
+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.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.ClassNameMinifier.ClassNamingStrategy;
 import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
+import com.android.tools.r8.naming.ClassNameMinifier.Namespace;
+import com.android.tools.r8.naming.ClassNameMinifier.PackageNamingStrategy;
 import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
+import com.android.tools.r8.naming.MemberNameMinifier.MemberNamingStrategy;
 import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
-import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+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.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableMap;
-import java.util.IdentityHashMap;
-import java.util.Map;
+import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
 import java.util.Set;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
 
 public class Minifier {
 
@@ -54,7 +48,14 @@
   public NamingLens run(Timing timing) {
     assert options.enableMinification;
     timing.begin("MinifyClasses");
-    ClassNameMinifier classNameMinifier = new ClassNameMinifier(appView, rootSet);
+    ClassNameMinifier classNameMinifier =
+        new ClassNameMinifier(
+            appView,
+            rootSet,
+            new MinificationClassNamingStrategy(appInfo.dexItemFactory),
+            new MinificationPackageNamingStrategy(),
+            // Use deterministic class order to make sure renaming is deterministic.
+            appInfo.classesWithDeterministicOrder());
     ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
     timing.end();
 
@@ -62,16 +63,19 @@
             classRenaming, MethodRenaming.empty(), FieldRenaming.empty(), appInfo)
         .verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
 
+    MemberNamingStrategy minifyMembers = new MinifierMemberNamingStrategy(appView.dexItemFactory());
     timing.begin("MinifyMethods");
     MethodRenaming methodRenaming =
-        new MethodNameMinifier(appView, rootSet).computeRenaming(desugaredCallSites, timing);
+        new MethodNameMinifier(appView, rootSet, minifyMembers)
+            .computeRenaming(desugaredCallSites, timing);
     timing.end();
 
     assert new MinifiedRenaming(classRenaming, methodRenaming, FieldRenaming.empty(), appInfo)
         .verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
 
     timing.begin("MinifyFields");
-    FieldRenaming fieldRenaming = new FieldNameMinifier(appView, rootSet).computeRenaming(timing);
+    FieldRenaming fieldRenaming =
+        new FieldNameMinifier(appView, rootSet, minifyMembers).computeRenaming(timing);
     timing.end();
 
     NamingLens lens = new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
@@ -84,159 +88,73 @@
     return lens;
   }
 
-  private static class MinifiedRenaming extends NamingLens {
+  static class MinificationClassNamingStrategy implements ClassNamingStrategy {
 
-    private final AppInfo appInfo;
-    private final Map<String, String> packageRenaming;
-    private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
+    private final DexItemFactory factory;
+    private final Object2IntMap<Namespace> namespaceCounters = new Object2IntLinkedOpenHashMap<>();
 
-    private MinifiedRenaming(
-        ClassRenaming classRenaming,
-        MethodRenaming methodRenaming,
-        FieldRenaming fieldRenaming,
-        AppInfo appInfo) {
-      this.appInfo = appInfo;
-      this.packageRenaming = classRenaming.packageRenaming;
-      renaming.putAll(classRenaming.classRenaming);
-      renaming.putAll(methodRenaming.renaming);
-      renaming.putAll(methodRenaming.callSiteRenaming);
-      renaming.putAll(fieldRenaming.renaming);
+    MinificationClassNamingStrategy(DexItemFactory factory) {
+      this.factory = factory;
+      namespaceCounters.defaultReturnValue(1);
     }
 
     @Override
-    public String lookupPackageName(String packageName) {
-      return packageRenaming.getOrDefault(packageName, packageName);
+    public DexString next(Namespace namespace, DexType type, char[] packagePrefix) {
+      int counter = namespaceCounters.put(namespace, namespaceCounters.getInt(namespace) + 1);
+      DexString string =
+          factory.createString(StringUtils.numberToIdentifier(packagePrefix, counter, true));
+      return string;
     }
 
     @Override
-    public DexString lookupDescriptor(DexType type) {
-      return renaming.getOrDefault(type, type.descriptor);
+    public boolean bypassDictionary() {
+      return false;
+    }
+  }
+
+  static class MinificationPackageNamingStrategy implements PackageNamingStrategy {
+
+    private final Object2IntMap<Namespace> namespaceCounters = new Object2IntLinkedOpenHashMap<>();
+
+    public MinificationPackageNamingStrategy() {
+      namespaceCounters.defaultReturnValue(1);
     }
 
     @Override
-    public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
-      if (attribute.getInnerName() == null) {
-        return null;
-      }
-      // The Java reflection library assumes that that inner-class names are separated by a $ and
-      // thus we allow the mapping of an inner name to rely on that too. If the dollar is not
-      // present after pulling off the original inner-name, then we revert to using the simple name
-      // of the inner class as its name.
-      DexType innerType = attribute.getInner();
-      String inner = DescriptorUtils.descriptorToInternalName(innerType.descriptor.toString());
-      String innerName = attribute.getInnerName().toString();
-      int lengthOfPrefix = inner.length() - innerName.length();
-      if (lengthOfPrefix < 0
-          || inner.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR, lengthOfPrefix - 1) < 0
-          || !inner.endsWith(innerName)) {
-        return lookupSimpleName(innerType, options.itemFactory);
-      }
-
-      // At this point we assume the input was of the form: <OuterType>$<index><InnerName>
-      // Find the mapped type and if it remains the same return that, otherwise split at $.
-      String innerTypeMapped =
-          DescriptorUtils.descriptorToInternalName(lookupDescriptor(innerType).toString());
-      if (inner.equals(innerTypeMapped)) {
-        return attribute.getInnerName();
-      }
-      int index = innerTypeMapped.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
-      if (index < 0) {
-        // TODO(b/120639028): Replace this by "assert false" and remove the testing option.
-        // Hitting means we have converted a proper Outer$Inner relationship to an invalid one.
-        assert !options.testing.allowFailureOnInnerClassErrors
-            : "Outer$Inner class was remapped without keeping the dollar separator";
-        return lookupSimpleName(innerType, options.itemFactory);
-      }
-      return options.itemFactory.createString(innerTypeMapped.substring(index + 1));
+    public String next(Namespace namespace, char[] packagePrefix) {
+      // Note that the differences between this method and the other variant for class renaming are
+      // 1) this one uses the different dictionary and counter,
+      // 2) this one does not append ';' at the end, and
+      // 3) this one removes 'L' at the beginning to make the return value a binary form.
+      int counter = namespaceCounters.put(namespace, namespaceCounters.getInt(namespace) + 1);
+      return StringUtils.numberToIdentifier(packagePrefix, counter, false).substring(1);
     }
 
     @Override
-    public DexString lookupName(DexMethod method) {
-      return renaming.getOrDefault(method, method.name);
+    public boolean bypassDictionary() {
+      return false;
+    }
+  }
+
+  static class MinifierMemberNamingStrategy implements MemberNamingStrategy {
+
+    char[] EMPTY_CHAR_ARRAY = new char[0];
+
+    private final DexItemFactory factory;
+
+    public MinifierMemberNamingStrategy(DexItemFactory factory) {
+      this.factory = factory;
     }
 
     @Override
-    public DexString lookupMethodName(DexCallSite callSite) {
-      return renaming.getOrDefault(callSite, callSite.methodName);
+    public DexString next(DexReference dexReference, InternalState internalState) {
+      int counter = internalState.incrementAndGet();
+      return factory.createString(StringUtils.numberToIdentifier(EMPTY_CHAR_ARRAY, counter, false));
     }
 
     @Override
-    public DexString lookupName(DexField field) {
-      return renaming.getOrDefault(field, field.name);
-    }
-
-    @Override
-    void forAllRenamedTypes(Consumer<DexType> consumer) {
-      DexReference.filterDexType(DexReference.filterDexReference(renaming.keySet().stream()))
-          .forEach(consumer);
-    }
-
-    @Override
-    <T extends DexItem> Map<String, T> getRenamedItems(
-        Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
-      return renaming.keySet().stream()
-          .filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
-          .map(clazz::cast)
-          .collect(ImmutableMap.toImmutableMap(namer, i -> i));
-    }
-
-    /**
-     * Checks whether the target is precise enough to be translated,
-     * <p>
-     * We only track the renaming of actual definitions, Thus, if we encounter a method id that
-     * does not directly point at a definition, we won't find the actual renaming. To avoid
-     * dispatching on every lookup, we assume that the tree has been fully dispatched by
-     * {@link MemberRebindingAnalysis}.
-     * <p>
-     * Library methods are excluded from this check, as those are never renamed.
-     */
-    @Override
-    public boolean checkTargetCanBeTranslated(DexMethod item) {
-      if (item.holder.isArrayType()) {
-        // Array methods are never renamed, so do not bother to check.
-        return true;
-      }
-      DexClass holder = appInfo.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);
-      DexClass staticTargetHolder =
-          staticTarget != null ? appInfo.definitionFor(staticTarget.method.getHolder()) : null;
-      DexClass directTargetHolder =
-          directTarget != null ? appInfo.definitionFor(directTarget.method.getHolder()) : null;
-      DexClass virtualTargetHolder =
-          virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.getHolder()) : null;
-      return (directTarget == null && staticTarget == null && virtualTarget == null)
-          || (virtualTarget != null && virtualTarget.method == item)
-          || (directTarget != null && directTarget.method == item)
-          || (staticTarget != null && staticTarget.method == item)
-          || (directTargetHolder != null && directTargetHolder.isLibraryClass())
-          || (virtualTargetHolder != null && virtualTargetHolder.isLibraryClass())
-          || (staticTargetHolder != null && staticTargetHolder.isLibraryClass());
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder builder = new StringBuilder();
-      renaming.forEach((item, str) -> {
-        if (item instanceof DexType) {
-          builder.append("[c] ");
-        } else if (item instanceof DexMethod) {
-          builder.append("[m] ");
-        } else if (item instanceof DexField) {
-          builder.append("[f] ");
-        }
-        builder.append(item.toSourceString());
-        builder.append(" -> ");
-        builder.append(str.toSourceString());
-        builder.append('\n');
-      });
-      return builder.toString();
+    public boolean bypassDictionary() {
+      return false;
     }
   }
 }
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 d414b3b..a3352ad 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -5,8 +5,9 @@
 
 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.utils.StringUtils;
+import com.android.tools.r8.naming.MemberNameMinifier.MemberNamingStrategy;
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
@@ -27,14 +28,17 @@
   private final DexItemFactory itemFactory;
   private final List<String> dictionary;
   private final Function<ProtoType, KeyType> keyTransform;
+  private final MemberNamingStrategy strategy;
   private final boolean useUniqueMemberNames;
 
   static <S, T extends CachedHashValueDexItem> NamingState<T, S> createRoot(
       DexItemFactory itemFactory,
       List<String> dictionary,
       Function<T, S> keyTransform,
+      MemberNamingStrategy strategy,
       boolean useUniqueMemberNames) {
-    return new NamingState<>(null, itemFactory, dictionary, keyTransform, useUniqueMemberNames);
+    return new NamingState<>(
+        null, itemFactory, dictionary, keyTransform, strategy, useUniqueMemberNames);
   }
 
   private NamingState(
@@ -42,16 +46,19 @@
       DexItemFactory itemFactory,
       List<String> dictionary,
       Function<ProtoType, KeyType> keyTransform,
+      MemberNamingStrategy strategy,
       boolean useUniqueMemberNames) {
     this.parent = parent;
     this.itemFactory = itemFactory;
     this.dictionary = dictionary;
     this.keyTransform = keyTransform;
+    this.strategy = strategy;
     this.useUniqueMemberNames = useUniqueMemberNames;
   }
 
   public NamingState<ProtoType, KeyType> createChild() {
-    return new NamingState<>(this, itemFactory, dictionary, keyTransform, useUniqueMemberNames);
+    return new NamingState<>(
+        this, itemFactory, dictionary, keyTransform, strategy, useUniqueMemberNames);
   }
 
   private InternalState findInternalStateFor(KeyType key) {
@@ -85,12 +92,13 @@
     return state.getAssignedNameFor(name, key);
   }
 
-  public DexString assignNewNameFor(DexString original, ProtoType proto, boolean markAsUsed) {
+  public DexString assignNewNameFor(
+      DexReference source, DexString original, ProtoType proto, boolean markAsUsed) {
     KeyType key = keyTransform.apply(proto);
     DexString result = getAssignedNameFor(original, key);
     if (result == null) {
       InternalState state = getOrCreateInternalStateFor(key);
-      result = state.getNameFor(original, key, markAsUsed);
+      result = state.getNameFor(source, original, key, markAsUsed);
     }
     return result;
   }
@@ -146,7 +154,6 @@
   class InternalState {
 
     private static final int INITIAL_NAME_COUNT = 1;
-    private final char[] EMPTY_CHAR_ARRAY = new char[0];
 
     protected final DexItemFactory itemFactory;
     private final InternalState parentInternalState;
@@ -193,6 +200,10 @@
       reservedNames.add(name);
     }
 
+    public int incrementAndGet() {
+      return nameCount++;
+    }
+
     DexString getAssignedNameFor(DexString original, KeyType proto) {
       DexString result = null;
       if (renamings != null) {
@@ -215,13 +226,14 @@
       return result;
     }
 
-    DexString getNameFor(DexString original, KeyType proto, boolean markAsUsed) {
+    DexString getNameFor(
+        DexReference source, DexString original, KeyType proto, boolean markAsUsed) {
       DexString name = getAssignedNameFor(original, proto);
       if (name != null) {
         return name;
       }
       do {
-        name = itemFactory.createString(nextSuggestedName());
+        name = nextSuggestedName(source);
       } while (!isAvailable(name));
       if (markAsUsed) {
         addRenaming(original, proto, name);
@@ -236,11 +248,11 @@
       renamings.put(original, proto, newName);
     }
 
-    String nextSuggestedName() {
-      if (dictionaryIterator.hasNext()) {
-        return dictionaryIterator.next();
+    DexString nextSuggestedName(DexReference source) {
+      if (!strategy.bypassDictionary() && dictionaryIterator.hasNext()) {
+        return itemFactory.createString(dictionaryIterator.next());
       } else {
-        return StringUtils.numberToIdentifier(EMPTY_CHAR_ARRAY, nameCount++, false);
+        return strategy.next(source, this);
       }
     }
 
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
index 139000e..698cf50 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
@@ -7,6 +7,8 @@
 import static org.junit.Assert.assertEquals;
 
 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.origin.Origin;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -39,13 +41,18 @@
     String expectedOutput = StringUtils.lines("I.m()", "JImpl.m()");
 
     byte[] dex = buildProgramDexFileData();
-    AndroidApp app =
-        AndroidApp.builder().addDexProgramData(buildProgramDexFileData(), Origin.unknown()).build();
-    assertEquals(expectedOutput, runOnArt(app, "TestClass"));
+    if (ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1)) {
+      AndroidApp app =
+          AndroidApp.builder()
+              .addDexProgramData(buildProgramDexFileData(), Origin.unknown())
+              .build();
+      assertEquals(expectedOutput, runOnArt(app, "TestClass"));
+    }
 
     testForR8(Backend.DEX)
         .addProgramDexFileData(dex)
         .addKeepMainRule("TestClass")
+        .setMinApi(AndroidApiLevel.M)
         .run("TestClass")
         .assertSuccessWithOutput(expectedOutput);
   }
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index b99cbdc..3e3fd4e 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -105,6 +105,7 @@
     SingleFileConsumer consumer = new SingleFileConsumer();
     AndroidApp app = AndroidApp.builder().addDexProgramData(data, Origin.unknown()).build();
     InternalOptions options = new InternalOptions();
+    options.minApiLevel = apiLevel;
     options.programConsumer = consumer;
     ExecutorService executor = ThreadUtils.getExecutorService(1);
     try {
diff --git a/tools/archive.py b/tools/archive.py
index 5c0c231..687844a 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -8,6 +8,7 @@
 import jdk
 import optparse
 import os
+import resource
 import shutil
 import subprocess
 import sys
@@ -90,16 +91,21 @@
 def GetMavenUrl(is_master):
   return GetVersionDestination('http://storage.googleapis.com/', '', is_master)
 
+def PrintResourceInfo():
+  (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
+  print('INFO: Open files soft limit: %s' % soft)
+  print('INFO: Open files hard limit: %s' % hard)
+
 def Main():
   (options, args) = ParseOptions()
-  # TODO(126871526): Fix the is_bot check.
-  # if not utils.is_bot() and not options.dry_run:
-  #   raise Exception('You are not a bot, don\'t archive builds')
+  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
 
+  PrintResourceInfo()
   # Create maven release which uses a build that exclude dependencies.
   create_maven_release.main(["--out", utils.LIBS])