• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dx.merge;
18 
19 import com.android.dex.Annotation;
20 import com.android.dex.CallSiteId;
21 import com.android.dex.ClassData;
22 import com.android.dex.ClassDef;
23 import com.android.dex.Code;
24 import com.android.dex.Dex;
25 import com.android.dex.DexException;
26 import com.android.dex.DexIndexOverflowException;
27 import com.android.dex.FieldId;
28 import com.android.dex.MethodHandle;
29 import com.android.dex.MethodId;
30 import com.android.dex.ProtoId;
31 import com.android.dex.SizeOf;
32 import com.android.dex.TableOfContents;
33 import com.android.dex.TypeList;
34 import com.android.dx.command.dexer.DxContext;
35 
36 import java.io.File;
37 import java.io.IOException;
38 import java.util.*;
39 
40 /**
41  * Combine two dex files into one.
42  */
43 public final class DexMerger {
44     private final Dex[] dexes;
45     private final IndexMap[] indexMaps;
46 
47     private final CollisionPolicy collisionPolicy;
48     private final DxContext context;
49     private final WriterSizes writerSizes;
50 
51     private final Dex dexOut;
52 
53     private final Dex.Section headerOut;
54 
55     /** All IDs and definitions sections */
56     private final Dex.Section idsDefsOut;
57 
58     private final Dex.Section mapListOut;
59 
60     private final Dex.Section typeListOut;
61 
62     private final Dex.Section classDataOut;
63 
64     private final Dex.Section codeOut;
65 
66     private final Dex.Section stringDataOut;
67 
68     private final Dex.Section debugInfoOut;
69 
70     private final Dex.Section encodedArrayOut;
71 
72     /** annotations directory on a type */
73     private final Dex.Section annotationsDirectoryOut;
74 
75     /** sets of annotations on a member, parameter or type */
76     private final Dex.Section annotationSetOut;
77 
78     /** parameter lists */
79     private final Dex.Section annotationSetRefListOut;
80 
81     /** individual annotations, each containing zero or more fields */
82     private final Dex.Section annotationOut;
83 
84     private final TableOfContents contentsOut;
85 
86     private final InstructionTransformer instructionTransformer;
87 
88     /** minimum number of wasted bytes before it's worthwhile to compact the result */
89     private int compactWasteThreshold = 1024 * 1024; // 1MiB
90 
DexMerger(Dex[] dexes, CollisionPolicy collisionPolicy, DxContext context)91     public DexMerger(Dex[] dexes, CollisionPolicy collisionPolicy, DxContext context)
92             throws IOException {
93         this(dexes, collisionPolicy, context, new WriterSizes(dexes));
94     }
95 
DexMerger(Dex[] dexes, CollisionPolicy collisionPolicy, DxContext context, WriterSizes writerSizes)96     private DexMerger(Dex[] dexes, CollisionPolicy collisionPolicy, DxContext context,
97             WriterSizes writerSizes) throws IOException {
98         this.dexes = dexes;
99         this.collisionPolicy = collisionPolicy;
100         this.context = context;
101         this.writerSizes = writerSizes;
102 
103         dexOut = new Dex(writerSizes.size());
104 
105         indexMaps = new IndexMap[dexes.length];
106         for (int i = 0; i < dexes.length; i++) {
107             indexMaps[i] = new IndexMap(dexOut, dexes[i].getTableOfContents());
108         }
109         instructionTransformer = new InstructionTransformer();
110 
111         headerOut = dexOut.appendSection(writerSizes.header, "header");
112         idsDefsOut = dexOut.appendSection(writerSizes.idsDefs, "ids defs");
113 
114         contentsOut = dexOut.getTableOfContents();
115         contentsOut.dataOff = dexOut.getNextSectionStart();
116 
117         contentsOut.mapList.off = dexOut.getNextSectionStart();
118         contentsOut.mapList.size = 1;
119         mapListOut = dexOut.appendSection(writerSizes.mapList, "map list");
120 
121         contentsOut.typeLists.off = dexOut.getNextSectionStart();
122         typeListOut = dexOut.appendSection(writerSizes.typeList, "type list");
123 
124         contentsOut.annotationSetRefLists.off = dexOut.getNextSectionStart();
125         annotationSetRefListOut = dexOut.appendSection(
126                 writerSizes.annotationsSetRefList, "annotation set ref list");
127 
128         contentsOut.annotationSets.off = dexOut.getNextSectionStart();
129         annotationSetOut = dexOut.appendSection(writerSizes.annotationsSet, "annotation sets");
130 
131         contentsOut.classDatas.off = dexOut.getNextSectionStart();
132         classDataOut = dexOut.appendSection(writerSizes.classData, "class data");
133 
134         contentsOut.codes.off = dexOut.getNextSectionStart();
135         codeOut = dexOut.appendSection(writerSizes.code, "code");
136 
137         contentsOut.stringDatas.off = dexOut.getNextSectionStart();
138         stringDataOut = dexOut.appendSection(writerSizes.stringData, "string data");
139 
140         contentsOut.debugInfos.off = dexOut.getNextSectionStart();
141         debugInfoOut = dexOut.appendSection(writerSizes.debugInfo, "debug info");
142 
143         contentsOut.annotations.off = dexOut.getNextSectionStart();
144         annotationOut = dexOut.appendSection(writerSizes.annotation, "annotation");
145 
146         contentsOut.encodedArrays.off = dexOut.getNextSectionStart();
147         encodedArrayOut = dexOut.appendSection(writerSizes.encodedArray, "encoded array");
148 
149         contentsOut.annotationsDirectories.off = dexOut.getNextSectionStart();
150         annotationsDirectoryOut = dexOut.appendSection(
151                 writerSizes.annotationsDirectory, "annotations directory");
152 
153         contentsOut.dataSize = dexOut.getNextSectionStart() - contentsOut.dataOff;
154     }
155 
setCompactWasteThreshold(int compactWasteThreshold)156     public void setCompactWasteThreshold(int compactWasteThreshold) {
157         this.compactWasteThreshold = compactWasteThreshold;
158     }
159 
mergeDexes()160     private Dex mergeDexes() throws IOException {
161         mergeStringIds();
162         mergeTypeIds();
163         mergeTypeLists();
164         mergeProtoIds();
165         mergeFieldIds();
166         mergeMethodIds();
167         mergeMethodHandles();
168         mergeAnnotations();
169         unionAnnotationSetsAndDirectories();
170         mergeCallSiteIds();
171         mergeClassDefs();
172 
173         // computeSizesFromOffsets expects sections sorted by offset, so make it so
174         Arrays.sort(contentsOut.sections);
175 
176         // write the header
177         contentsOut.header.off = 0;
178         contentsOut.header.size = 1;
179         contentsOut.fileSize = dexOut.getLength();
180         contentsOut.computeSizesFromOffsets();
181         contentsOut.writeHeader(headerOut, mergeApiLevels());
182         contentsOut.writeMap(mapListOut);
183 
184         // generate and write the hashes
185         dexOut.writeHashes();
186 
187         return dexOut;
188     }
189 
merge()190     public Dex merge() throws IOException {
191         if (dexes.length == 1) {
192             return dexes[0];
193         } else if (dexes.length == 0) {
194             return null;
195         }
196 
197         long start = System.nanoTime();
198         Dex result = mergeDexes();
199 
200         /*
201          * We use pessimistic sizes when merging dex files. If those sizes
202          * result in too many bytes wasted, compact the result. To compact,
203          * simply merge the result with itself.
204          */
205         WriterSizes compactedSizes = new WriterSizes(this);
206         int wastedByteCount = writerSizes.size() - compactedSizes.size();
207         if (wastedByteCount >  + compactWasteThreshold) {
208             DexMerger compacter = new DexMerger(
209                     new Dex[] {dexOut, new Dex(0)}, CollisionPolicy.FAIL, context, compactedSizes);
210             result = compacter.mergeDexes();
211             context.out.printf("Result compacted from %.1fKiB to %.1fKiB to save %.1fKiB%n",
212                     dexOut.getLength() / 1024f,
213                     result.getLength() / 1024f,
214                     wastedByteCount / 1024f);
215         }
216 
217         long elapsed = System.nanoTime() - start;
218         for (int i = 0; i < dexes.length; i++) {
219             context.out.printf("Merged dex #%d (%d defs/%.1fKiB)%n",
220                 i + 1,
221                 dexes[i].getTableOfContents().classDefs.size,
222                 dexes[i].getLength() / 1024f);
223         }
224         context.out.printf("Result is %d defs/%.1fKiB. Took %.1fs%n",
225                 result.getTableOfContents().classDefs.size,
226                 result.getLength() / 1024f,
227                 elapsed / 1000000000f);
228 
229         return result;
230     }
231 
232     /**
233      * Reads an IDs section of two dex files and writes an IDs section of a
234      * merged dex file. Populates maps from old to new indices in the process.
235      */
236     abstract class IdMerger<T extends Comparable<T>> {
237         private final Dex.Section out;
238 
IdMerger(Dex.Section out)239         protected IdMerger(Dex.Section out) {
240             this.out = out;
241         }
242 
243         /**
244          * Merges already-sorted sections, reading one value from each dex into memory
245          * at a time.
246          */
mergeSorted()247         public final void mergeSorted() {
248             TableOfContents.Section[] sections = new TableOfContents.Section[dexes.length];
249             Dex.Section[] dexSections = new Dex.Section[dexes.length];
250             int[] offsets = new int[dexes.length];
251             int[] indexes = new int[dexes.length];
252 
253             // values contains one value from each dex, sorted for fast retrieval of
254             // the smallest value. The list associated with a value has the indexes
255             // of the dexes that had that value.
256             TreeMap<T, List<Integer>> values = new TreeMap<T, List<Integer>>();
257 
258             for (int i = 0; i < dexes.length; i++) {
259                 sections[i] = getSection(dexes[i].getTableOfContents());
260                 dexSections[i] = sections[i].exists() ? dexes[i].open(sections[i].off) : null;
261                 // Fill in values with the first value of each dex.
262                 offsets[i] = readIntoMap(
263                         dexSections[i], sections[i], indexMaps[i], indexes[i], values, i);
264             }
265             if (values.isEmpty()) {
266                 getSection(contentsOut).off = 0;
267                 getSection(contentsOut).size = 0;
268                 return;
269             }
270             getSection(contentsOut).off = out.getPosition();
271 
272             int outCount = 0;
273             while (!values.isEmpty()) {
274                 Map.Entry<T, List<Integer>> first = values.pollFirstEntry();
275                 for (Integer dex : first.getValue()) {
276                     updateIndex(offsets[dex], indexMaps[dex], indexes[dex]++, outCount);
277                     // Fetch the next value of the dexes we just polled out
278                     offsets[dex] = readIntoMap(dexSections[dex], sections[dex],
279                             indexMaps[dex], indexes[dex], values, dex);
280                 }
281                 write(first.getKey());
282                 outCount++;
283             }
284 
285             getSection(contentsOut).size = outCount;
286         }
287 
readIntoMap(Dex.Section in, TableOfContents.Section section, IndexMap indexMap, int index, TreeMap<T, List<Integer>> values, int dex)288         private int readIntoMap(Dex.Section in, TableOfContents.Section section, IndexMap indexMap,
289                                 int index, TreeMap<T, List<Integer>> values, int dex) {
290             int offset = in != null ? in.getPosition() : -1;
291             if (index < section.size) {
292                 T v = read(in, indexMap, index);
293                 List<Integer> l = values.get(v);
294                 if (l == null) {
295                     l = new ArrayList<Integer>();
296                     values.put(v, l);
297                 }
298                 l.add(dex);
299             }
300             return offset;
301         }
302 
303         /**
304          * Merges unsorted sections by reading them completely into memory and
305          * sorting in memory.
306          */
mergeUnsorted()307         public final void mergeUnsorted() {
308             getSection(contentsOut).off = out.getPosition();
309 
310             List<UnsortedValue> all = new ArrayList<UnsortedValue>();
311             for (int i = 0; i < dexes.length; i++) {
312                 all.addAll(readUnsortedValues(dexes[i], indexMaps[i]));
313             }
314             if (all.isEmpty()) {
315                 getSection(contentsOut).off = 0;
316                 getSection(contentsOut).size = 0;
317                 return;
318             }
319             Collections.sort(all);
320 
321             int outCount = 0;
322             for (int i = 0; i < all.size(); ) {
323                 UnsortedValue e1 = all.get(i++);
324                 updateIndex(e1.offset, e1.indexMap, e1.index, outCount - 1);
325 
326                 while (i < all.size() && e1.compareTo(all.get(i)) == 0) {
327                     UnsortedValue e2 = all.get(i++);
328                     updateIndex(e2.offset, e2.indexMap, e2.index, outCount - 1);
329                 }
330 
331                 write(e1.value);
332                 outCount++;
333             }
334 
335             getSection(contentsOut).size = outCount;
336         }
337 
readUnsortedValues(Dex source, IndexMap indexMap)338         private List<UnsortedValue> readUnsortedValues(Dex source, IndexMap indexMap) {
339             TableOfContents.Section section = getSection(source.getTableOfContents());
340             if (!section.exists()) {
341                 return Collections.emptyList();
342             }
343 
344             List<UnsortedValue> result = new ArrayList<UnsortedValue>();
345             Dex.Section in = source.open(section.off);
346             for (int i = 0; i < section.size; i++) {
347                 int offset = in.getPosition();
348                 T value = read(in, indexMap, 0);
349                 result.add(new UnsortedValue(source, indexMap, value, i, offset));
350             }
351             return result;
352         }
353 
getSection(TableOfContents tableOfContents)354         abstract TableOfContents.Section getSection(TableOfContents tableOfContents);
read(Dex.Section in, IndexMap indexMap, int index)355         abstract T read(Dex.Section in, IndexMap indexMap, int index);
updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex)356         abstract void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex);
write(T value)357         abstract void write(T value);
358 
359         class UnsortedValue implements Comparable<UnsortedValue> {
360             final Dex source;
361             final IndexMap indexMap;
362             final T value;
363             final int index;
364             final int offset;
365 
UnsortedValue(Dex source, IndexMap indexMap, T value, int index, int offset)366             UnsortedValue(Dex source, IndexMap indexMap, T value, int index, int offset) {
367                 this.source = source;
368                 this.indexMap = indexMap;
369                 this.value = value;
370                 this.index = index;
371                 this.offset = offset;
372             }
373 
374             @Override
compareTo(UnsortedValue unsortedValue)375             public int compareTo(UnsortedValue unsortedValue) {
376                 return value.compareTo(unsortedValue.value);
377             }
378         }
379     }
380 
mergeApiLevels()381     private int mergeApiLevels() {
382         int maxApi = -1;
383         for (int i = 0; i < dexes.length; i++) {
384             int dexMinApi = dexes[i].getTableOfContents().apiLevel;
385             if (maxApi < dexMinApi) {
386                 maxApi = dexMinApi;
387             }
388         }
389         return maxApi;
390     }
391 
mergeStringIds()392     private void mergeStringIds() {
393         new IdMerger<String>(idsDefsOut) {
394             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
395                 return tableOfContents.stringIds;
396             }
397 
398             @Override String read(Dex.Section in, IndexMap indexMap, int index) {
399                 return in.readString();
400             }
401 
402             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
403                 indexMap.stringIds[oldIndex] = newIndex;
404             }
405 
406             @Override void write(String value) {
407                 contentsOut.stringDatas.size++;
408                 idsDefsOut.writeInt(stringDataOut.getPosition());
409                 stringDataOut.writeStringData(value);
410             }
411         }.mergeSorted();
412     }
413 
mergeTypeIds()414     private void mergeTypeIds() {
415         new IdMerger<Integer>(idsDefsOut) {
416             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
417                 return tableOfContents.typeIds;
418             }
419 
420             @Override Integer read(Dex.Section in, IndexMap indexMap, int index) {
421                 int stringIndex = in.readInt();
422                 return indexMap.adjustString(stringIndex);
423             }
424 
425             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
426                 if (newIndex < 0 || newIndex > 0xffff) {
427                     throw new DexIndexOverflowException("type ID not in [0, 0xffff]: " + newIndex);
428                 }
429                 indexMap.typeIds[oldIndex] = (short) newIndex;
430             }
431 
432             @Override void write(Integer value) {
433                 idsDefsOut.writeInt(value);
434             }
435         }.mergeSorted();
436     }
437 
mergeTypeLists()438     private void mergeTypeLists() {
439         new IdMerger<TypeList>(typeListOut) {
440             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
441                 return tableOfContents.typeLists;
442             }
443 
444             @Override TypeList read(Dex.Section in, IndexMap indexMap, int index) {
445                 return indexMap.adjustTypeList(in.readTypeList());
446             }
447 
448             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
449                 indexMap.putTypeListOffset(offset, typeListOut.getPosition());
450             }
451 
452             @Override void write(TypeList value) {
453                 typeListOut.writeTypeList(value);
454             }
455         }.mergeUnsorted();
456     }
457 
mergeProtoIds()458     private void mergeProtoIds() {
459         new IdMerger<ProtoId>(idsDefsOut) {
460             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
461                 return tableOfContents.protoIds;
462             }
463 
464             @Override ProtoId read(Dex.Section in, IndexMap indexMap, int index) {
465                 return indexMap.adjust(in.readProtoId());
466             }
467 
468             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
469                 if (newIndex < 0 || newIndex > 0xffff) {
470                     throw new DexIndexOverflowException("proto ID not in [0, 0xffff]: " + newIndex);
471                 }
472                 indexMap.protoIds[oldIndex] = (short) newIndex;
473             }
474 
475             @Override
476             void write(ProtoId value) {
477                 value.writeTo(idsDefsOut);
478             }
479         }.mergeSorted();
480     }
481 
mergeCallSiteIds()482     private void mergeCallSiteIds() {
483         new IdMerger<CallSiteId>(idsDefsOut) {
484             @Override
485             TableOfContents.Section getSection(TableOfContents tableOfContents) {
486                 return tableOfContents.callSiteIds;
487             }
488 
489             @Override
490             CallSiteId read(Dex.Section in, IndexMap indexMap, int index) {
491                 return indexMap.adjust(in.readCallSiteId());
492             }
493 
494             @Override
495             void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
496                 indexMap.callSiteIds[oldIndex] = newIndex;
497             }
498 
499             @Override
500             void write(CallSiteId value) {
501                 value.writeTo(idsDefsOut);
502             }
503         }.mergeSorted();
504     }
505 
mergeMethodHandles()506     private void mergeMethodHandles() {
507         new IdMerger<MethodHandle>(idsDefsOut) {
508             @Override
509             TableOfContents.Section getSection(TableOfContents tableOfContents) {
510                 return tableOfContents.methodHandles;
511             }
512 
513             @Override
514             MethodHandle read(Dex.Section in, IndexMap indexMap, int index) {
515                 return indexMap.adjust(in.readMethodHandle());
516             }
517 
518             @Override
519             void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
520                 indexMap.methodHandleIds.put(oldIndex, indexMap.methodHandleIds.size());
521             }
522 
523             @Override
524             void write(MethodHandle value) {
525                 value.writeTo(idsDefsOut);
526             }
527         }.mergeUnsorted();
528     }
529 
mergeFieldIds()530     private void mergeFieldIds() {
531         new IdMerger<FieldId>(idsDefsOut) {
532             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
533                 return tableOfContents.fieldIds;
534             }
535 
536             @Override FieldId read(Dex.Section in, IndexMap indexMap, int index) {
537                 return indexMap.adjust(in.readFieldId());
538             }
539 
540             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
541                 if (newIndex < 0 || newIndex > 0xffff) {
542                     throw new DexIndexOverflowException("field ID not in [0, 0xffff]: " + newIndex);
543                 }
544                 indexMap.fieldIds[oldIndex] = (short) newIndex;
545             }
546 
547             @Override void write(FieldId value) {
548                 value.writeTo(idsDefsOut);
549             }
550         }.mergeSorted();
551     }
552 
mergeMethodIds()553     private void mergeMethodIds() {
554         new IdMerger<MethodId>(idsDefsOut) {
555             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
556                 return tableOfContents.methodIds;
557             }
558 
559             @Override MethodId read(Dex.Section in, IndexMap indexMap, int index) {
560                 return indexMap.adjust(in.readMethodId());
561             }
562 
563             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
564                 if (newIndex < 0 || newIndex > 0xffff) {
565                     throw new DexIndexOverflowException(
566                         "method ID not in [0, 0xffff]: " + newIndex);
567                 }
568                 indexMap.methodIds[oldIndex] = (short) newIndex;
569             }
570 
571             @Override void write(MethodId methodId) {
572                 methodId.writeTo(idsDefsOut);
573             }
574         }.mergeSorted();
575     }
576 
mergeAnnotations()577     private void mergeAnnotations() {
578         new IdMerger<Annotation>(annotationOut) {
579             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
580                 return tableOfContents.annotations;
581             }
582 
583             @Override Annotation read(Dex.Section in, IndexMap indexMap, int index) {
584                 return indexMap.adjust(in.readAnnotation());
585             }
586 
587             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
588                 indexMap.putAnnotationOffset(offset, annotationOut.getPosition());
589             }
590 
591             @Override void write(Annotation value) {
592                 value.writeTo(annotationOut);
593             }
594         }.mergeUnsorted();
595     }
596 
mergeClassDefs()597     private void mergeClassDefs() {
598         SortableType[] types = getSortedTypes();
599         contentsOut.classDefs.off = idsDefsOut.getPosition();
600         contentsOut.classDefs.size = types.length;
601 
602         for (SortableType type : types) {
603             Dex in = type.getDex();
604             transformClassDef(in, type.getClassDef(), type.getIndexMap());
605         }
606     }
607 
608     /**
609      * Returns the union of classes from both files, sorted in order such that
610      * a class is always preceded by its supertype and implemented interfaces.
611      */
getSortedTypes()612     private SortableType[] getSortedTypes() {
613         // size is pessimistic; doesn't include arrays
614         SortableType[] sortableTypes = new SortableType[contentsOut.typeIds.size];
615         for (int i = 0; i < dexes.length; i++) {
616             readSortableTypes(sortableTypes, dexes[i], indexMaps[i]);
617         }
618 
619         /*
620          * Populate the depths of each sortable type. This makes D iterations
621          * through all N types, where 'D' is the depth of the deepest type. For
622          * example, the deepest class in libcore is Xalan's KeyIterator, which
623          * is 11 types deep.
624          */
625         while (true) {
626             boolean allDone = true;
627             for (SortableType sortableType : sortableTypes) {
628                 if (sortableType != null && !sortableType.isDepthAssigned()) {
629                     allDone &= sortableType.tryAssignDepth(sortableTypes);
630                 }
631             }
632             if (allDone) {
633                 break;
634             }
635         }
636 
637         // Now that all types have depth information, the result can be sorted
638         Arrays.sort(sortableTypes, SortableType.NULLS_LAST_ORDER);
639 
640         // Strip nulls from the end
641         int firstNull = Arrays.asList(sortableTypes).indexOf(null);
642         return firstNull != -1
643                 ? Arrays.copyOfRange(sortableTypes, 0, firstNull)
644                 : sortableTypes;
645     }
646 
647     /**
648      * Reads just enough data on each class so that we can sort it and then find
649      * it later.
650      */
readSortableTypes(SortableType[] sortableTypes, Dex buffer, IndexMap indexMap)651     private void readSortableTypes(SortableType[] sortableTypes, Dex buffer,
652             IndexMap indexMap) {
653         for (ClassDef classDef : buffer.classDefs()) {
654             SortableType sortableType = indexMap.adjust(
655                     new SortableType(buffer, indexMap, classDef));
656             int t = sortableType.getTypeIndex();
657             if (sortableTypes[t] == null) {
658                 sortableTypes[t] = sortableType;
659             } else if (collisionPolicy != CollisionPolicy.KEEP_FIRST) {
660                 throw new DexException("Multiple dex files define "
661                         + buffer.typeNames().get(classDef.getTypeIndex()));
662             }
663         }
664     }
665 
666     /**
667      * Copy annotation sets from each input to the output.
668      *
669      * TODO: this may write multiple copies of the same annotation set.
670      * We should shrink the output by merging rather than unioning
671      */
unionAnnotationSetsAndDirectories()672     private void unionAnnotationSetsAndDirectories() {
673         for (int i = 0; i < dexes.length; i++) {
674             transformAnnotationSets(dexes[i], indexMaps[i]);
675         }
676         for (int i = 0; i < dexes.length; i++) {
677             transformAnnotationSetRefLists(dexes[i], indexMaps[i]);
678         }
679         for (int i = 0; i < dexes.length; i++) {
680             transformAnnotationDirectories(dexes[i], indexMaps[i]);
681         }
682         for (int i = 0; i < dexes.length; i++) {
683             transformStaticValues(dexes[i], indexMaps[i]);
684         }
685     }
686 
transformAnnotationSets(Dex in, IndexMap indexMap)687     private void transformAnnotationSets(Dex in, IndexMap indexMap) {
688         TableOfContents.Section section = in.getTableOfContents().annotationSets;
689         if (section.exists()) {
690             Dex.Section setIn = in.open(section.off);
691             for (int i = 0; i < section.size; i++) {
692                 transformAnnotationSet(indexMap, setIn);
693             }
694         }
695     }
696 
transformAnnotationSetRefLists(Dex in, IndexMap indexMap)697     private void transformAnnotationSetRefLists(Dex in, IndexMap indexMap) {
698         TableOfContents.Section section = in.getTableOfContents().annotationSetRefLists;
699         if (section.exists()) {
700             Dex.Section setIn = in.open(section.off);
701             for (int i = 0; i < section.size; i++) {
702                 transformAnnotationSetRefList(indexMap, setIn);
703             }
704         }
705     }
706 
transformAnnotationDirectories(Dex in, IndexMap indexMap)707     private void transformAnnotationDirectories(Dex in, IndexMap indexMap) {
708         TableOfContents.Section section = in.getTableOfContents().annotationsDirectories;
709         if (section.exists()) {
710             Dex.Section directoryIn = in.open(section.off);
711             for (int i = 0; i < section.size; i++) {
712                 transformAnnotationDirectory(directoryIn, indexMap);
713             }
714         }
715     }
716 
transformStaticValues(Dex in, IndexMap indexMap)717     private void transformStaticValues(Dex in, IndexMap indexMap) {
718         TableOfContents.Section section = in.getTableOfContents().encodedArrays;
719         if (section.exists()) {
720             Dex.Section staticValuesIn = in.open(section.off);
721             for (int i = 0; i < section.size; i++) {
722                 transformStaticValues(staticValuesIn, indexMap);
723             }
724         }
725     }
726 
727     /**
728      * Reads a class_def_item beginning at {@code in} and writes the index and
729      * data.
730      */
transformClassDef(Dex in, ClassDef classDef, IndexMap indexMap)731     private void transformClassDef(Dex in, ClassDef classDef, IndexMap indexMap) {
732         idsDefsOut.assertFourByteAligned();
733         idsDefsOut.writeInt(classDef.getTypeIndex());
734         idsDefsOut.writeInt(classDef.getAccessFlags());
735         idsDefsOut.writeInt(classDef.getSupertypeIndex());
736         idsDefsOut.writeInt(classDef.getInterfacesOffset());
737 
738         int sourceFileIndex = indexMap.adjustString(classDef.getSourceFileIndex());
739         idsDefsOut.writeInt(sourceFileIndex);
740 
741         int annotationsOff = classDef.getAnnotationsOffset();
742         idsDefsOut.writeInt(indexMap.adjustAnnotationDirectory(annotationsOff));
743 
744         int classDataOff = classDef.getClassDataOffset();
745         if (classDataOff == 0) {
746             idsDefsOut.writeInt(0);
747         } else {
748             idsDefsOut.writeInt(classDataOut.getPosition());
749             ClassData classData = in.readClassData(classDef);
750             transformClassData(in, classData, indexMap);
751         }
752 
753         int staticValuesOff = classDef.getStaticValuesOffset();
754         idsDefsOut.writeInt(indexMap.adjustEncodedArray(staticValuesOff));
755     }
756 
757     /**
758      * Transform all annotations on a class.
759      */
transformAnnotationDirectory( Dex.Section directoryIn, IndexMap indexMap)760     private void transformAnnotationDirectory(
761             Dex.Section directoryIn, IndexMap indexMap) {
762         contentsOut.annotationsDirectories.size++;
763         annotationsDirectoryOut.assertFourByteAligned();
764         indexMap.putAnnotationDirectoryOffset(
765                 directoryIn.getPosition(), annotationsDirectoryOut.getPosition());
766 
767         int classAnnotationsOffset = indexMap.adjustAnnotationSet(directoryIn.readInt());
768         annotationsDirectoryOut.writeInt(classAnnotationsOffset);
769 
770         int fieldsSize = directoryIn.readInt();
771         annotationsDirectoryOut.writeInt(fieldsSize);
772 
773         int methodsSize = directoryIn.readInt();
774         annotationsDirectoryOut.writeInt(methodsSize);
775 
776         int parameterListSize = directoryIn.readInt();
777         annotationsDirectoryOut.writeInt(parameterListSize);
778 
779         for (int i = 0; i < fieldsSize; i++) {
780             // field index
781             annotationsDirectoryOut.writeInt(indexMap.adjustField(directoryIn.readInt()));
782 
783             // annotations offset
784             annotationsDirectoryOut.writeInt(indexMap.adjustAnnotationSet(directoryIn.readInt()));
785         }
786 
787         for (int i = 0; i < methodsSize; i++) {
788             // method index
789             annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt()));
790 
791             // annotation set offset
792             annotationsDirectoryOut.writeInt(
793                     indexMap.adjustAnnotationSet(directoryIn.readInt()));
794         }
795 
796         for (int i = 0; i < parameterListSize; i++) {
797             // method index
798             annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt()));
799 
800             // annotations offset
801             annotationsDirectoryOut.writeInt(
802                     indexMap.adjustAnnotationSetRefList(directoryIn.readInt()));
803         }
804     }
805 
806     /**
807      * Transform all annotations on a single type, member or parameter.
808      */
transformAnnotationSet(IndexMap indexMap, Dex.Section setIn)809     private void transformAnnotationSet(IndexMap indexMap, Dex.Section setIn) {
810         contentsOut.annotationSets.size++;
811         annotationSetOut.assertFourByteAligned();
812         indexMap.putAnnotationSetOffset(setIn.getPosition(), annotationSetOut.getPosition());
813 
814         int size = setIn.readInt();
815         annotationSetOut.writeInt(size);
816 
817         for (int j = 0; j < size; j++) {
818             annotationSetOut.writeInt(indexMap.adjustAnnotation(setIn.readInt()));
819         }
820     }
821 
822     /**
823      * Transform all annotation set ref lists.
824      */
transformAnnotationSetRefList(IndexMap indexMap, Dex.Section refListIn)825     private void transformAnnotationSetRefList(IndexMap indexMap, Dex.Section refListIn) {
826         contentsOut.annotationSetRefLists.size++;
827         annotationSetRefListOut.assertFourByteAligned();
828         indexMap.putAnnotationSetRefListOffset(
829                 refListIn.getPosition(), annotationSetRefListOut.getPosition());
830 
831         int parameterCount = refListIn.readInt();
832         annotationSetRefListOut.writeInt(parameterCount);
833         for (int p = 0; p < parameterCount; p++) {
834             annotationSetRefListOut.writeInt(indexMap.adjustAnnotationSet(refListIn.readInt()));
835         }
836     }
837 
transformClassData(Dex in, ClassData classData, IndexMap indexMap)838     private void transformClassData(Dex in, ClassData classData, IndexMap indexMap) {
839         contentsOut.classDatas.size++;
840 
841         ClassData.Field[] staticFields = classData.getStaticFields();
842         ClassData.Field[] instanceFields = classData.getInstanceFields();
843         ClassData.Method[] directMethods = classData.getDirectMethods();
844         ClassData.Method[] virtualMethods = classData.getVirtualMethods();
845 
846         classDataOut.writeUleb128(staticFields.length);
847         classDataOut.writeUleb128(instanceFields.length);
848         classDataOut.writeUleb128(directMethods.length);
849         classDataOut.writeUleb128(virtualMethods.length);
850 
851         transformFields(indexMap, staticFields);
852         transformFields(indexMap, instanceFields);
853         transformMethods(in, indexMap, directMethods);
854         transformMethods(in, indexMap, virtualMethods);
855     }
856 
transformFields(IndexMap indexMap, ClassData.Field[] fields)857     private void transformFields(IndexMap indexMap, ClassData.Field[] fields) {
858         int lastOutFieldIndex = 0;
859         for (ClassData.Field field : fields) {
860             int outFieldIndex = indexMap.adjustField(field.getFieldIndex());
861             classDataOut.writeUleb128(outFieldIndex - lastOutFieldIndex);
862             lastOutFieldIndex = outFieldIndex;
863             classDataOut.writeUleb128(field.getAccessFlags());
864         }
865     }
866 
transformMethods(Dex in, IndexMap indexMap, ClassData.Method[] methods)867     private void transformMethods(Dex in, IndexMap indexMap, ClassData.Method[] methods) {
868         int lastOutMethodIndex = 0;
869         for (ClassData.Method method : methods) {
870             int outMethodIndex = indexMap.adjustMethod(method.getMethodIndex());
871             classDataOut.writeUleb128(outMethodIndex - lastOutMethodIndex);
872             lastOutMethodIndex = outMethodIndex;
873 
874             classDataOut.writeUleb128(method.getAccessFlags());
875 
876             if (method.getCodeOffset() == 0) {
877                 classDataOut.writeUleb128(0);
878             } else {
879                 codeOut.alignToFourBytesWithZeroFill();
880                 classDataOut.writeUleb128(codeOut.getPosition());
881                 transformCode(in, in.readCode(method), indexMap);
882             }
883         }
884     }
885 
transformCode(Dex in, Code code, IndexMap indexMap)886     private void transformCode(Dex in, Code code, IndexMap indexMap) {
887         contentsOut.codes.size++;
888         codeOut.assertFourByteAligned();
889 
890         codeOut.writeUnsignedShort(code.getRegistersSize());
891         codeOut.writeUnsignedShort(code.getInsSize());
892         codeOut.writeUnsignedShort(code.getOutsSize());
893 
894         Code.Try[] tries = code.getTries();
895         Code.CatchHandler[] catchHandlers = code.getCatchHandlers();
896         codeOut.writeUnsignedShort(tries.length);
897 
898         int debugInfoOffset = code.getDebugInfoOffset();
899         if (debugInfoOffset != 0) {
900             codeOut.writeInt(debugInfoOut.getPosition());
901             transformDebugInfoItem(in.open(debugInfoOffset), indexMap);
902         } else {
903             codeOut.writeInt(0);
904         }
905 
906         short[] instructions = code.getInstructions();
907         short[] newInstructions = instructionTransformer.transform(indexMap, instructions);
908         codeOut.writeInt(newInstructions.length);
909         codeOut.write(newInstructions);
910 
911         if (tries.length > 0) {
912             if (newInstructions.length % 2 == 1) {
913                 codeOut.writeShort((short) 0); // padding
914             }
915 
916             /*
917              * We can't write the tries until we've written the catch handlers.
918              * Unfortunately they're in the opposite order in the dex file so we
919              * need to transform them out-of-order.
920              */
921             Dex.Section triesSection = dexOut.open(codeOut.getPosition());
922             codeOut.skip(tries.length * SizeOf.TRY_ITEM);
923             int[] offsets = transformCatchHandlers(indexMap, catchHandlers);
924             transformTries(triesSection, tries, offsets);
925         }
926     }
927 
928     /**
929      * Writes the catch handlers to {@code codeOut} and returns their indices.
930      */
transformCatchHandlers(IndexMap indexMap, Code.CatchHandler[] catchHandlers)931     private int[] transformCatchHandlers(IndexMap indexMap, Code.CatchHandler[] catchHandlers) {
932         int baseOffset = codeOut.getPosition();
933         codeOut.writeUleb128(catchHandlers.length);
934         int[] offsets = new int[catchHandlers.length];
935         for (int i = 0; i < catchHandlers.length; i++) {
936             offsets[i] = codeOut.getPosition() - baseOffset;
937             transformEncodedCatchHandler(catchHandlers[i], indexMap);
938         }
939         return offsets;
940     }
941 
transformTries(Dex.Section out, Code.Try[] tries, int[] catchHandlerOffsets)942     private void transformTries(Dex.Section out, Code.Try[] tries,
943             int[] catchHandlerOffsets) {
944         for (Code.Try tryItem : tries) {
945             out.writeInt(tryItem.getStartAddress());
946             out.writeUnsignedShort(tryItem.getInstructionCount());
947             out.writeUnsignedShort(catchHandlerOffsets[tryItem.getCatchHandlerIndex()]);
948         }
949     }
950 
951     private static final byte DBG_END_SEQUENCE = 0x00;
952     private static final byte DBG_ADVANCE_PC = 0x01;
953     private static final byte DBG_ADVANCE_LINE = 0x02;
954     private static final byte DBG_START_LOCAL = 0x03;
955     private static final byte DBG_START_LOCAL_EXTENDED = 0x04;
956     private static final byte DBG_END_LOCAL = 0x05;
957     private static final byte DBG_RESTART_LOCAL = 0x06;
958     private static final byte DBG_SET_PROLOGUE_END = 0x07;
959     private static final byte DBG_SET_EPILOGUE_BEGIN = 0x08;
960     private static final byte DBG_SET_FILE = 0x09;
961 
transformDebugInfoItem(Dex.Section in, IndexMap indexMap)962     private void transformDebugInfoItem(Dex.Section in, IndexMap indexMap) {
963         contentsOut.debugInfos.size++;
964         int lineStart = in.readUleb128();
965         debugInfoOut.writeUleb128(lineStart);
966 
967         int parametersSize = in.readUleb128();
968         debugInfoOut.writeUleb128(parametersSize);
969 
970         for (int p = 0; p < parametersSize; p++) {
971             int parameterName = in.readUleb128p1();
972             debugInfoOut.writeUleb128p1(indexMap.adjustString(parameterName));
973         }
974 
975         int addrDiff;    // uleb128   address delta.
976         int lineDiff;    // sleb128   line delta.
977         int registerNum; // uleb128   register number.
978         int nameIndex;   // uleb128p1 string index.    Needs indexMap adjustment.
979         int typeIndex;   // uleb128p1 type index.      Needs indexMap adjustment.
980         int sigIndex;    // uleb128p1 string index.    Needs indexMap adjustment.
981 
982         while (true) {
983             int opcode = in.readByte();
984             debugInfoOut.writeByte(opcode);
985 
986             switch (opcode) {
987             case DBG_END_SEQUENCE:
988                 return;
989 
990             case DBG_ADVANCE_PC:
991                 addrDiff = in.readUleb128();
992                 debugInfoOut.writeUleb128(addrDiff);
993                 break;
994 
995             case DBG_ADVANCE_LINE:
996                 lineDiff = in.readSleb128();
997                 debugInfoOut.writeSleb128(lineDiff);
998                 break;
999 
1000             case DBG_START_LOCAL:
1001             case DBG_START_LOCAL_EXTENDED:
1002                 registerNum = in.readUleb128();
1003                 debugInfoOut.writeUleb128(registerNum);
1004                 nameIndex = in.readUleb128p1();
1005                 debugInfoOut.writeUleb128p1(indexMap.adjustString(nameIndex));
1006                 typeIndex = in.readUleb128p1();
1007                 debugInfoOut.writeUleb128p1(indexMap.adjustType(typeIndex));
1008                 if (opcode == DBG_START_LOCAL_EXTENDED) {
1009                     sigIndex = in.readUleb128p1();
1010                     debugInfoOut.writeUleb128p1(indexMap.adjustString(sigIndex));
1011                 }
1012                 break;
1013 
1014             case DBG_END_LOCAL:
1015             case DBG_RESTART_LOCAL:
1016                 registerNum = in.readUleb128();
1017                 debugInfoOut.writeUleb128(registerNum);
1018                 break;
1019 
1020             case DBG_SET_FILE:
1021                 nameIndex = in.readUleb128p1();
1022                 debugInfoOut.writeUleb128p1(indexMap.adjustString(nameIndex));
1023                 break;
1024 
1025             case DBG_SET_PROLOGUE_END:
1026             case DBG_SET_EPILOGUE_BEGIN:
1027             default:
1028                 break;
1029             }
1030         }
1031     }
1032 
transformEncodedCatchHandler(Code.CatchHandler catchHandler, IndexMap indexMap)1033     private void transformEncodedCatchHandler(Code.CatchHandler catchHandler, IndexMap indexMap) {
1034         int catchAllAddress = catchHandler.getCatchAllAddress();
1035         int[] typeIndexes = catchHandler.getTypeIndexes();
1036         int[] addresses = catchHandler.getAddresses();
1037 
1038         if (catchAllAddress != -1) {
1039             codeOut.writeSleb128(-typeIndexes.length);
1040         } else {
1041             codeOut.writeSleb128(typeIndexes.length);
1042         }
1043 
1044         for (int i = 0; i < typeIndexes.length; i++) {
1045             codeOut.writeUleb128(indexMap.adjustType(typeIndexes[i]));
1046             codeOut.writeUleb128(addresses[i]);
1047         }
1048 
1049         if (catchAllAddress != -1) {
1050             codeOut.writeUleb128(catchAllAddress);
1051         }
1052     }
1053 
transformStaticValues(Dex.Section in, IndexMap indexMap)1054     private void transformStaticValues(Dex.Section in, IndexMap indexMap) {
1055         contentsOut.encodedArrays.size++;
1056         indexMap.putEncodedArrayValueOffset(in.getPosition(), encodedArrayOut.getPosition());
1057         indexMap.adjustEncodedArray(in.readEncodedArray()).writeTo(encodedArrayOut);
1058     }
1059 
1060     /**
1061      * Byte counts for the sections written when creating a dex. Target sizes
1062      * are defined in one of two ways:
1063      * <ul>
1064      * <li>By pessimistically guessing how large the union of dex files will be.
1065      *     We're pessimistic because we can't predict the amount of duplication
1066      *     between dex files, nor can we predict the length of ULEB-encoded
1067      *     offsets or indices.
1068      * <li>By exactly measuring an existing dex.
1069      * </ul>
1070      */
1071     private static class WriterSizes {
1072         private int header = SizeOf.HEADER_ITEM;
1073         private int idsDefs;
1074         private int mapList;
1075         private int typeList;
1076         private int classData;
1077         private int code;
1078         private int stringData;
1079         private int debugInfo;
1080         private int encodedArray;
1081         private int annotationsDirectory;
1082         private int annotationsSet;
1083         private int annotationsSetRefList;
1084         private int annotation;
1085 
1086         /**
1087          * Compute sizes for merging several dexes.
1088          */
WriterSizes(Dex[] dexes)1089         public WriterSizes(Dex[] dexes) {
1090             for (int i = 0; i < dexes.length; i++) {
1091                 plus(dexes[i].getTableOfContents(), false);
1092             }
1093             fourByteAlign();
1094         }
1095 
WriterSizes(DexMerger dexMerger)1096         public WriterSizes(DexMerger dexMerger) {
1097             header = dexMerger.headerOut.used();
1098             idsDefs = dexMerger.idsDefsOut.used();
1099             mapList = dexMerger.mapListOut.used();
1100             typeList = dexMerger.typeListOut.used();
1101             classData = dexMerger.classDataOut.used();
1102             code = dexMerger.codeOut.used();
1103             stringData = dexMerger.stringDataOut.used();
1104             debugInfo = dexMerger.debugInfoOut.used();
1105             encodedArray = dexMerger.encodedArrayOut.used();
1106             annotationsDirectory = dexMerger.annotationsDirectoryOut.used();
1107             annotationsSet = dexMerger.annotationSetOut.used();
1108             annotationsSetRefList = dexMerger.annotationSetRefListOut.used();
1109             annotation = dexMerger.annotationOut.used();
1110             fourByteAlign();
1111         }
1112 
plus(TableOfContents contents, boolean exact)1113         private void plus(TableOfContents contents, boolean exact) {
1114             idsDefs += contents.stringIds.size * SizeOf.STRING_ID_ITEM
1115                     + contents.typeIds.size * SizeOf.TYPE_ID_ITEM
1116                     + contents.protoIds.size * SizeOf.PROTO_ID_ITEM
1117                     + contents.fieldIds.size * SizeOf.MEMBER_ID_ITEM
1118                     + contents.methodIds.size * SizeOf.MEMBER_ID_ITEM
1119                     + contents.classDefs.size * SizeOf.CLASS_DEF_ITEM;
1120             mapList = SizeOf.UINT + (contents.sections.length * SizeOf.MAP_ITEM);
1121             typeList += fourByteAlign(contents.typeLists.byteCount); // We count each dex's
1122             // typelists section as realigned on 4 bytes, because each typelist of each dex's
1123             // typelists section is aligned on 4 bytes. If we didn't, there is a case where each
1124             // size of both dex's typelists section is a multiple of 2 but not a multiple of 4,
1125             // and the sum of both sizes is a multiple of 4 but would not be sufficient to write
1126             // each typelist aligned on 4 bytes.
1127             stringData += contents.stringDatas.byteCount;
1128             annotationsDirectory += contents.annotationsDirectories.byteCount;
1129             annotationsSet += contents.annotationSets.byteCount;
1130             annotationsSetRefList += contents.annotationSetRefLists.byteCount;
1131 
1132             if (exact) {
1133                 code += contents.codes.byteCount;
1134                 classData += contents.classDatas.byteCount;
1135                 encodedArray += contents.encodedArrays.byteCount;
1136                 annotation += contents.annotations.byteCount;
1137                 debugInfo += contents.debugInfos.byteCount;
1138             } else {
1139                 // at most 1/4 of the bytes in a code section are uleb/sleb
1140                 code += (int) Math.ceil(contents.codes.byteCount * 1.25);
1141                 // at most 2/3 of the bytes in a class data section are uleb/sleb that may change
1142                 // (assuming the worst case that section contains only methods and no fields)
1143                 classData += (int) Math.ceil(contents.classDatas.byteCount * 1.67);
1144                 // all of the bytes in an encoding arrays section may be uleb/sleb
1145                 encodedArray += contents.encodedArrays.byteCount * 2;
1146                 // all of the bytes in an annotations section may be uleb/sleb
1147                 annotation += (int) Math.ceil(contents.annotations.byteCount * 2);
1148                 // all of the bytes in a debug info section may be uleb/sleb
1149                 debugInfo += contents.debugInfos.byteCount * 2;
1150             }
1151         }
1152 
fourByteAlign()1153         private void fourByteAlign() {
1154             header = fourByteAlign(header);
1155             idsDefs = fourByteAlign(idsDefs);
1156             mapList = fourByteAlign(mapList);
1157             typeList = fourByteAlign(typeList);
1158             classData = fourByteAlign(classData);
1159             code = fourByteAlign(code);
1160             stringData = fourByteAlign(stringData);
1161             debugInfo = fourByteAlign(debugInfo);
1162             encodedArray = fourByteAlign(encodedArray);
1163             annotationsDirectory = fourByteAlign(annotationsDirectory);
1164             annotationsSet = fourByteAlign(annotationsSet);
1165             annotationsSetRefList = fourByteAlign(annotationsSetRefList);
1166             annotation = fourByteAlign(annotation);
1167         }
1168 
fourByteAlign(int position)1169         private static int fourByteAlign(int position) {
1170             return (position + 3) & ~3;
1171         }
1172 
size()1173         public int size() {
1174             return header + idsDefs + mapList + typeList + classData + code + stringData + debugInfo
1175                     + encodedArray + annotationsDirectory + annotationsSet + annotationsSetRefList
1176                     + annotation;
1177         }
1178     }
1179 
main(String[] args)1180     public static void main(String[] args) throws IOException {
1181         if (args.length < 2) {
1182             printUsage();
1183             return;
1184         }
1185 
1186         Dex[] dexes = new Dex[args.length - 1];
1187         for (int i = 1; i < args.length; i++) {
1188             dexes[i - 1] = new Dex(new File(args[i]));
1189         }
1190         Dex merged = new DexMerger(dexes, CollisionPolicy.KEEP_FIRST, new DxContext()).merge();
1191         merged.writeTo(new File(args[0]));
1192     }
1193 
printUsage()1194     private static void printUsage() {
1195         System.out.println("Usage: DexMerger <out.dex> <a.dex> <b.dex> ...");
1196         System.out.println();
1197         System.out.println(
1198             "If a class is defined in several dex, the class found in the first dex will be used.");
1199     }
1200 }
1201