1418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
2418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager// for details. All rights reserved. Use of this source code is governed by a
3418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager// BSD-style license that can be found in the LICENSE file.
4418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerpackage com.android.tools.r8.dex;
5418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
6418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.errors.CompilationError;
7418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.errors.InternalCompilerError;
8418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexApplication;
9418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexCallSite;
10418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexClass;
11418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexField;
12418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexItem;
13418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexItemFactory;
14418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexMethod;
15418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexMethodHandle;
16418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexProgramClass;
17418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexProto;
18418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexString;
19418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexType;
20418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.IndexedDexItem;
21418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.ObjectToOffsetMapping;
22418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.naming.ClassNameMapper;
23418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.naming.NamingLens;
24418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.utils.DescriptorUtils;
25418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.utils.FileUtils;
26418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.utils.PackageDistribution;
27418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.utils.ThreadUtils;
28418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.google.common.collect.Iterators;
29854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesseimport com.google.common.collect.Maps;
30418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.google.common.collect.Sets;
31418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.io.IOException;
32418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.io.OutputStreamWriter;
33418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.io.Writer;
34418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.nio.file.Path;
35418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.nio.file.Paths;
36418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.ArrayList;
37418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.Collection;
38418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.Collections;
39418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.HashMap;
40f19dfabc704b3975d66019c0817d65fc48921284Denis Vnukovimport java.util.HashSet;
41418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.Iterator;
42418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.LinkedHashMap;
43418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.LinkedHashSet;
44418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.List;
45418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.Map;
46418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.Map.Entry;
47418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.Set;
48418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.TreeSet;
49418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.concurrent.Callable;
50418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.concurrent.ExecutionException;
51418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.concurrent.ExecutorService;
52418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.concurrent.Future;
53418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.function.Function;
54418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
55418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerpublic class VirtualFile {
56418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
57854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse  // The fill strategy determine how to distribute classes into dex files.
585af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse  enum FillStrategy {
59854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    // Only put classes matches by the main dex rules into the first dex file. Distribute remaining
60854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    // classes in additional dex files filling each dex file as much as possible.
61854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    MINIMAL_MAIN_DEX,
62854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    // Distribute classes in as few dex files as possible filling each dex file as much as possible.
635af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse    FILL_MAX,
64854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    // Distribute classes keeping some space for future growth. This is mainly useful together with
65854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    // the package map distribution.
665af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse    LEAVE_SPACE_FOR_GROWTH,
67854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    // TODO(sgjesse): Does "minimal main dex" combined with "leave space for growth" make sense?
685af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse  }
695af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse
7002cc4ebce7deb01a5509624f70e7d667d5f4e2c8Ivan Gavrilovic  private static final int MAX_ENTRIES = Constants.U16BIT_MAX + 1;
71418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  /**
72418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   * When distributing classes across files we aim to leave some space. The amount of space left is
73418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   * driven by this constant.
74418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   */
75418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private static final int MAX_PREFILL_ENTRIES = MAX_ENTRIES - 5000;
76418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
77418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private final int id;
78418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private final VirtualFileIndexedItemCollection indexedItems;
79418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private final IndexedItemTransaction transaction;
80418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
81418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private VirtualFile(int id, NamingLens namingLens) {
82418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    this.id = id;
83418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    this.indexedItems = new VirtualFileIndexedItemCollection(id);
84418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    this.transaction = new IndexedItemTransaction(indexedItems, namingLens);
85418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
86418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
87854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse  public int getId() {
88854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    return id;
89854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse  }
90854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
91f19dfabc704b3975d66019c0817d65fc48921284Denis Vnukov  public Set<String> getClassDescriptors() {
92cf03facd65c934a2cd76562114646423ac200294Denis Vnukov    Set<String> classDescriptors = new HashSet<>();
93cf03facd65c934a2cd76562114646423ac200294Denis Vnukov    for (DexProgramClass clazz : indexedItems.classes) {
94cf03facd65c934a2cd76562114646423ac200294Denis Vnukov      boolean added = classDescriptors.add(clazz.type.descriptor.toString());
95cf03facd65c934a2cd76562114646423ac200294Denis Vnukov      assert added;
96cf03facd65c934a2cd76562114646423ac200294Denis Vnukov    }
97f19dfabc704b3975d66019c0817d65fc48921284Denis Vnukov    return classDescriptors;
98f19dfabc704b3975d66019c0817d65fc48921284Denis Vnukov  }
99f19dfabc704b3975d66019c0817d65fc48921284Denis Vnukov
100418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public static String deriveCommonPrefixAndSanityCheck(List<String> fileNames) {
101418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    Iterator<String> nameIterator = fileNames.iterator();
102418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    String first = nameIterator.next();
103418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    if (!first.toLowerCase().endsWith(FileUtils.DEX_EXTENSION)) {
104418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      throw new RuntimeException("Illegal suffix for dex file: `" + first + "`.");
105418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
106418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    String prefix = first.substring(0, first.length() - FileUtils.DEX_EXTENSION.length());
107418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    int index = 2;
108418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    while (nameIterator.hasNext()) {
109418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      String next = nameIterator.next();
110418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (!next.toLowerCase().endsWith(FileUtils.DEX_EXTENSION)) {
111418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        throw new RuntimeException("Illegal suffix for dex file: `" + first + "`.");
112418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
113418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (!next.startsWith(prefix)) {
114418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        throw new RuntimeException("Input filenames lack common prefix.");
115418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
116418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      String numberPart = next.substring(prefix.length(), next.length() - FileUtils.DEX_EXTENSION.length());
117418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (Integer.parseInt(numberPart) != index++) {
118418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        throw new RuntimeException("DEX files are not numbered consecutively.");
119418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
120418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
121418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return prefix;
122418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
123418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
124418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private static Map<DexProgramClass, String> computeOriginalNameMapping(
125418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      Collection<DexProgramClass> classes,
126418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      ClassNameMapper proguardMap) {
127418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    Map<DexProgramClass, String> originalNames = new HashMap<>();
128418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    classes.forEach((DexProgramClass c) ->
129418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        originalNames.put(c,
130418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            DescriptorUtils.descriptorToJavaType(c.type.toDescriptorString(), proguardMap)));
131418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return originalNames;
132418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
133418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
134418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private static String extractPrefixToken(int prefixLength, String className, boolean addStar) {
135418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    int index = 0;
136418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    int lastIndex = 0;
137418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    int segmentCount = 0;
138418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    while (lastIndex != -1 && segmentCount++ < prefixLength) {
139418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      index = lastIndex;
140418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      lastIndex = className.indexOf('.', index + 1);
141418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
142418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    String prefix = className.substring(0, index);
143418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    if (addStar && segmentCount >= prefixLength) {
144418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      // Full match, add a * to also match sub-packages.
145418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      prefix += ".*";
146418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
147418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return prefix;
148418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
149418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
150418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public ObjectToOffsetMapping computeMapping(DexApplication application) {
151418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    assert transaction.isEmpty();
152418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return new ObjectToOffsetMapping(
153418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        id,
154418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        application,
155418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        indexedItems.classes.toArray(new DexProgramClass[indexedItems.classes.size()]),
156418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        indexedItems.protos.toArray(new DexProto[indexedItems.protos.size()]),
157418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        indexedItems.types.toArray(new DexType[indexedItems.types.size()]),
158418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        indexedItems.methods.toArray(new DexMethod[indexedItems.methods.size()]),
159418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        indexedItems.fields.toArray(new DexField[indexedItems.fields.size()]),
160418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        indexedItems.strings.toArray(new DexString[indexedItems.strings.size()]),
161418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        indexedItems.callSites.toArray(new DexCallSite[indexedItems.callSites.size()]),
162418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        indexedItems.methodHandles.toArray(new DexMethodHandle[indexedItems.methodHandles.size()]));
163418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
164418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
165418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private void addClass(DexProgramClass clazz) {
166418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    transaction.addClassAndDependencies(clazz);
167418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
168418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
169418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private static boolean isFull(int numberOfMethods, int numberOfFields, int maximum) {
170418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return (numberOfMethods > maximum) || (numberOfFields > maximum);
171418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
172418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
173418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private boolean isFull() {
174418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return isFull(transaction.getNumberOfMethods(), transaction.getNumberOfFields(), MAX_ENTRIES);
175418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
176418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
1775af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse  private boolean isFilledEnough(FillStrategy fillStrategy) {
178418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return isFull(
179418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        transaction.getNumberOfMethods(),
180418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        transaction.getNumberOfFields(),
1815af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse        fillStrategy == FillStrategy.FILL_MAX ? MAX_ENTRIES : MAX_PREFILL_ENTRIES);
182418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
183418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
184418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public void abortTransaction() {
185418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    transaction.abort();
186418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
187418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
188418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public void commitTransaction() {
189418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    transaction.commit();
190418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
191418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
192418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public boolean isEmpty() {
193418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return indexedItems.classes.isEmpty();
194418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
195418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
196418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public List<DexProgramClass> classes() {
197418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return indexedItems.classes;
198418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
199418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
2006b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse  public abstract static class Distributor {
2016b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    protected final DexApplication application;
2026b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    protected final ApplicationWriter writer;
203854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    protected final Map<Integer, VirtualFile> nameToFileMap = new HashMap<>();
204e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse
2056b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    public Distributor(ApplicationWriter writer) {
2066b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      this.application = writer.application;
207e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      this.writer = writer;
2086b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    }
2096b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
2106b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    public abstract Map<Integer, VirtualFile> run() throws ExecutionException, IOException;
2116b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse  }
2126b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
2136b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse  public static class FilePerClassDistributor extends Distributor {
2146b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
2156b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    public FilePerClassDistributor(ApplicationWriter writer) {
2166b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      super(writer);
217e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse    }
218e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse
219e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse    public Map<Integer, VirtualFile> run() throws ExecutionException, IOException {
2206b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      // Assign dedicated virtual files for all program classes.
2216b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      for (DexProgramClass clazz : application.classes()) {
2226b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        VirtualFile file = new VirtualFile(nameToFileMap.size(), writer.namingLens);
2236b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        nameToFileMap.put(nameToFileMap.size(), file);
2246b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        file.addClass(clazz);
2256b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        file.commitTransaction();
226e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      }
2276b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      return nameToFileMap;
2286b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    }
2296b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse  }
230e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse
2316b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse  public abstract static class DistributorBase extends Distributor {
2326b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    protected Set<DexProgramClass> classes;
2336b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    protected Map<DexProgramClass, String> originalNames;
234e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse
2356b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    public DistributorBase(ApplicationWriter writer) {
2366b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      super(writer);
237e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse
2386b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      classes = Sets.newHashSet(application.classes());
2396b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      originalNames = computeOriginalNameMapping(classes, application.getProguardMap());
2406b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    }
2416b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
2426b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    protected void fillForMainDexList(Set<DexProgramClass> classes) {
243e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      if (application.mainDexList != null) {
244e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse        VirtualFile mainDexFile = nameToFileMap.get(0);
245e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse        for (DexType type : application.mainDexList) {
246e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse          DexClass clazz = application.definitionFor(type);
247e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse          if (clazz != null && clazz.isProgramClass()) {
248e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse            DexProgramClass programClass = (DexProgramClass) clazz;
249e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse            mainDexFile.addClass(programClass);
250e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse            if (mainDexFile.isFull()) {
251e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse              throw new CompilationError("Cannot fit requested classes in main-dex file.");
252e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse            }
253e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse            classes.remove(programClass);
254e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse          } else {
255e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse            System.out.println(
256e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse                "WARNING: Application does not contain `"
257e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse                    + type.toSourceString()
258e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse                    + "` as referenced in main-dex-list.");
259e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse          }
260e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse          mainDexFile.commitTransaction();
261e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse        }
262e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      }
2636b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    }
2646b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
2656b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    TreeSet<DexProgramClass> sortClassesByPackage(Set<DexProgramClass> classes,
2666b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        Map<DexProgramClass, String> originalNames) {
2676b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      TreeSet<DexProgramClass> sortedClasses = new TreeSet<>(
2686b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse          (DexProgramClass a, DexProgramClass b) -> {
2696b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            String originalA = originalNames.get(a);
2706b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            String originalB = originalNames.get(b);
2716b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            int indexA = originalA.lastIndexOf('.');
2726b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            int indexB = originalB.lastIndexOf('.');
2736b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            if (indexA == -1 && indexB == -1) {
2746b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse              // Empty package, compare the class names.
2756b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse              return originalA.compareTo(originalB);
2766b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            }
2776b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            if (indexA == -1) {
2786b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse              // Empty package name comes first.
2796b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse              return -1;
2806b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            }
2816b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            if (indexB == -1) {
2826b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse              // Empty package name comes first.
2836b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse              return 1;
2846b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            }
2856b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            String prefixA = originalA.substring(0, indexA);
2866b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            String prefixB = originalB.substring(0, indexB);
2876b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            int result = prefixA.compareTo(prefixB);
2886b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            if (result != 0) {
2896b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse              return result;
2906b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            }
2916b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse            return originalA.compareTo(originalB);
2926b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse          });
2936b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      sortedClasses.addAll(classes);
2946b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      return sortedClasses;
2956b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    }
2966b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse  }
2976b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
2986b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse  public static class FillFilesDistributor extends DistributorBase {
299854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private final FillStrategy fillStrategy;
300854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
301854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    public FillFilesDistributor(ApplicationWriter writer, boolean minimalMainDex) {
3026b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      super(writer);
303854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      this.fillStrategy = minimalMainDex ? FillStrategy.MINIMAL_MAIN_DEX : FillStrategy.FILL_MAX;
3046b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    }
3056b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
3066b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    public Map<Integer, VirtualFile> run() throws ExecutionException, IOException {
3076b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      // Strategy for distributing classes for write out:
3086b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      // 1. Place the remaining files based on their packages in sorted order.
3096b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
3106b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      // Start with 1 file. The package populator will add more if needed.
3116b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      nameToFileMap.put(0, new VirtualFile(0, writer.namingLens));
3126b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
3136b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      // First fill required classes into the main dex file.
3146b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      fillForMainDexList(classes);
315e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse
316854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      // Sort the remaining classes based on the original names.
317e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      // This with make classes from the same package be adjacent.
318e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      classes = sortClassesByPackage(classes, originalNames);
319e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse
3206b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      new PackageSplitPopulator(
3216b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse          nameToFileMap, classes, originalNames, null, application.dexItemFactory,
322854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse          fillStrategy, writer.namingLens)
3236b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse          .call();
3246b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      return nameToFileMap;
3256b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    }
3266b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse  }
3276b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
328c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel  public static class MonoDexDistributor extends DistributorBase {
329c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel    public MonoDexDistributor(ApplicationWriter writer) {
330c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel      super(writer);
331c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel    }
332c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel
333c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel    @Override
334c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel    public Map<Integer, VirtualFile> run() throws ExecutionException, IOException {
335c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel      VirtualFile mainDexFile = new VirtualFile(0, writer.namingLens);
336c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel      nameToFileMap.put(0, mainDexFile);
337c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel
338c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel      for (DexProgramClass programClass : classes) {
339c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel        mainDexFile.addClass(programClass);
340c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel        if (mainDexFile.isFull()) {
341c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel          throw new CompilationError("Cannot fit all classes in a single dex file.");
342c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel        }
343c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel      }
344c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel      mainDexFile.commitTransaction();
345c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel      return nameToFileMap;
346c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel    }
347c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel  }
348c80dd12efaa1c8188037a240f63a7ad738a17580Yohann Roussel
3496b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse  public static class PackageMapDistributor extends DistributorBase {
3506b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    private final PackageDistribution packageDistribution;
3516b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    private final ExecutorService executorService;
3526b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
3536b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    public PackageMapDistributor(
3546b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        ApplicationWriter writer,
3556b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        PackageDistribution packageDistribution,
3566b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        ExecutorService executorService) {
3576b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      super(writer);
3586b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      this.packageDistribution = packageDistribution;
3596b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      this.executorService = executorService;
3606b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    }
3616b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
3626b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    public Map<Integer, VirtualFile> run() throws ExecutionException, IOException {
3636b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      // Strategy for distributing classes for write out:
3646b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      // 1. Place all files in the package distribution file in the proposed files (if any).
3656b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      // 2. Place the remaining files based on their packages in sorted order.
3666b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
3676b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      int maxReferencedIndex = packageDistribution.maxReferencedIndex();
3686b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      for (int index = 0; index <= maxReferencedIndex; index++) {
3696b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        VirtualFile file = new VirtualFile(index, writer.namingLens);
3706b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        nameToFileMap.put(index, file);
371e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      }
372e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse
3736b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      // First fill required classes into the main dex file.
3746b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      fillForMainDexList(classes);
3756b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
376854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      // Sort the remaining classes based on the original names.
3776b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      // This with make classes from the same package be adjacent.
3786b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      classes = sortClassesByPackage(classes, originalNames);
3796b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
3806b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      Set<String> usedPrefixes = fillForDistribution(classes, originalNames);
3816b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
382e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      // TODO(zerny): Add the package map to AndroidApp and refactor its generation.
383e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      Map<String, Integer> newAssignments;
384e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      if (classes.isEmpty()) {
385e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse        newAssignments = Collections.emptyMap();
386e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      } else {
387e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse        newAssignments =
388e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse            new PackageSplitPopulator(
389e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse                nameToFileMap, classes, originalNames, usedPrefixes, application.dexItemFactory,
3905af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse                FillStrategy.LEAVE_SPACE_FOR_GROWTH, writer.namingLens)
391e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse                .call();
392e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse        if (!newAssignments.isEmpty() && nameToFileMap.size() > 1) {
3936b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse          System.err.println(" * The used package map is missing entries. The following default "
3946b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse              + "mappings have been used:");
3956b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse          writeAssignments(newAssignments, new OutputStreamWriter(System.err));
3966b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse          System.err.println(" * Consider updating the map.");
397e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse        }
398e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      }
3996b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
4006b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      Path newPackageMap = Paths.get("package.map");
4016b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      System.out.println(" - " + newPackageMap.toString());
4026b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      PackageDistribution.writePackageToFileMap(newPackageMap, newAssignments, packageDistribution);
4036b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
404e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse      return nameToFileMap;
405e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse    }
4066b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
4076b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    private Set<String> fillForDistribution(Set<DexProgramClass> classes,
4086b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        Map<DexProgramClass, String> originalNames) throws ExecutionException {
4096b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      Set<String> usedPrefixes = null;
4106b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      if (packageDistribution != null) {
4116b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        ArrayList<Future<List<DexProgramClass>>> futures = new ArrayList<>(nameToFileMap.size());
4126b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        usedPrefixes = packageDistribution.getFiles();
4136b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        for (VirtualFile file : nameToFileMap.values()) {
4146b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse          PackageMapPopulator populator =
4156b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse              new PackageMapPopulator(file, classes, packageDistribution, originalNames);
4166b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse          futures.add(executorService.submit(populator));
4176b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        }
4186b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        ThreadUtils.awaitFutures(futures).forEach(classes::removeAll);
4196b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      }
4206b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      return usedPrefixes;
4216b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    }
4226b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse
4236b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    private void writeAssignments(Map<String, Integer> assignments, Writer output)
4246b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        throws IOException{
4256b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      for (Entry<String, Integer> entry : assignments.entrySet()) {
4266b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        output.write("    ");
4276b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        PackageDistribution.formatEntry(entry, output);
4286b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse        output.write("\n");
4296b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      }
4306b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse      output.flush();
4316b8d5aa58746674200e94f9827aa54b39ce74fdaSøren Gjesse    }
432e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse  }
433e74fee3f365ac52a0210c1df5515a806e021d17aSøren Gjesse
434418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private static class VirtualFileIndexedItemCollection implements IndexedItemCollection {
435418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
436418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    final int id;
437418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
438418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final List<DexProgramClass> classes = new ArrayList<>();
439418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final List<DexProto> protos = new ArrayList<>();
440418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final List<DexType> types = new ArrayList<>();
441418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final List<DexMethod> methods = new ArrayList<>();
442418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final List<DexField> fields = new ArrayList<>();
443418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final List<DexString> strings = new ArrayList<>();
444418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final List<DexCallSite> callSites = new ArrayList<>();
445418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final List<DexMethodHandle> methodHandles = new ArrayList<>();
446418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
447418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Set<DexClass> seenClasses = Sets.newIdentityHashSet();
448418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
449418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private VirtualFileIndexedItemCollection(int id) {
450418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.id = id;
451418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
452418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
453418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private <T extends IndexedDexItem> boolean addItem(T item, List<T> itemList) {
454418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      assert item != null;
455418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (item.assignToVirtualFile(id)) {
456418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        itemList.add(item);
457418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return true;
458418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
459418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return false;
460418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
461418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
462418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
463418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addClass(DexProgramClass clazz) {
464418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (seenClasses.add(clazz)) {
465418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        classes.add(clazz);
466418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return true;
467418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
468418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return false;
469418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
470418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
471418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
472418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addField(DexField field) {
473418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return addItem(field, fields);
474418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
475418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
476418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
477418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addMethod(DexMethod method) {
478418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return addItem(method, methods);
479418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
480418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
481418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
482418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addString(DexString string) {
483418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return addItem(string, strings);
484418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
485418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
486418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
487418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addProto(DexProto proto) {
488418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return addItem(proto, protos);
489418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
490418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
491418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
492418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addCallSite(DexCallSite callSite) {
493418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return addItem(callSite, callSites);
494418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
495418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
496418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
497418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addMethodHandle(DexMethodHandle methodHandle) {
498418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return addItem(methodHandle, methodHandles);
499418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
500418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
501418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
502418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addType(DexType type) {
503418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return addItem(type, types);
504418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
505418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
506418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public int getNumberOfMethods() {
507418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return methods.size();
508418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
509418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
510418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public int getNumberOfFields() {
511418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return fields.size();
512418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
513418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
514418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public int getNumberOfStrings() {
515418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return strings.size();
516418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
517418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
518418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
519418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private static class IndexedItemTransaction implements IndexedItemCollection {
520418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
521418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final VirtualFileIndexedItemCollection base;
522418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final NamingLens namingLens;
523418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
524418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Set<DexProgramClass> classes = new LinkedHashSet<>();
525418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Set<DexField> fields = new LinkedHashSet<>();
526418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Set<DexMethod> methods = new LinkedHashSet<>();
527418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Set<DexType> types = new LinkedHashSet<>();
528418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Set<DexProto> protos = new LinkedHashSet<>();
529418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Set<DexString> strings = new LinkedHashSet<>();
530418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Set<DexCallSite> callSites = new LinkedHashSet<>();
531418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Set<DexMethodHandle> methodHandles = new LinkedHashSet<>();
532418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
533418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private IndexedItemTransaction(VirtualFileIndexedItemCollection base,
534418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        NamingLens namingLens) {
535418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.base = base;
536418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.namingLens = namingLens;
537418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
538418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
539418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private <T extends IndexedDexItem> boolean maybeInsert(T item, Set<T> set) {
540418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (item.hasVirtualFileData(base.id) || set.contains(item)) {
541418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return false;
542418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
543418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      set.add(item);
544418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return true;
545418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
546418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
547418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    void addClassAndDependencies(DexProgramClass clazz) {
548418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      clazz.collectIndexedItems(this);
549418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
550418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
551418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
552418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addClass(DexProgramClass dexProgramClass) {
553418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (base.seenClasses.contains(dexProgramClass) || classes.contains(dexProgramClass)) {
554418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return false;
555418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
556418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      classes.add(dexProgramClass);
557418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return true;
558418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
559418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
560418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
561418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addField(DexField field) {
562418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return maybeInsert(field, fields);
563418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
564418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
565418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
566418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addMethod(DexMethod method) {
567418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return maybeInsert(method, methods);
568418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
569418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
570418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
571418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addString(DexString string) {
572418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return maybeInsert(string, strings);
573418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
574418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
575418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
576418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addProto(DexProto proto) {
577418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return maybeInsert(proto, protos);
578418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
579418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
580418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
581418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addType(DexType type) {
582418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return maybeInsert(type, types);
583418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
584418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
585418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
586418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addCallSite(DexCallSite callSite) {
587418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return maybeInsert(callSite, callSites);
588418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
589418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
590418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
591418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean addMethodHandle(DexMethodHandle methodHandle) {
592418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return maybeInsert(methodHandle, methodHandles);
593418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
594418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
595418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
596418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public DexString getRenamedDescriptor(DexType type) {
597418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return namingLens.lookupDescriptor(type);
598418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
599418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
600418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
601418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public DexString getRenamedName(DexMethod method) {
602418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      assert namingLens.checkTargetCanBeTranslated(method);
603418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return namingLens.lookupName(method);
604418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
605418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
606418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
607418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public DexString getRenamedName(DexField field) {
608418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return namingLens.lookupName(field);
609418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
610418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
611418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    int getNumberOfMethods() {
612418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return methods.size() + base.getNumberOfMethods();
613418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
614418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
615418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    int getNumberOfFields() {
616418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return fields.size() + base.getNumberOfFields();
617418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
618418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
619418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private <T extends DexItem> void commitItemsIn(Set<T> set, Function<T, Boolean> hook) {
620418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      set.forEach((item) -> {
621418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        boolean newlyAdded = hook.apply(item);
622418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        assert newlyAdded;
623418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      });
624418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      set.clear();
625418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
626418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
627418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    void commit() {
628418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      commitItemsIn(classes, base::addClass);
629418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      commitItemsIn(fields, base::addField);
630418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      commitItemsIn(methods, base::addMethod);
631418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      commitItemsIn(protos, base::addProto);
632418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      commitItemsIn(types, base::addType);
633418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      commitItemsIn(strings, base::addString);
634418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      commitItemsIn(callSites, base::addCallSite);
635418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      commitItemsIn(methodHandles, base::addMethodHandle);
636418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
637418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
638418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    void abort() {
639418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      classes.clear();
640418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      fields.clear();
641418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      methods.clear();
642418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      protos.clear();
643418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      types.clear();
644418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      strings.clear();
645418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
646418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
647418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public boolean isEmpty() {
648418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return classes.isEmpty() && fields.isEmpty() && methods.isEmpty() && protos.isEmpty()
649418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          && types.isEmpty() && strings.isEmpty();
650418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
651418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
652418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    int getNumberOfStrings() {
653418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return strings.size() + base.getNumberOfStrings();
654418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
655418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
656418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    int getNumberOfClasses() {
657418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return classes.size() + base.classes.size();
658418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
659418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
660418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
661418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  /**
662418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   * Adds all classes from the given set that are covered by a corresponding package map
663418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   * specification to the given file.
664418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   */
665418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private static class PackageMapPopulator implements Callable<List<DexProgramClass>> {
666418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
667418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final VirtualFile file;
668418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Collection<DexProgramClass> classes;
669418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final PackageDistribution packageDistribution;
670418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Map<DexProgramClass, String> originalNames;
671418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
672418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    PackageMapPopulator(
673418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        VirtualFile file,
674418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        Collection<DexProgramClass> classes,
675418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        PackageDistribution packageDistribution,
676418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        Map<DexProgramClass, String> originalNames) {
677418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.file = file;
678418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.classes = classes;
679418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.packageDistribution = packageDistribution;
680418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.originalNames = originalNames;
681418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
682418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
683ca1299205e0a907ebe029c0461801a1c912d28c4Benoit Lamarche    @Override
684418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public List<DexProgramClass> call() {
685418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      String currentPrefix = null;
686418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      int currentFileId = -1;
687418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      List<DexProgramClass> inserted = new ArrayList<>();
688418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      for (DexProgramClass clazz : classes) {
689418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        String originalName = originalNames.get(clazz);
690418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        assert originalName != null;
691418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (!coveredByPrefix(originalName, currentPrefix)) {
692418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          if (currentPrefix != null) {
693418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            file.commitTransaction();
694418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          }
695418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          currentPrefix = lookupPrefixFor(originalName);
696418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          if (currentPrefix == null) {
697418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            currentFileId = -1;
698418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          } else {
699418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            currentFileId = packageDistribution.get(currentPrefix);
700418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          }
701418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
702418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (currentFileId == file.id) {
703418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          file.addClass(clazz);
704418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          inserted.add(clazz);
705418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
706418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (file.isFull()) {
707ca1299205e0a907ebe029c0461801a1c912d28c4Benoit Lamarche          throw new CompilationError(
708418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              "Cannot fit package " + currentPrefix
709ca1299205e0a907ebe029c0461801a1c912d28c4Benoit Lamarche                  + " in requested dex file, consider removing mapping.");
710418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
711418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
712418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      file.commitTransaction();
713418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return inserted;
714418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
715418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
716418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private String lookupPrefixFor(String originalName) {
717418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      // First, check whether we have a match on the full package name.
718418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      int lastIndexOfDot = originalName.lastIndexOf('.');
719418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (lastIndexOfDot < 0) {
720418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return null;
721418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
722418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      String prefix = originalName.substring(0, lastIndexOfDot);
723418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (packageDistribution.containsFile(prefix)) {
724418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return prefix;
725418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
726418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      // Second, look for .* qualified entries.
727418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      int index;
728418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      prefix = originalName;
729418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      while ((index = prefix.lastIndexOf('.')) != -1) {
730418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        prefix = prefix.substring(0, index);
731418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (packageDistribution.containsFile(prefix + ".*")) {
732418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          return prefix + ".*";
733418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
734418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
735418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return null;
736418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
737418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
738418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    static boolean coveredByPrefix(String originalName, String currentPrefix) {
739418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (currentPrefix == null) {
740418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return false;
741418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
742418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (currentPrefix.endsWith(".*")) {
743418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return originalName.startsWith(currentPrefix.substring(0, currentPrefix.length() - 2));
744418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else {
745418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return originalName.startsWith(currentPrefix)
746418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            && originalName.lastIndexOf('.') == currentPrefix.length();
747418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
748418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
749418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
750418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
751418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  /**
752854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse   * Helper class to cycle through the set of virtual files.
753854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse   *
754854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse   * Iteration starts at the first file and iterates through all files.
755854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse   *
756854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse   * When {@link VirtualFileCycler#restart()} is called iteration of all files is restarted at the
757854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse   * current file.
758854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse   *
759854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse   * If the fill strategy indicate that the main dex file should be minimal, then the main dex file
760854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse   * will not be part of the iteration.
761854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse   */
762854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse  private static class VirtualFileCycler {
763854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private Map<Integer, VirtualFile> files;
764854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private final NamingLens namingLens;
765854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private final FillStrategy fillStrategy;
766854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
767854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private int nextFileId;
768854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private Iterator<VirtualFile> allFilesCyclic;
769854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private Iterator<VirtualFile> activeFiles;
770854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
771854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    VirtualFileCycler(Map<Integer, VirtualFile> files, NamingLens namingLens,
772854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse        FillStrategy fillStrategy) {
773854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      this.files = files;
774854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      this.namingLens = namingLens;
775854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      this.fillStrategy = fillStrategy;
776854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
777854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      nextFileId = files.size();
778854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      if (fillStrategy == FillStrategy.MINIMAL_MAIN_DEX) {
779854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse        // The main dex file is filtered out, so ensure at least one file for the remaining
780854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse        // classes
781854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse        files.put(nextFileId, new VirtualFile(nextFileId, namingLens));
782854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse        this.files = Maps.filterKeys(files, key -> key != 0);
783854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse        nextFileId++;
784854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      }
785854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
786854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      reset();
787854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    }
788854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
789854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private void reset() {
790854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      allFilesCyclic = Iterators.cycle(files.values());
791854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      restart();
792854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    }
793854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
794854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    boolean hasNext() {
795854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      return activeFiles.hasNext();
796854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    }
797854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
798854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    VirtualFile next() {
799854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      VirtualFile next = activeFiles.next();
800854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      assert fillStrategy != FillStrategy.MINIMAL_MAIN_DEX || next.getId() != 0;
801854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      return next;
802854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    }
803854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
804854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    // Start a new iteration over all files, starting at the current one.
805854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    void restart() {
806854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      activeFiles = Iterators.limit(allFilesCyclic, files.size());
807854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    }
808854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
809854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    VirtualFile addFile() {
810854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      VirtualFile newFile = new VirtualFile(nextFileId, namingLens);
811854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      files.put(nextFileId, newFile);
812854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      nextFileId++;
813854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
814854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      reset();
815854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      return newFile;
816854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    }
817854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse  }
818854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse
819854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse  /**
820418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   * Distributes the given classes over the files in package order.
821418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   *
822418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   * <p>The populator avoids package splits. Big packages are split into subpackages if their size
823418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   * exceeds 20% of the dex file. This populator also avoids filling files completely to cater for
824418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   * future growth.
825418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   *
826418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   * <p>The populator cycles through the files until all classes have been successfully placed and
827418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   * adds new files to the passed in map if it can't fit in the existing files.
828418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager   */
829418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private static class PackageSplitPopulator implements Callable<Map<String, Integer>> {
830418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
831418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    /**
832418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager     * Android suggests com.company.product for package names, so the components will be at level 4
833418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager     */
834418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private static final int MINIMUM_PREFIX_LENGTH = 4;
835418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private static final int MAXIMUM_PREFIX_LENGTH = 7;
836418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    /**
837418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager     * We allow 1/MIN_FILL_FACTOR of a file to remain empty when moving to the next file, i.e., a
838418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager     * rollback with less than 1/MAX_FILL_FACTOR of the total classes in a file will move to the
839418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager     * next file.
840418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager     */
841418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private static final int MIN_FILL_FACTOR = 5;
842418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
843418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final List<DexProgramClass> classes;
844418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Map<DexProgramClass, String> originalNames;
845418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final Set<String> previousPrefixes;
846418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final DexItemFactory dexItemFactory;
8475af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse    private final FillStrategy fillStrategy;
848854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private final VirtualFileCycler cycler;
849418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
850418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    PackageSplitPopulator(
851418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        Map<Integer, VirtualFile> files,
852418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        Set<DexProgramClass> classes,
853418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        Map<DexProgramClass, String> originalNames,
854418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        Set<String> previousPrefixes,
855418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        DexItemFactory dexItemFactory,
8565af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse        FillStrategy fillStrategy,
857418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        NamingLens namingLens) {
858418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.classes = new ArrayList<>(classes);
859418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.originalNames = originalNames;
860418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.previousPrefixes = previousPrefixes;
861418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.dexItemFactory = dexItemFactory;
8625af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse      this.fillStrategy = fillStrategy;
863854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      this.cycler = new VirtualFileCycler(files, namingLens, fillStrategy);
864418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
865418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
866418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private String getOriginalName(DexProgramClass clazz) {
867418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return originalNames != null ? originalNames.get(clazz) : clazz.toString();
868418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
869418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
870ca1299205e0a907ebe029c0461801a1c912d28c4Benoit Lamarche    @Override
871418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public Map<String, Integer> call() throws IOException {
872418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      int prefixLength = MINIMUM_PREFIX_LENGTH;
873418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      int transactionStartIndex = 0;
874418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      int fileStartIndex = 0;
875418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      String currentPrefix = null;
876418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      Map<String, Integer> newPackageAssignments = new LinkedHashMap<>();
877854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      VirtualFile current = cycler.next();
878418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      List<DexProgramClass> nonPackageClasses = new ArrayList<>();
879418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      for (int classIndex = 0; classIndex < classes.size(); classIndex++) {
880418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        DexProgramClass clazz = classes.get(classIndex);
881418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        String originalName = getOriginalName(clazz);
882418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (!PackageMapPopulator.coveredByPrefix(originalName, currentPrefix)) {
883418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          if (currentPrefix != null) {
884418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            current.commitTransaction();
885854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse            // Reset the cycler to again iterate over all files, starting with the current one.
886854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse            cycler.restart();
887418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            assert !newPackageAssignments.containsKey(currentPrefix);
888418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            newPackageAssignments.put(currentPrefix, current.id);
889418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            // Try to reduce the prefix length if possible. Only do this on a successful commit.
890418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            prefixLength = MINIMUM_PREFIX_LENGTH - 1;
891418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          }
892418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          String newPrefix;
893418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // Also, we need to avoid new prefixes that are a prefix of previously used prefixes, as
894418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // otherwise we might generate an overlap that will trigger problems when reusing the
895418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // package mapping generated here. For example, if an existing map contained
896418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          //   com.android.foo.*
897418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // but we now try to place some new subpackage
898418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          //   com.android.bar.*,
899418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // we locally could use
900418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          //   com.android.*.
901418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // However, when writing out the final package map, we get overlapping patterns
902418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // com.android.* and com.android.foo.*.
903418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          do {
904418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            newPrefix = extractPrefixToken(++prefixLength, originalName, false);
905418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          } while (currentPrefix != null &&
906418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              (currentPrefix.startsWith(newPrefix)
907418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              || conflictsWithPreviousPrefix(newPrefix, originalName)));
908418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // Don't set the current prefix if we did not extract one.
909418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          if (!newPrefix.equals("")) {
910418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            currentPrefix = extractPrefixToken(prefixLength, originalName, true);
911418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          }
912418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          transactionStartIndex = classIndex;
913418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
914418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (currentPrefix != null) {
915418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          assert clazz.superType != null || clazz.type == dexItemFactory.objectType;
916418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          current.addClass(clazz);
917418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        } else {
918418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          assert clazz.superType != null;
919418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // We don't have a package, add this to a list of classes that we will add last.
920418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          assert current.transaction.isEmpty();
921418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          nonPackageClasses.add(clazz);
922418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          continue;
923418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
9245af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse        if (current.isFilledEnough(fillStrategy) || current.isFull()) {
925418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          current.abortTransaction();
926418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // We allow for a final rollback that has at most 20% of classes in it.
927418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // This is a somewhat random number that was empirically chosen.
928418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          if (classIndex - transactionStartIndex > (classIndex - fileStartIndex) / MIN_FILL_FACTOR
929418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              && prefixLength < MAXIMUM_PREFIX_LENGTH) {
930418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            prefixLength++;
931418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          } else {
932418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            // Reset the state to after the last commit and cycle through files.
933418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            // The idea is that we do not increase the number of files, so it has to fit
934418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            // somewhere.
935418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            fileStartIndex = transactionStartIndex;
936854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse            if (!cycler.hasNext()) {
937418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              // Special case where we simply will never be able to fit the current package into
938418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              // one dex file. This is currently the case for Strings in jumbo tests, see:
939418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              // b/33227518
940418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              if (current.transaction.getNumberOfClasses() == 0) {
941418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager                for (int j = transactionStartIndex; j <= classIndex; j++) {
942418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager                  nonPackageClasses.add(classes.get(j));
943418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager                }
944418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager                transactionStartIndex = classIndex + 1;
945418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              }
946418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              // All files are filled up to the 20% mark.
947854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse              cycler.addFile();
948418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            }
949854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse            current = cycler.next();
950418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          }
951418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          currentPrefix = null;
952418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // Go back to previous start index.
953418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          classIndex = transactionStartIndex - 1;
954418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          assert current != null;
955418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
956418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
957418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      current.commitTransaction();
958418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      assert !newPackageAssignments.containsKey(currentPrefix);
959418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (currentPrefix != null) {
960418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        newPackageAssignments.put(currentPrefix, current.id);
961418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
962418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (nonPackageClasses.size() > 0) {
963854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse        addNonPackageClasses(cycler, nonPackageClasses);
964418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
965418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return newPackageAssignments;
966418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
967418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
968854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private void addNonPackageClasses(
969854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse        VirtualFileCycler cycler, List<DexProgramClass> nonPackageClasses) {
970854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      cycler.restart();
971418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      VirtualFile current;
972854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      current = cycler.next();
973418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      for (DexProgramClass clazz : nonPackageClasses) {
9745af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse        if (current.isFilledEnough(fillStrategy)) {
975854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse          current = getVirtualFile(cycler);
976418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
977418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        current.addClass(clazz);
978418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        while (current.isFull()) {
979418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // This only happens if we have a huge class, that takes up more than 20% of a dex file.
980418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          current.abortTransaction();
981854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse          current = getVirtualFile(cycler);
982418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          boolean wasEmpty = current.isEmpty();
983418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          current.addClass(clazz);
984418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          if (wasEmpty && current.isFull()) {
985418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            throw new InternalCompilerError(
986418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager                "Class " + clazz.toString() + " does not fit into a single dex file.");
987418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          }
988418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
989418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        current.commitTransaction();
990418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
991418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
992418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
993854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private VirtualFile getVirtualFile(VirtualFileCycler cycler) {
994418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      VirtualFile current = null;
995854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      while (cycler.hasNext()
996854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse          && (current = cycler.next()).isFilledEnough(fillStrategy)) {}
9975af35d2bb2157db3a238d91e3340573368051eaaSøren Gjesse      if (current == null || current.isFilledEnough(fillStrategy)) {
998854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse        current = cycler.addFile();
999418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
1000418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return current;
1001418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
1002418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
1003418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private boolean conflictsWithPreviousPrefix(String newPrefix, String originalName) {
1004418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (previousPrefixes == null) {
1005418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return false;
1006418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
1007418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      for (String previous : previousPrefixes) {
1008418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        // Check whether a previous prefix starts with this new prefix and, if so,
1009418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        // whether the new prefix already is maximal. So for example a new prefix of
1010418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        //   foo.bar
1011418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        // would match
1012418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        //   foo.bar.goo.*
1013418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        // However, if the original class is
1014418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        //   foo.bar.X
1015418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        // then this prefix is the best we can do, and will not turn into a .* prefix and
1016418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        // thus does not conflict.
1017418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (previous.startsWith(newPrefix)
1018418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            && (originalName.lastIndexOf('.') > newPrefix.length())) {
1019418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          return true;
1020418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
1021418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
1022418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
1023418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return false;
1024418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
1025418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
1026418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager}
1027