Home | History | Annotate | Download | only in merge
      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.dx.dex.SizeOf;
     20 import com.android.dx.dex.TableOfContents;
     21 import com.android.dx.io.Annotation;
     22 import com.android.dx.io.ClassData;
     23 import com.android.dx.io.ClassDef;
     24 import com.android.dx.io.Code;
     25 import com.android.dx.io.DexBuffer;
     26 import com.android.dx.io.DexHasher;
     27 import com.android.dx.io.FieldId;
     28 import com.android.dx.io.MethodId;
     29 import com.android.dx.io.ProtoId;
     30 import com.android.dx.util.DexException;
     31 import java.io.File;
     32 import java.io.IOException;
     33 import java.util.ArrayList;
     34 import java.util.Arrays;
     35 import java.util.Collections;
     36 import java.util.HashSet;
     37 import java.util.List;
     38 import java.util.Set;
     39 
     40 /**
     41  * Combine two dex files into one.
     42  */
     43 public final class DexMerger {
     44     private final DexBuffer dexA;
     45     private final DexBuffer dexB;
     46     private final CollisionPolicy collisionPolicy;
     47     private final WriterSizes writerSizes;
     48 
     49     private final DexBuffer dexOut = new DexBuffer();
     50 
     51     private final DexBuffer.Section headerOut;
     52 
     53     /** All IDs and definitions sections */
     54     private final DexBuffer.Section idsDefsOut;
     55 
     56     private final DexBuffer.Section mapListOut;
     57 
     58     private final DexBuffer.Section typeListOut;
     59 
     60     private final DexBuffer.Section classDataOut;
     61 
     62     private final DexBuffer.Section codeOut;
     63 
     64     private final DexBuffer.Section stringDataOut;
     65 
     66     private final DexBuffer.Section debugInfoOut;
     67 
     68     private final DexBuffer.Section encodedArrayOut;
     69 
     70     /** annotations directory on a type */
     71     private final DexBuffer.Section annotationsDirectoryOut;
     72 
     73     /** sets of annotations on a member, parameter or type */
     74     private final DexBuffer.Section annotationSetOut;
     75 
     76     /** parameter lists */
     77     private final DexBuffer.Section annotationSetRefListOut;
     78 
     79     /** individual annotations, each containing zero or more fields */
     80     private final DexBuffer.Section annotationOut;
     81 
     82     private final TableOfContents contentsOut;
     83 
     84     private final IndexMap aIndexMap;
     85     private final IndexMap bIndexMap;
     86     private final InstructionTransformer aInstructionTransformer;
     87     private final InstructionTransformer bInstructionTransformer;
     88 
     89     /** minimum number of wasted bytes before it's worthwhile to compact the result */
     90     private int compactWasteThreshold = 1024 * 1024; // 1MiB
     91 
     92     public DexMerger(DexBuffer dexA, DexBuffer dexB, CollisionPolicy collisionPolicy)
     93             throws IOException {
     94         this(dexA, dexB, collisionPolicy, new WriterSizes(dexA, dexB));
     95     }
     96 
     97     private DexMerger(DexBuffer dexA, DexBuffer dexB, CollisionPolicy collisionPolicy,
     98             WriterSizes writerSizes) throws IOException {
     99         this.dexA = dexA;
    100         this.dexB = dexB;
    101         this.collisionPolicy = collisionPolicy;
    102         this.writerSizes = writerSizes;
    103 
    104         TableOfContents aContents = dexA.getTableOfContents();
    105         TableOfContents bContents = dexB.getTableOfContents();
    106         aIndexMap = new IndexMap(dexOut, aContents);
    107         bIndexMap = new IndexMap(dexOut, bContents);
    108         aInstructionTransformer = new InstructionTransformer(aIndexMap);
    109         bInstructionTransformer = new InstructionTransformer(bIndexMap);
    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.getLength();
    116 
    117         contentsOut.mapList.off = dexOut.getLength();
    118         contentsOut.mapList.size = 1;
    119         mapListOut = dexOut.appendSection(writerSizes.mapList, "map list");
    120 
    121         contentsOut.typeLists.off = dexOut.getLength();
    122         typeListOut = dexOut.appendSection(writerSizes.typeList, "type list");
    123 
    124         contentsOut.annotationSetRefLists.off = dexOut.getLength();
    125         annotationSetRefListOut = dexOut.appendSection(
    126                 writerSizes.annotationsSetRefList, "annotation set ref list");
    127 
    128         contentsOut.annotationSets.off = dexOut.getLength();
    129         annotationSetOut = dexOut.appendSection(writerSizes.annotationsSet, "annotation sets");
    130 
    131         contentsOut.classDatas.off = dexOut.getLength();
    132         classDataOut = dexOut.appendSection(writerSizes.classData, "class data");
    133 
    134         contentsOut.codes.off = dexOut.getLength();
    135         codeOut = dexOut.appendSection(writerSizes.code, "code");
    136 
    137         contentsOut.stringDatas.off = dexOut.getLength();
    138         stringDataOut = dexOut.appendSection(writerSizes.stringData, "string data");
    139 
    140         contentsOut.debugInfos.off = dexOut.getLength();
    141         debugInfoOut = dexOut.appendSection(writerSizes.debugInfo, "debug info");
    142 
    143         contentsOut.annotations.off = dexOut.getLength();
    144         annotationOut = dexOut.appendSection(writerSizes.annotation, "annotation");
    145 
    146         contentsOut.encodedArrays.off = dexOut.getLength();
    147         encodedArrayOut = dexOut.appendSection(writerSizes.encodedArray, "encoded array");
    148 
    149         contentsOut.annotationsDirectories.off = dexOut.getLength();
    150         annotationsDirectoryOut = dexOut.appendSection(
    151                 writerSizes.annotationsDirectory, "annotations directory");
    152 
    153         dexOut.noMoreSections();
    154         contentsOut.dataSize = dexOut.getLength() - contentsOut.dataOff;
    155     }
    156 
    157     public void setCompactWasteThreshold(int compactWasteThreshold) {
    158         this.compactWasteThreshold = compactWasteThreshold;
    159     }
    160 
    161     private DexBuffer mergeDexBuffers() throws IOException {
    162         mergeStringIds();
    163         mergeTypeIds();
    164         mergeTypeLists();
    165         mergeProtoIds();
    166         mergeFieldIds();
    167         mergeMethodIds();
    168         mergeAnnotations();
    169         unionAnnotationSetsAndDirectories();
    170         mergeClassDefs();
    171 
    172         // write the header
    173         contentsOut.header.off = 0;
    174         contentsOut.header.size = 1;
    175         contentsOut.fileSize = dexOut.getLength();
    176         contentsOut.computeSizesFromOffsets();
    177         contentsOut.writeHeader(headerOut);
    178         contentsOut.writeMap(mapListOut);
    179 
    180         // generate and write the hashes
    181         new DexHasher().writeHashes(dexOut);
    182 
    183         return dexOut;
    184     }
    185 
    186     public DexBuffer merge() throws IOException {
    187         long start = System.nanoTime();
    188         DexBuffer result = mergeDexBuffers();
    189 
    190         /*
    191          * We use pessimistic sizes when merging dex files. If those sizes
    192          * result in too many bytes wasted, compact the result. To compact,
    193          * simply merge the result with itself.
    194          */
    195         WriterSizes compactedSizes = new WriterSizes(this);
    196         int wastedByteCount = writerSizes.size() - compactedSizes.size();
    197         if (wastedByteCount >  + compactWasteThreshold) {
    198             DexMerger compacter = new DexMerger(
    199                     dexOut, new DexBuffer(), CollisionPolicy.FAIL, compactedSizes);
    200             result = compacter.mergeDexBuffers();
    201             System.out.printf("Result compacted from %.1fKiB to %.1fKiB to save %.1fKiB%n",
    202                     dexOut.getLength() / 1024f,
    203                     result.getLength() / 1024f,
    204                     wastedByteCount / 1024f);
    205         }
    206 
    207         long elapsed = System.nanoTime() - start;
    208         System.out.printf("Merged dex A (%d defs/%.1fKiB) with dex B "
    209                 + "(%d defs/%.1fKiB). Result is %d defs/%.1fKiB. Took %.1fs%n",
    210                 dexA.getTableOfContents().classDefs.size,
    211                 dexA.getLength() / 1024f,
    212                 dexB.getTableOfContents().classDefs.size,
    213                 dexB.getLength() / 1024f,
    214                 result.getTableOfContents().classDefs.size,
    215                 result.getLength() / 1024f,
    216                 elapsed / 1000000000f);
    217 
    218         return result;
    219     }
    220 
    221     /**
    222      * Reads an IDs section of two dex files and writes an IDs section of a
    223      * merged dex file. Populates maps from old to new indices in the process.
    224      */
    225     abstract class IdMerger<T extends Comparable<T>> {
    226         private final DexBuffer.Section out;
    227 
    228         protected IdMerger(DexBuffer.Section out) {
    229             this.out = out;
    230         }
    231 
    232         /**
    233          * Merges already-sorted sections, reading only two values into memory
    234          * at a time.
    235          */
    236         public final void mergeSorted() {
    237             TableOfContents.Section aSection = getSection(dexA.getTableOfContents());
    238             TableOfContents.Section bSection = getSection(dexB.getTableOfContents());
    239             getSection(contentsOut).off = out.getPosition();
    240 
    241             DexBuffer.Section inA = aSection.exists() ? dexA.open(aSection.off) : null;
    242             DexBuffer.Section inB = bSection.exists() ? dexB.open(bSection.off) : null;
    243             int aOffset = -1;
    244             int bOffset = -1;
    245             int aIndex = 0;
    246             int bIndex = 0;
    247             int outCount = 0;
    248             T a = null;
    249             T b = null;
    250 
    251             while (true) {
    252                 if (a == null && aIndex < aSection.size) {
    253                     aOffset = inA.getPosition();
    254                     a = read(inA, aIndexMap, aIndex);
    255                 }
    256                 if (b == null && bIndex < bSection.size) {
    257                     bOffset = inB.getPosition();
    258                     b = read(inB, bIndexMap, bIndex);
    259                 }
    260 
    261                 // Write the smaller of a and b. If they're equal, write only once
    262                 boolean advanceA;
    263                 boolean advanceB;
    264                 if (a != null && b != null) {
    265                     int compare = a.compareTo(b);
    266                     advanceA = compare <= 0;
    267                     advanceB = compare >= 0;
    268                 } else {
    269                     advanceA = (a != null);
    270                     advanceB = (b != null);
    271                 }
    272 
    273                 T toWrite = null;
    274                 if (advanceA) {
    275                     toWrite = a;
    276                     updateIndex(aOffset, aIndexMap, aIndex++, outCount);
    277                     a = null;
    278                     aOffset = -1;
    279                 }
    280                 if (advanceB) {
    281                     toWrite = b;
    282                     updateIndex(bOffset, bIndexMap, bIndex++, outCount);
    283                     b = null;
    284                     bOffset = -1;
    285                 }
    286                 if (toWrite == null) {
    287                     break; // advanceA == false && advanceB == false
    288                 }
    289                 write(toWrite);
    290                 outCount++;
    291             }
    292 
    293             getSection(contentsOut).size = outCount;
    294         }
    295 
    296         /**
    297          * Merges unsorted sections by reading them completely into memory and
    298          * sorting in memory.
    299          */
    300         public final void mergeUnsorted() {
    301             getSection(contentsOut).off = out.getPosition();
    302 
    303             List<UnsortedValue> all = new ArrayList<UnsortedValue>();
    304             all.addAll(readUnsortedValues(dexA, aIndexMap));
    305             all.addAll(readUnsortedValues(dexB, bIndexMap));
    306             Collections.sort(all);
    307 
    308             int outCount = 0;
    309             for (int i = 0; i < all.size(); ) {
    310                 UnsortedValue e1 = all.get(i++);
    311                 updateIndex(e1.offset, getIndexMap(e1.source), e1.index, outCount - 1);
    312 
    313                 while (i < all.size() && e1.compareTo(all.get(i)) == 0) {
    314                     UnsortedValue e2 = all.get(i++);
    315                     updateIndex(e2.offset, getIndexMap(e2.source), e2.index, outCount - 1);
    316                 }
    317 
    318                 write(e1.value);
    319                 outCount++;
    320             }
    321 
    322             getSection(contentsOut).size = outCount;
    323         }
    324 
    325         private List<UnsortedValue> readUnsortedValues(DexBuffer source, IndexMap indexMap) {
    326             TableOfContents.Section section = getSection(source.getTableOfContents());
    327             if (!section.exists()) {
    328                 return Collections.emptyList();
    329             }
    330 
    331             List<UnsortedValue> result = new ArrayList<UnsortedValue>();
    332             DexBuffer.Section in = source.open(section.off);
    333             for (int i = 0; i < section.size; i++) {
    334                 int offset = in.getPosition();
    335                 T value = read(in, indexMap, 0);
    336                 result.add(new UnsortedValue(source, indexMap, value, i, offset));
    337             }
    338             return result;
    339         }
    340 
    341         abstract TableOfContents.Section getSection(TableOfContents tableOfContents);
    342         abstract T read(DexBuffer.Section in, IndexMap indexMap, int index);
    343         abstract void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex);
    344         abstract void write(T value);
    345 
    346         class UnsortedValue implements Comparable<UnsortedValue> {
    347             final DexBuffer source;
    348             final IndexMap indexMap;
    349             final T value;
    350             final int index;
    351             final int offset;
    352 
    353             UnsortedValue(DexBuffer source, IndexMap indexMap, T value, int index, int offset) {
    354                 this.source = source;
    355                 this.indexMap = indexMap;
    356                 this.value = value;
    357                 this.index = index;
    358                 this.offset = offset;
    359             }
    360 
    361             public int compareTo(UnsortedValue unsortedValue) {
    362                 return value.compareTo(unsortedValue.value);
    363             }
    364         }
    365     }
    366 
    367     private IndexMap getIndexMap(DexBuffer dexBuffer) {
    368         if (dexBuffer == dexA) {
    369             return aIndexMap;
    370         } else if (dexBuffer == dexB) {
    371             return bIndexMap;
    372         } else {
    373             throw new IllegalArgumentException();
    374         }
    375     }
    376 
    377     private void mergeStringIds() {
    378         new IdMerger<String>(idsDefsOut) {
    379             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
    380                 return tableOfContents.stringIds;
    381             }
    382 
    383             @Override String read(DexBuffer.Section in, IndexMap indexMap, int index) {
    384                 return in.readString();
    385             }
    386 
    387             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
    388                 indexMap.stringIds[oldIndex] = newIndex;
    389             }
    390 
    391             @Override void write(String value) {
    392                 contentsOut.stringDatas.size++;
    393                 idsDefsOut.writeInt(stringDataOut.getPosition());
    394                 stringDataOut.writeStringData(value);
    395             }
    396         }.mergeSorted();
    397     }
    398 
    399     private void mergeTypeIds() {
    400         new IdMerger<Integer>(idsDefsOut) {
    401             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
    402                 return tableOfContents.typeIds;
    403             }
    404 
    405             @Override Integer read(DexBuffer.Section in, IndexMap indexMap, int index) {
    406                 int stringIndex = in.readInt();
    407                 return indexMap.adjustString(stringIndex);
    408             }
    409 
    410             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
    411                 indexMap.typeIds[oldIndex] = (short) newIndex;
    412             }
    413 
    414             @Override void write(Integer value) {
    415                 idsDefsOut.writeInt(value);
    416             }
    417         }.mergeSorted();
    418     }
    419 
    420     private void mergeTypeLists() {
    421         new IdMerger<TypeList>(typeListOut) {
    422             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
    423                 return tableOfContents.typeLists;
    424             }
    425 
    426             @Override TypeList read(DexBuffer.Section in, IndexMap indexMap, int index) {
    427                 return indexMap.adjustTypeList(in.readTypeList());
    428             }
    429 
    430             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
    431                 indexMap.putTypeListOffset(offset, typeListOut.getPosition());
    432             }
    433 
    434             @Override void write(TypeList value) {
    435                 typeListOut.writeTypeList(value);
    436             }
    437         }.mergeUnsorted();
    438     }
    439 
    440     private void mergeProtoIds() {
    441         new IdMerger<ProtoId>(idsDefsOut) {
    442             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
    443                 return tableOfContents.protoIds;
    444             }
    445 
    446             @Override ProtoId read(DexBuffer.Section in, IndexMap indexMap, int index) {
    447                 return indexMap.adjust(in.readProtoId());
    448             }
    449 
    450             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
    451                 indexMap.protoIds[oldIndex] = (short) newIndex;
    452             }
    453 
    454             @Override void write(ProtoId value) {
    455                 value.writeTo(idsDefsOut);
    456             }
    457         }.mergeSorted();
    458     }
    459 
    460     private void mergeFieldIds() {
    461         new IdMerger<FieldId>(idsDefsOut) {
    462             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
    463                 return tableOfContents.fieldIds;
    464             }
    465 
    466             @Override FieldId read(DexBuffer.Section in, IndexMap indexMap, int index) {
    467                 return indexMap.adjust(in.readFieldId());
    468             }
    469 
    470             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
    471                 indexMap.fieldIds[oldIndex] = (short) newIndex;
    472             }
    473 
    474             @Override void write(FieldId value) {
    475                 value.writeTo(idsDefsOut);
    476             }
    477         }.mergeSorted();
    478     }
    479 
    480     private void mergeMethodIds() {
    481         new IdMerger<MethodId>(idsDefsOut) {
    482             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
    483                 return tableOfContents.methodIds;
    484             }
    485 
    486             @Override MethodId read(DexBuffer.Section in, IndexMap indexMap, int index) {
    487                 return indexMap.adjust(in.readMethodId());
    488             }
    489 
    490             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
    491                 indexMap.methodIds[oldIndex] = (short) newIndex;
    492             }
    493 
    494             @Override void write(MethodId methodId) {
    495                 methodId.writeTo(idsDefsOut);
    496             }
    497         }.mergeSorted();
    498     }
    499 
    500     private void mergeAnnotations() {
    501         new IdMerger<Annotation>(annotationOut) {
    502             @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
    503                 return tableOfContents.annotations;
    504             }
    505 
    506             @Override Annotation read(DexBuffer.Section in, IndexMap indexMap, int index) {
    507                 return indexMap.adjust(in.readAnnotation());
    508             }
    509 
    510             @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
    511                 indexMap.putAnnotationOffset(offset, annotationOut.getPosition());
    512             }
    513 
    514             @Override void write(Annotation value) {
    515                 value.writeTo(annotationOut);
    516             }
    517         }.mergeUnsorted();
    518     }
    519 
    520     private void mergeClassDefs() {
    521         SortableType[] types = getSortedTypes();
    522         contentsOut.classDefs.off = idsDefsOut.getPosition();
    523         contentsOut.classDefs.size = types.length;
    524 
    525         for (SortableType type : types) {
    526             DexBuffer in = type.getBuffer();
    527             IndexMap indexMap = (in == dexA) ? aIndexMap : bIndexMap;
    528             transformClassDef(in, type.getClassDef(), indexMap);
    529         }
    530     }
    531 
    532     /**
    533      * Returns the union of classes from both files, sorted in order such that
    534      * a class is always preceded by its supertype and implemented interfaces.
    535      */
    536     private SortableType[] getSortedTypes() {
    537         // size is pessimistic; doesn't include arrays
    538         SortableType[] sortableTypes = new SortableType[contentsOut.typeIds.size];
    539         readSortableTypes(sortableTypes, dexA, aIndexMap);
    540         readSortableTypes(sortableTypes, dexB, bIndexMap);
    541 
    542         /*
    543          * Populate the depths of each sortable type. This makes D iterations
    544          * through all N types, where 'D' is the depth of the deepest type. For
    545          * example, the deepest class in libcore is Xalan's KeyIterator, which
    546          * is 11 types deep.
    547          */
    548         while (true) {
    549             boolean allDone = true;
    550             for (SortableType sortableType : sortableTypes) {
    551                 if (sortableType != null && !sortableType.isDepthAssigned()) {
    552                     allDone &= sortableType.tryAssignDepth(sortableTypes);
    553                 }
    554             }
    555             if (allDone) {
    556                 break;
    557             }
    558         }
    559 
    560         // Now that all types have depth information, the result can be sorted
    561         Arrays.sort(sortableTypes, SortableType.NULLS_LAST_ORDER);
    562 
    563         // Strip nulls from the end
    564         int firstNull = Arrays.asList(sortableTypes).indexOf(null);
    565         return firstNull != -1
    566                 ? Arrays.copyOfRange(sortableTypes, 0, firstNull)
    567                 : sortableTypes;
    568     }
    569 
    570     /**
    571      * Reads just enough data on each class so that we can sort it and then find
    572      * it later.
    573      */
    574     private void readSortableTypes(SortableType[] sortableTypes, DexBuffer buffer,
    575             IndexMap indexMap) {
    576         for (ClassDef classDef : buffer.classDefs()) {
    577             SortableType sortableType = indexMap.adjust(new SortableType(buffer, classDef));
    578             int t = sortableType.getTypeIndex();
    579             if (sortableTypes[t] == null) {
    580                 sortableTypes[t] = sortableType;
    581             } else if (collisionPolicy != CollisionPolicy.KEEP_FIRST) {
    582                 throw new DexException("Multiple dex files define "
    583                         + buffer.typeNames().get(classDef.getTypeIndex()));
    584             }
    585         }
    586     }
    587 
    588     /**
    589      * Copy annotation sets from each input to the output.
    590      *
    591      * TODO: this may write multiple copies of the same annotation set.
    592      * We should shrink the output by merging rather than unioning
    593      */
    594     private void unionAnnotationSetsAndDirectories() {
    595         transformAnnotationSets(dexA, aIndexMap);
    596         transformAnnotationSets(dexB, bIndexMap);
    597         transformAnnotationDirectories(dexA, aIndexMap);
    598         transformAnnotationDirectories(dexB, bIndexMap);
    599         transformStaticValues(dexA, aIndexMap);
    600         transformStaticValues(dexB, bIndexMap);
    601     }
    602 
    603     private void transformAnnotationSets(DexBuffer in, IndexMap indexMap) {
    604         TableOfContents.Section section = in.getTableOfContents().annotationSets;
    605         if (section.exists()) {
    606             DexBuffer.Section setIn = in.open(section.off);
    607             for (int i = 0; i < section.size; i++) {
    608                 transformAnnotationSet(indexMap, setIn);
    609             }
    610         }
    611     }
    612 
    613     private void transformAnnotationDirectories(DexBuffer in, IndexMap indexMap) {
    614         TableOfContents.Section section = in.getTableOfContents().annotationsDirectories;
    615         if (section.exists()) {
    616             DexBuffer.Section directoryIn = in.open(section.off);
    617             for (int i = 0; i < section.size; i++) {
    618                 transformAnnotationDirectory(in, directoryIn, indexMap);
    619             }
    620         }
    621     }
    622 
    623     private void transformStaticValues(DexBuffer in, IndexMap indexMap) {
    624         TableOfContents.Section section = in.getTableOfContents().encodedArrays;
    625         if (section.exists()) {
    626             DexBuffer.Section staticValuesIn = in.open(section.off);
    627             for (int i = 0; i < section.size; i++) {
    628                 transformStaticValues(staticValuesIn, indexMap);
    629             }
    630         }
    631     }
    632 
    633     /**
    634      * Reads a class_def_item beginning at {@code in} and writes the index and
    635      * data.
    636      */
    637     private void transformClassDef(DexBuffer in, ClassDef classDef, IndexMap indexMap) {
    638         idsDefsOut.assertFourByteAligned();
    639         idsDefsOut.writeInt(classDef.getTypeIndex());
    640         idsDefsOut.writeInt(classDef.getAccessFlags());
    641         idsDefsOut.writeInt(classDef.getSupertypeIndex());
    642         idsDefsOut.writeInt(classDef.getInterfacesOffset());
    643 
    644         int sourceFileIndex = indexMap.adjustString(classDef.getSourceFileIndex());
    645         idsDefsOut.writeInt(sourceFileIndex);
    646 
    647         int annotationsOff = classDef.getAnnotationsOffset();
    648         idsDefsOut.writeInt(indexMap.adjustAnnotationDirectory(annotationsOff));
    649 
    650         int classDataOff = classDef.getClassDataOffset();
    651         if (classDataOff == 0) {
    652             idsDefsOut.writeInt(0);
    653         } else {
    654             idsDefsOut.writeInt(classDataOut.getPosition());
    655             ClassData classData = in.readClassData(classDef);
    656             transformClassData(in, classData, indexMap);
    657         }
    658 
    659         int staticValuesOff = classDef.getStaticValuesOffset();
    660         idsDefsOut.writeInt(indexMap.adjustStaticValues(staticValuesOff));
    661     }
    662 
    663     /**
    664      * Transform all annotations on a class.
    665      */
    666     private void transformAnnotationDirectory(
    667             DexBuffer in, DexBuffer.Section directoryIn, IndexMap indexMap) {
    668         contentsOut.annotationsDirectories.size++;
    669         annotationsDirectoryOut.assertFourByteAligned();
    670         indexMap.putAnnotationDirectoryOffset(
    671                 directoryIn.getPosition(), annotationsDirectoryOut.getPosition());
    672 
    673         int classAnnotationsOffset = indexMap.adjustAnnotationSet(directoryIn.readInt());
    674         annotationsDirectoryOut.writeInt(classAnnotationsOffset);
    675 
    676         int fieldsSize = directoryIn.readInt();
    677         annotationsDirectoryOut.writeInt(fieldsSize);
    678 
    679         int methodsSize = directoryIn.readInt();
    680         annotationsDirectoryOut.writeInt(methodsSize);
    681 
    682         int parameterListSize = directoryIn.readInt();
    683         annotationsDirectoryOut.writeInt(parameterListSize);
    684 
    685         for (int i = 0; i < fieldsSize; i++) {
    686             // field index
    687             annotationsDirectoryOut.writeInt(indexMap.adjustField(directoryIn.readInt()));
    688 
    689             // annotations offset
    690             annotationsDirectoryOut.writeInt(indexMap.adjustAnnotationSet(directoryIn.readInt()));
    691         }
    692 
    693         for (int i = 0; i < methodsSize; i++) {
    694             // method index
    695             annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt()));
    696 
    697             // annotation set offset
    698             annotationsDirectoryOut.writeInt(
    699                     indexMap.adjustAnnotationSet(directoryIn.readInt()));
    700         }
    701 
    702         for (int i = 0; i < parameterListSize; i++) {
    703             contentsOut.annotationSetRefLists.size++;
    704             annotationSetRefListOut.assertFourByteAligned();
    705 
    706             // method index
    707             annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt()));
    708 
    709             // annotations offset
    710             annotationsDirectoryOut.writeInt(annotationSetRefListOut.getPosition());
    711             DexBuffer.Section refListIn = in.open(directoryIn.readInt());
    712 
    713             // parameters
    714             int parameterCount = refListIn.readInt();
    715             annotationSetRefListOut.writeInt(parameterCount);
    716             for (int p = 0; p < parameterCount; p++) {
    717                 annotationSetRefListOut.writeInt(indexMap.adjustAnnotationSet(refListIn.readInt()));
    718             }
    719         }
    720     }
    721 
    722     /**
    723      * Transform all annotations on a single type, member or parameter.
    724      */
    725     private void transformAnnotationSet(IndexMap indexMap, DexBuffer.Section setIn) {
    726         contentsOut.annotationSets.size++;
    727         annotationSetOut.assertFourByteAligned();
    728         indexMap.putAnnotationSetOffset(setIn.getPosition(), annotationSetOut.getPosition());
    729 
    730         int size = setIn.readInt();
    731         annotationSetOut.writeInt(size);
    732 
    733         for (int j = 0; j < size; j++) {
    734             annotationSetOut.writeInt(indexMap.adjustAnnotation(setIn.readInt()));
    735         }
    736     }
    737 
    738     private void transformClassData(DexBuffer in, ClassData classData, IndexMap indexMap) {
    739         contentsOut.classDatas.size++;
    740 
    741         ClassData.Field[] staticFields = classData.getStaticFields();
    742         ClassData.Field[] instanceFields = classData.getInstanceFields();
    743         ClassData.Method[] directMethods = classData.getDirectMethods();
    744         ClassData.Method[] virtualMethods = classData.getVirtualMethods();
    745 
    746         classDataOut.writeUleb128(staticFields.length);
    747         classDataOut.writeUleb128(instanceFields.length);
    748         classDataOut.writeUleb128(directMethods.length);
    749         classDataOut.writeUleb128(virtualMethods.length);
    750 
    751         transformFields(indexMap, staticFields);
    752         transformFields(indexMap, instanceFields);
    753         transformMethods(in, indexMap, directMethods);
    754         transformMethods(in, indexMap, virtualMethods);
    755     }
    756 
    757     private void transformFields(IndexMap indexMap, ClassData.Field[] fields) {
    758         int lastOutFieldIndex = 0;
    759         for (ClassData.Field field : fields) {
    760             int outFieldIndex = indexMap.adjustField(field.getFieldIndex());
    761             classDataOut.writeUleb128(outFieldIndex - lastOutFieldIndex);
    762             lastOutFieldIndex = outFieldIndex;
    763             classDataOut.writeUleb128(field.getAccessFlags());
    764         }
    765     }
    766 
    767     private void transformMethods(DexBuffer in, IndexMap indexMap, ClassData.Method[] methods) {
    768         int lastOutMethodIndex = 0;
    769         for (ClassData.Method method : methods) {
    770             int outMethodIndex = indexMap.adjustMethod(method.getMethodIndex());
    771             classDataOut.writeUleb128(outMethodIndex - lastOutMethodIndex);
    772             lastOutMethodIndex = outMethodIndex;
    773 
    774             classDataOut.writeUleb128(method.getAccessFlags());
    775 
    776             if (method.getCodeOffset() == 0) {
    777                 classDataOut.writeUleb128(0);
    778             } else {
    779                 codeOut.alignToFourBytes();
    780                 classDataOut.writeUleb128(codeOut.getPosition());
    781                 transformCode(in, in.readCode(method), indexMap);
    782             }
    783         }
    784     }
    785 
    786     private void transformCode(DexBuffer in, Code code, IndexMap indexMap) {
    787         contentsOut.codes.size++;
    788         codeOut.assertFourByteAligned();
    789 
    790         codeOut.writeUnsignedShort(code.getRegistersSize());
    791         codeOut.writeUnsignedShort(code.getInsSize());
    792         codeOut.writeUnsignedShort(code.getOutsSize());
    793 
    794         Code.Try[] tries = code.getTries();
    795         codeOut.writeUnsignedShort(tries.length);
    796 
    797         // TODO: retain debug info
    798         // code.getDebugInfoOffset();
    799         codeOut.writeInt(0);
    800 
    801         short[] instructions = code.getInstructions();
    802         InstructionTransformer transformer = (in == dexA)
    803                 ? aInstructionTransformer
    804                 : bInstructionTransformer;
    805         short[] newInstructions = transformer.transform(instructions);
    806         codeOut.writeInt(newInstructions.length);
    807         codeOut.write(newInstructions);
    808 
    809         if (tries.length > 0) {
    810             if (newInstructions.length % 2 == 1) {
    811                 codeOut.writeShort((short) 0); // padding
    812             }
    813             for (Code.Try tryItem : tries) {
    814                 codeOut.writeInt(tryItem.getStartAddress());
    815                 codeOut.writeUnsignedShort(tryItem.getInstructionCount());
    816                 codeOut.writeUnsignedShort(tryItem.getHandlerOffset());
    817             }
    818             Code.CatchHandler[] catchHandlers = code.getCatchHandlers();
    819             codeOut.writeUleb128(catchHandlers.length);
    820             for (Code.CatchHandler catchHandler : catchHandlers) {
    821                 transformEncodedCatchHandler(catchHandler, indexMap);
    822             }
    823         }
    824     }
    825 
    826     private void transformEncodedCatchHandler(Code.CatchHandler catchHandler, IndexMap indexMap) {
    827         int catchAllAddress = catchHandler.getCatchAllAddress();
    828         int[] typeIndexes = catchHandler.getTypeIndexes();
    829         int[] addresses = catchHandler.getAddresses();
    830 
    831         if (catchAllAddress != -1) {
    832             codeOut.writeSleb128(-typeIndexes.length);
    833         } else {
    834             codeOut.writeSleb128(typeIndexes.length);
    835         }
    836 
    837         for (int i = 0; i < typeIndexes.length; i++) {
    838             codeOut.writeUleb128(indexMap.adjustType(typeIndexes[i]));
    839             codeOut.writeUleb128(addresses[i]);
    840         }
    841 
    842         if (catchAllAddress != -1) {
    843             codeOut.writeUleb128(catchAllAddress);
    844         }
    845     }
    846 
    847     private void transformStaticValues(DexBuffer.Section in, IndexMap indexMap) {
    848         contentsOut.encodedArrays.size++;
    849         indexMap.putStaticValuesOffset(in.getPosition(), encodedArrayOut.getPosition());
    850         indexMap.adjustEncodedArray(in.readEncodedArray()).writeTo(encodedArrayOut);
    851     }
    852 
    853     /**
    854      * Byte counts for the sections written when creating a dex. Target sizes
    855      * are defined in one of two ways:
    856      * <ul>
    857      * <li>By pessimistically guessing how large the union of dex files will be.
    858      *     We're pessimistic because we can't predict the amount of duplication
    859      *     between dex files, nor can we predict the length of ULEB-encoded
    860      *     offsets or indices.
    861      * <li>By exactly measuring an existing dex.
    862      * </ul>
    863      */
    864     private static class WriterSizes {
    865         private int header = SizeOf.HEADER_ITEM;
    866         private int idsDefs;
    867         private int mapList;
    868         private int typeList;
    869         private int classData;
    870         private int code;
    871         private int stringData;
    872         private int debugInfo;
    873         private int encodedArray;
    874         private int annotationsDirectory;
    875         private int annotationsSet;
    876         private int annotationsSetRefList;
    877         private int annotation;
    878 
    879         /**
    880          * Compute sizes for merging a and b.
    881          */
    882         public WriterSizes(DexBuffer a, DexBuffer b) {
    883             plus(a.getTableOfContents(), false);
    884             plus(b.getTableOfContents(), false);
    885         }
    886 
    887         public WriterSizes(DexMerger dexMerger) {
    888             header = dexMerger.headerOut.used();
    889             idsDefs = dexMerger.idsDefsOut.used();
    890             mapList = dexMerger.mapListOut.used();
    891             typeList = dexMerger.typeListOut.used();
    892             classData = dexMerger.classDataOut.used();
    893             code = dexMerger.codeOut.used();
    894             stringData = dexMerger.stringDataOut.used();
    895             debugInfo = dexMerger.debugInfoOut.used();
    896             encodedArray = dexMerger.encodedArrayOut.used();
    897             annotationsDirectory = dexMerger.annotationsDirectoryOut.used();
    898             annotationsSet = dexMerger.annotationSetOut.used();
    899             annotationsSetRefList = dexMerger.annotationSetRefListOut.used();
    900             annotation = dexMerger.annotationOut.used();
    901         }
    902 
    903         public void plus(TableOfContents contents, boolean exact) {
    904             idsDefs += contents.stringIds.size * SizeOf.STRING_ID_ITEM
    905                     + contents.typeIds.size * SizeOf.TYPE_ID_ITEM
    906                     + contents.protoIds.size * SizeOf.PROTO_ID_ITEM
    907                     + contents.fieldIds.size * SizeOf.MEMBER_ID_ITEM
    908                     + contents.methodIds.size * SizeOf.MEMBER_ID_ITEM
    909                     + contents.classDefs.size * SizeOf.CLASS_DEF_ITEM;
    910             mapList = SizeOf.UINT + (contents.sections.length * SizeOf.MAP_ITEM);
    911             typeList += contents.typeLists.byteCount;
    912             stringData += contents.stringDatas.byteCount;
    913             debugInfo += contents.debugInfos.byteCount;
    914             annotationsDirectory += contents.annotationsDirectories.byteCount;
    915             annotationsSet += contents.annotationSets.byteCount;
    916             annotationsSetRefList += contents.annotationSetRefLists.byteCount;
    917 
    918             if (exact) {
    919                 code += contents.codes.byteCount;
    920                 classData += contents.classDatas.byteCount;
    921                 encodedArray += contents.encodedArrays.byteCount;
    922                 annotation += contents.annotations.byteCount;
    923             } else {
    924                 // at most 1/4 of the bytes in a code section are uleb/sleb
    925                 code += (int) Math.ceil(contents.codes.byteCount * 1.25);
    926                 // at most 1/3 of the bytes in a class data section are uleb/sleb
    927                 classData += (int) Math.ceil(contents.classDatas.byteCount * 1.34);
    928                 // all of the bytes in an encoding arrays section may be uleb/sleb
    929                 encodedArray += contents.encodedArrays.byteCount * 2;
    930                 // at most 1/3 of the bytes in an encoding arrays section are uleb/sleb
    931                 annotation += (int) Math.ceil(contents.annotations.byteCount * 1.34);
    932             }
    933 
    934             typeList = DexBuffer.fourByteAlign(typeList);
    935             code = DexBuffer.fourByteAlign(code);
    936         }
    937 
    938         public int size() {
    939             return header + idsDefs + mapList + typeList + classData + code + stringData + debugInfo
    940                     + encodedArray + annotationsDirectory + annotationsSet + annotationsSetRefList
    941                     + annotation;
    942         }
    943     }
    944 
    945     public static void main(String[] args) throws IOException {
    946         if (args.length != 3) {
    947             printUsage();
    948             return;
    949         }
    950 
    951         DexBuffer dexA = new DexBuffer(new File(args[1]));
    952         DexBuffer dexB = new DexBuffer(new File(args[2]));
    953         DexBuffer merged = new DexMerger(dexA, dexB, CollisionPolicy.KEEP_FIRST).merge();
    954         merged.writeTo(new File(args[0]));
    955     }
    956 
    957     private static void printUsage() {
    958         System.out.println("Usage: DexMerger <out.dex> <a.dex> <b.dex>");
    959         System.out.println();
    960         System.out.println("If both a and b define the same classes, a's copy will be used.");
    961     }
    962 }
    963