Support for package based startup classes distribution

Change-Id: I776a4f9a35f37bc7b1f1cdb0d8d17edaaf25f134
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 65c0f2e..75c4f9d 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -1259,7 +1259,7 @@
     }
 
     // Start a new iteration over all files, starting at the current one.
-    void restart() {
+    public void restart() {
       activeFiles = Iterators.limit(allFilesCyclic, filesForDistribution.size());
     }
 
@@ -1299,7 +1299,7 @@
    * <p>The populator cycles through the files until all classes have been successfully placed and
    * adds new files to the passed in map if it can't fit in the existing files.
    */
-  private static class PackageSplitPopulator {
+  public static class PackageSplitPopulator {
 
     static class PackageSplitClassPartioning {
 
@@ -1442,8 +1442,7 @@
 
     public void run() {
       addStartupClasses();
-      List<DexProgramClass> nonPackageClasses = addNonStartupClasses();
-      addNonPackageClasses(cycler, nonPackageClasses);
+      distributeClasses(classPartioning.getNonStartupClasses());
     }
 
     private void addStartupClasses() {
@@ -1469,7 +1468,7 @@
 
         // If the above failed, then apply the selected multi startup dex distribution strategy.
         MultiStartupDexDistributor distributor = MultiStartupDexDistributor.get(options);
-        distributor.distribute(classPartioning.getStartupClasses(), cycler, virtualFile);
+        distributor.distribute(classPartioning.getStartupClasses(), this, virtualFile, cycler);
 
         options.reporter.warning(
             createStartupClassesOverflowDiagnostic(cycler.filesForDistribution.size()));
@@ -1493,14 +1492,18 @@
       }
     }
 
+    public void distributeClasses(List<DexProgramClass> classes) {
+      List<DexProgramClass> nonPackageClasses = addPackageClasses(classes);
+      addNonPackageClasses(cycler, nonPackageClasses);
+    }
+
     @SuppressWarnings("ReferenceEquality")
-    private List<DexProgramClass> addNonStartupClasses() {
+    private List<DexProgramClass> addPackageClasses(List<DexProgramClass> classes) {
       int prefixLength = MINIMUM_PREFIX_LENGTH;
       int transactionStartIndex = 0;
       String currentPrefix = null;
       Object2IntMap<String> packageAssignments = new Object2IntOpenHashMap<>();
       VirtualFile current = cycler.ensureFile().next();
-      List<DexProgramClass> classes = classPartioning.getNonStartupClasses();
       List<DexProgramClass> nonPackageClasses = new ArrayList<>();
       for (int classIndex = 0; classIndex < classes.size(); classIndex++) {
         DexProgramClass clazz = classes.get(classIndex);
diff --git a/src/main/java/com/android/tools/r8/profile/startup/distribution/MultiStartupDexDistributor.java b/src/main/java/com/android/tools/r8/profile/startup/distribution/MultiStartupDexDistributor.java
index 471ea08..6a3132c 100644
--- a/src/main/java/com/android/tools/r8/profile/startup/distribution/MultiStartupDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/profile/startup/distribution/MultiStartupDexDistributor.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.profile.startup.distribution;
 
 import com.android.tools.r8.dex.VirtualFile;
+import com.android.tools.r8.dex.VirtualFile.PackageSplitPopulator;
 import com.android.tools.r8.dex.VirtualFile.VirtualFileCycler;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -14,7 +15,10 @@
 public abstract class MultiStartupDexDistributor {
 
   public abstract void distribute(
-      List<DexProgramClass> classes, VirtualFileCycler cycler, VirtualFile virtualFile);
+      List<DexProgramClass> classes,
+      PackageSplitPopulator packageSplitPopulator,
+      VirtualFile virtualFile,
+      VirtualFileCycler virtualFileCycler);
 
   boolean hasSpaceForTransaction(VirtualFile virtualFile) {
     return !virtualFile.isFull();
@@ -37,7 +41,7 @@
       case "classByNumberOfStartupMethodsMinusNumberOfNonStartupMethods":
         throw new Unimplemented();
       case "packageByName":
-        throw new Unimplemented();
+        return new PackageByNameMultiStartupDexDistributor();
       case "packageByNumberOfStartupMethods":
         throw new Unimplemented();
       default:
@@ -50,7 +54,10 @@
 
     @Override
     public void distribute(
-        List<DexProgramClass> classes, VirtualFileCycler cycler, VirtualFile virtualFile) {
+        List<DexProgramClass> classes,
+        PackageSplitPopulator packageSplitPopulator,
+        VirtualFile virtualFile,
+        VirtualFileCycler virtualFileCycler) {
       // Add the (already sorted) startup classes one by one.
       for (DexProgramClass startupClass : classes) {
         virtualFile.addClass(startupClass);
@@ -58,7 +65,7 @@
           virtualFile.commitTransaction();
         } else {
           virtualFile.abortTransaction();
-          virtualFile = cycler.addFile();
+          virtualFile = virtualFileCycler.addFile();
           virtualFile.addClass(startupClass);
           assert hasSpaceForTransaction(virtualFile);
           virtualFile.commitTransaction();
@@ -66,4 +73,17 @@
       }
     }
   }
+
+  private static class PackageByNameMultiStartupDexDistributor extends MultiStartupDexDistributor {
+
+    @Override
+    public void distribute(
+        List<DexProgramClass> classes,
+        PackageSplitPopulator packageSplitPopulator,
+        VirtualFile virtualFile,
+        VirtualFileCycler virtualFileCycler) {
+      virtualFileCycler.restart();
+      packageSplitPopulator.distributeClasses(classes);
+    }
+  }
 }