Merge "Refactor name minification into strategies"
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/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index f0f4011..9aa8502 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.ClassNameMinifier.ClassNamingStrategy;
@@ -13,7 +14,9 @@
 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.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;
@@ -60,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);
@@ -129,4 +135,26 @@
       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 next(DexReference dexReference, InternalState internalState) {
+      int counter = internalState.incrementAndGet();
+      return factory.createString(StringUtils.numberToIdentifier(EMPTY_CHAR_ARRAY, counter, false));
+    }
+
+    @Override
+    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);
       }
     }