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