DexWriter.java revision 242dd62a714d5d114b35437b5f39a5b2e107dcfe
1/*
2 * Copyright 2013, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.dexlib2.writer;
33
34import com.google.common.collect.Iterables;
35import com.google.common.collect.Lists;
36import com.google.common.collect.Maps;
37import com.google.common.collect.Ordering;
38import org.jf.dexlib2.AccessFlags;
39import org.jf.dexlib2.base.BaseAnnotation;
40import org.jf.dexlib2.dexbacked.raw.*;
41import org.jf.dexlib2.iface.Annotation;
42import org.jf.dexlib2.iface.TryBlock;
43import org.jf.dexlib2.iface.debug.LineNumber;
44import org.jf.dexlib2.iface.instruction.Instruction;
45import org.jf.dexlib2.iface.instruction.formats.*;
46import org.jf.dexlib2.iface.reference.FieldReference;
47import org.jf.dexlib2.iface.reference.MethodReference;
48import org.jf.dexlib2.iface.reference.StringReference;
49import org.jf.dexlib2.iface.reference.TypeReference;
50import org.jf.dexlib2.util.MethodUtil;
51import org.jf.dexlib2.writer.util.InstructionWriteUtil;
52import org.jf.dexlib2.writer.util.TryListBuilder;
53import org.jf.util.CollectionUtils;
54import org.jf.util.ExceptionWithContext;
55import org.jf.util.RandomAccessFileOutputStream;
56
57import javax.annotation.Nonnull;
58import javax.annotation.Nullable;
59import java.io.ByteArrayOutputStream;
60import java.io.IOException;
61import java.io.RandomAccessFile;
62import java.nio.ByteBuffer;
63import java.nio.ByteOrder;
64import java.nio.channels.FileChannel;
65import java.security.MessageDigest;
66import java.security.NoSuchAlgorithmException;
67import java.util.*;
68import java.util.Map.Entry;
69import java.util.zip.Adler32;
70
71public abstract class DexWriter<
72        StringKey extends CharSequence, StringRef extends StringReference, TypeKey extends CharSequence,
73        TypeRef extends TypeReference, ProtoKey extends Comparable<ProtoKey>,
74        FieldRefKey extends FieldReference, MethodRefKey extends MethodReference,
75        ClassKey extends Comparable<? super ClassKey>,
76        AnnotationKey extends Annotation, AnnotationSetKey, AnnotationSetRefKey,
77        TypeListKey,
78        FieldKey extends FieldRefKey, MethodKey extends MethodRefKey,
79        EncodedValue, AnnotationElement,
80        DebugItem extends org.jf.dexlib2.iface.debug.DebugItem,
81        Insn extends Instruction, ExceptionHandler extends org.jf.dexlib2.iface.ExceptionHandler> {
82    public static final int NO_INDEX = -1;
83    public static final int NO_OFFSET = 0;
84
85    protected int stringIndexSectionOffset = NO_OFFSET;
86    protected int typeSectionOffset = NO_OFFSET;
87    protected int protoSectionOffset = NO_OFFSET;
88    protected int fieldSectionOffset = NO_OFFSET;
89    protected int methodSectionOffset = NO_OFFSET;
90    protected int classIndexSectionOffset = NO_OFFSET;
91
92    protected int stringDataSectionOffset = NO_OFFSET;
93    protected int classDataSectionOffset = NO_OFFSET;
94    protected int typeListSectionOffset = NO_OFFSET;
95    protected int encodedArraySectionOffset = NO_OFFSET;
96    protected int annotationSectionOffset = NO_OFFSET;
97    protected int annotationSetSectionOffset = NO_OFFSET;
98    protected int annotationSetRefSectionOffset = NO_OFFSET;
99    protected int annotationDirectorySectionOffset = NO_OFFSET;
100    protected int debugSectionOffset = NO_OFFSET;
101    protected int codeSectionOffset = NO_OFFSET;
102    protected int mapSectionOffset = NO_OFFSET;
103
104    protected int numEncodedArrayItems = 0;
105    protected int numAnnotationDirectoryPoolItems = 0;
106    protected int numDebugInfoPoolItems = 0;
107    protected int numCodeItemPoolItems = 0;
108    protected int numClassDataItems = 0;
109
110    protected InstructionFactory<? extends Insn> instructionFactory;
111
112    protected StringSection<StringKey, StringRef> stringSection;
113    protected TypeSection<StringKey, TypeKey, TypeRef> typeSection;
114    protected ProtoSection<StringKey, TypeKey, ProtoKey, TypeListKey> protoSection;
115    protected FieldSection<StringKey, TypeKey, FieldRefKey> fieldSection;
116    protected MethodSection<StringKey, TypeKey, ProtoKey, MethodRefKey> methodSection;
117    protected ClassSection<StringKey, TypeKey, TypeListKey, ClassKey, FieldKey, MethodKey, AnnotationSetKey,
118            AnnotationSetRefKey, EncodedValue, DebugItem, Insn, ExceptionHandler> classSection;
119
120    protected TypeListSection<TypeKey, TypeListKey> typeListSection;
121    protected AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement, EncodedValue> annotationSection;
122    protected AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection;
123    protected AnnotationSetRefSection<AnnotationSetKey, AnnotationSetRefKey> annotationSetRefSection;
124
125    protected DexWriter(InstructionFactory<? extends Insn> instructionFactory,
126                        StringSection<StringKey, StringRef> stringSection,
127                        TypeSection<StringKey, TypeKey, TypeRef> typeSection,
128                        ProtoSection<StringKey, TypeKey, ProtoKey, TypeListKey> protoSection,
129                        FieldSection<StringKey, TypeKey, FieldRefKey> fieldSection,
130                        MethodSection<StringKey, TypeKey, ProtoKey, MethodRefKey> methodSection,
131                        ClassSection<StringKey, TypeKey, TypeListKey, ClassKey, FieldKey, MethodKey, AnnotationSetKey,
132                                AnnotationSetRefKey, EncodedValue, DebugItem, Insn, ExceptionHandler> classSection,
133                        TypeListSection<TypeKey, TypeListKey> typeListSection,
134                        AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement,
135                                EncodedValue> annotationSection,
136                        AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection,
137                        AnnotationSetRefSection<AnnotationSetKey, AnnotationSetRefKey> annotationSetRefSection) {
138        this.instructionFactory = instructionFactory;
139        this.stringSection = stringSection;
140        this.typeSection = typeSection;
141        this.protoSection = protoSection;
142        this.fieldSection = fieldSection;
143        this.methodSection = methodSection;
144        this.classSection = classSection;
145        this.typeListSection = typeListSection;
146        this.annotationSection = annotationSection;
147        this.annotationSetSection = annotationSetSection;
148        this.annotationSetRefSection = annotationSetRefSection;
149    }
150
151    protected abstract void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer,
152                                              @Nonnull EncodedValue encodedValue) throws IOException;
153
154    private static Comparator<Map.Entry> toStringKeyComparator =
155            new Comparator<Map.Entry>() {
156                @Override public int compare(Entry o1, Entry o2) {
157                    return o1.getKey().toString().compareTo(o2.getKey().toString());
158                }
159            };
160
161    private static <T extends Comparable<? super T>> Comparator<Map.Entry<? extends T, ?>> comparableKeyComparator() {
162        return new Comparator<Entry<? extends T, ?>>() {
163            @Override public int compare(Entry<? extends T, ?> o1, Entry<? extends T, ?> o2) {
164                return o1.getKey().compareTo(o2.getKey());
165            }
166        };
167    }
168
169    protected class InternalEncodedValueWriter extends EncodedValueWriter<StringKey, TypeKey, FieldRefKey, MethodRefKey,
170            AnnotationElement, EncodedValue> {
171        private InternalEncodedValueWriter(@Nonnull DexDataWriter writer) {
172            super(writer, stringSection, typeSection, fieldSection, methodSection, annotationSection);
173        }
174
175        @Override protected void writeEncodedValue(@Nonnull EncodedValue encodedValue) throws IOException {
176            DexWriter.this.writeEncodedValue(this, encodedValue);
177        }
178    }
179
180    private int getDataSectionOffset() {
181        return HeaderItem.ITEM_SIZE +
182                stringSection.getItems().size() * StringIdItem.ITEM_SIZE +
183                typeSection.getItems().size() * TypeIdItem.ITEM_SIZE +
184                protoSection.getItems().size() * ProtoIdItem.ITEM_SIZE +
185                fieldSection.getItems().size() * FieldIdItem.ITEM_SIZE +
186                methodSection.getItems().size() * MethodIdItem.ITEM_SIZE +
187                classSection.getItems().size() * ClassDefItem.ITEM_SIZE;
188    }
189
190    public void writeTo(String path) throws IOException {
191        RandomAccessFile raf = new RandomAccessFile(path, "rw");
192        raf.setLength(0);
193        try {
194            int dataSectionOffset = getDataSectionOffset();
195            DexDataWriter headerWriter  = outputAt(raf, 0);
196            DexDataWriter indexWriter   = outputAt(raf, HeaderItem.ITEM_SIZE);
197            DexDataWriter offsetWriter  = outputAt(raf, dataSectionOffset);
198            try {
199                writeStrings(indexWriter, offsetWriter);
200                writeTypes(indexWriter);
201                writeTypeLists(offsetWriter);
202                writeProtos(indexWriter);
203                writeFields(indexWriter);
204                writeMethods(indexWriter);
205                writeEncodedArrays(offsetWriter);
206                writeAnnotations(offsetWriter);
207                writeAnnotationSets(offsetWriter);
208                writeAnnotationSetRefs(offsetWriter);
209                writeAnnotationDirectories(offsetWriter);
210                writeDebugItems(offsetWriter);
211                writeCodeItems(offsetWriter);
212                writeClasses(indexWriter, offsetWriter);
213                writeMapItem(offsetWriter);
214                writeHeader(headerWriter, dataSectionOffset, offsetWriter.getPosition());
215            } finally {
216                headerWriter.close();
217                indexWriter.close();
218                offsetWriter.close();
219            }
220            FileChannel fileChannel = raf.getChannel();
221            updateSignature(fileChannel);
222            updateChecksum(fileChannel);
223        } finally {
224            raf.close();
225        }
226    }
227
228    private void updateSignature(FileChannel fileChannel) throws IOException {
229        MessageDigest md;
230        try {
231            md = MessageDigest.getInstance("SHA-1");
232        } catch (NoSuchAlgorithmException ex) {
233            throw new RuntimeException(ex);
234        }
235
236        ByteBuffer buffer = ByteBuffer.allocate(128 * 1024);
237        fileChannel.position(HeaderItem.HEADER_SIZE_OFFSET);
238        int bytesRead = fileChannel.read(buffer);
239        while (bytesRead >= 0) {
240            buffer.rewind();
241            md.update(buffer);
242            buffer.clear();
243            bytesRead = fileChannel.read(buffer);
244        }
245
246        byte[] signature = md.digest();
247        if (signature.length != HeaderItem.SIGNATURE_SIZE) {
248            throw new RuntimeException("unexpected digest write: " + signature.length + " bytes");
249        }
250
251        // write signature
252        fileChannel.position(HeaderItem.SIGNATURE_OFFSET);
253        fileChannel.write(ByteBuffer.wrap(signature));
254
255        // flush
256        fileChannel.force(false);
257    }
258
259    private void updateChecksum(FileChannel fileChannel) throws IOException {
260        Adler32 a32 = new Adler32();
261
262        ByteBuffer buffer = ByteBuffer.allocate(128 * 1024);
263        fileChannel.position(HeaderItem.SIGNATURE_OFFSET);
264        int bytesRead = fileChannel.read(buffer);
265        while (bytesRead >= 0) {
266            a32.update(buffer.array(), 0, bytesRead);
267            buffer.clear();
268            bytesRead = fileChannel.read(buffer);
269        }
270
271        // write checksum, utilizing logic in DexWriter to write the integer value properly
272        fileChannel.position(HeaderItem.CHECKSUM_OFFSET);
273        int checksum = (int) a32.getValue();
274        ByteArrayOutputStream checksumBuf = new ByteArrayOutputStream();
275        DexDataWriter.writeInt(checksumBuf, checksum);
276        fileChannel.write(ByteBuffer.wrap(checksumBuf.toByteArray()));
277
278        // flush
279        fileChannel.force(false);
280    }
281
282    private static DexDataWriter outputAt(RandomAccessFile raf, int filePosition) throws IOException {
283        return new DexDataWriter(new RandomAccessFileOutputStream(raf, filePosition), filePosition);
284    }
285
286    private void writeStrings(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter) throws IOException {
287        stringIndexSectionOffset = indexWriter.getPosition();
288        stringDataSectionOffset = offsetWriter.getPosition();
289        int index = 0;
290        List<Entry<? extends StringKey, Integer>> stringEntries = Lists.newArrayList(stringSection.getItems());
291        Collections.sort(stringEntries, toStringKeyComparator);
292
293        for (Map.Entry<? extends StringKey, Integer>  entry: stringEntries) {
294            entry.setValue(index++);
295            indexWriter.writeInt(offsetWriter.getPosition());
296            String stringValue = entry.getKey().toString();
297            offsetWriter.writeUleb128(stringValue.length());
298            offsetWriter.writeString(stringValue);
299            offsetWriter.write(0);
300        }
301    }
302
303    private void writeTypes(@Nonnull DexDataWriter writer) throws IOException {
304        typeSectionOffset = writer.getPosition();
305        int index = 0;
306
307        List<Map.Entry<? extends TypeKey, Integer>> typeEntries = Lists.newArrayList(typeSection.getItems());
308        Collections.sort(typeEntries, toStringKeyComparator);
309
310        for (Map.Entry<? extends TypeKey, Integer> entry : typeEntries) {
311            entry.setValue(index++);
312            writer.writeInt(stringSection.getItemIndex(typeSection.getString(entry.getKey())));
313        }
314    }
315
316    private void writeProtos(@Nonnull DexDataWriter writer) throws IOException {
317        protoSectionOffset = writer.getPosition();
318        int index = 0;
319
320        List<Map.Entry<? extends ProtoKey, Integer>> protoEntries = Lists.newArrayList(protoSection.getItems());
321        Collections.sort(protoEntries, DexWriter.<ProtoKey>comparableKeyComparator());
322
323        for (Map.Entry<? extends ProtoKey, Integer> entry: protoEntries) {
324            entry.setValue(index++);
325            ProtoKey key = entry.getKey();
326            writer.writeInt(stringSection.getItemIndex(protoSection.getShorty(key)));
327            writer.writeInt(typeSection.getItemIndex(protoSection.getReturnType(key)));
328            writer.writeInt(typeListSection.getNullableItemOffset(protoSection.getParameters(key)));
329        }
330    }
331
332    private void writeFields(@Nonnull DexDataWriter writer) throws IOException {
333        fieldSectionOffset = writer.getPosition();
334        int index = 0;
335
336        List<Map.Entry<? extends FieldRefKey, Integer>> fieldEntries = Lists.newArrayList(fieldSection.getItems());
337        Collections.sort(fieldEntries, DexWriter.<FieldRefKey>comparableKeyComparator());
338
339        for (Map.Entry<? extends FieldRefKey, Integer> entry: fieldEntries) {
340            entry.setValue(index++);
341            FieldRefKey key = entry.getKey();
342            writer.writeUshort(typeSection.getItemIndex(fieldSection.getDefiningClass(key)));
343            writer.writeUshort(typeSection.getItemIndex(fieldSection.getFieldType(key)));
344            writer.writeInt(stringSection.getItemIndex(fieldSection.getName(key)));
345        }
346    }
347
348    private void writeMethods(@Nonnull DexDataWriter writer) throws IOException {
349        methodSectionOffset = writer.getPosition();
350        int index = 0;
351
352        List<Map.Entry<? extends MethodRefKey, Integer>> methodEntries = Lists.newArrayList(methodSection.getItems());
353        Collections.sort(methodEntries, DexWriter.<MethodRefKey>comparableKeyComparator());
354
355        for (Map.Entry<? extends MethodRefKey, Integer> entry: methodEntries) {
356            entry.setValue(index++);
357            MethodRefKey key = entry.getKey();
358            writer.writeUshort(typeSection.getItemIndex(methodSection.getDefiningClass(key)));
359            writer.writeUshort(protoSection.getItemIndex(methodSection.getPrototype(key)));
360            writer.writeInt(stringSection.getItemIndex(methodSection.getName(key)));
361        }
362    }
363
364    private void writeClasses(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter) throws IOException {
365        classIndexSectionOffset = indexWriter.getPosition();
366        classDataSectionOffset = offsetWriter.getPosition();
367
368        List<Map.Entry<? extends ClassKey, Integer>> classEntries = Lists.newArrayList(classSection.getItems());
369        Collections.sort(classEntries, DexWriter.<ClassKey>comparableKeyComparator());
370
371        int index = 0;
372        for (Map.Entry<? extends ClassKey, Integer> key: classEntries) {
373            index = writeClass(indexWriter, offsetWriter, index, key);
374        }
375    }
376
377    /**
378     * Writes out the class_def_item and class_data_item for the given class.
379     *
380     * This will recursively write out any unwritten superclass/interface before writing the class itself, as per the
381     * dex specification.
382     *
383     * @return the index for the next class to be written
384     */
385    private int writeClass(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter,
386                           int nextIndex, @Nullable Map.Entry<? extends ClassKey, Integer> entry) throws IOException {
387        if (entry == null) {
388            // class does not exist in this dex file, cannot write it
389            return nextIndex;
390        }
391
392        if (entry.getValue() != NO_INDEX) {
393            // class has already been written, no need to write it
394            return nextIndex;
395        }
396
397        ClassKey key = entry.getKey();
398
399        // set a bogus index, to make sure we don't recuse and double-write it
400        entry.setValue(0);
401
402        // first, try to write the superclass
403        Map.Entry<? extends ClassKey, Integer> superEntry =
404                classSection.getClassEntryByType(classSection.getSuperclass(key));
405        nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, superEntry);
406
407        // then, try to write interfaces
408        for (TypeKey interfaceTypeKey: typeListSection.getTypes(classSection.getSortedInterfaces(key))) {
409            Map.Entry<? extends ClassKey, Integer> interfaceEntry = classSection.getClassEntryByType(interfaceTypeKey);
410            nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, interfaceEntry);
411        }
412
413        // now set the index for real
414        entry.setValue(nextIndex++);
415
416        // and finally, write the class itself
417        // first, the class_def_item
418        indexWriter.writeInt(typeSection.getItemIndex(classSection.getType(key)));
419        indexWriter.writeInt(classSection.getAccessFlags(key));
420        indexWriter.writeInt(typeSection.getNullableItemIndex(classSection.getSuperclass(key)));
421        indexWriter.writeInt(typeListSection.getNullableItemOffset(classSection.getSortedInterfaces(key)));
422        indexWriter.writeInt(stringSection.getNullableItemIndex(classSection.getSourceFile(key)));
423        indexWriter.writeInt(classSection.getAnnotationDirectoryOffset(key));
424
425        Collection<? extends FieldKey> staticFields = classSection.getSortedStaticFields(key);
426        Collection<? extends FieldKey> instanceFields = classSection.getSortedInstanceFields(key);
427        Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(key);
428        Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(key);
429        boolean classHasData = staticFields.size() > 0 ||
430                instanceFields.size() > 0 ||
431                directMethods.size() > 0 ||
432                virtualMethods.size() > 0;
433
434        if (classHasData) {
435            indexWriter.writeInt(offsetWriter.getPosition());
436        } else {
437            indexWriter.writeInt(0);
438        }
439
440        indexWriter.writeInt(classSection.getEncodedArrayOffset(key));
441
442        // now write the class_data_item
443        if (classHasData) {
444            numClassDataItems++;
445
446            offsetWriter.writeUleb128(staticFields.size());
447            offsetWriter.writeUleb128(instanceFields.size());
448            offsetWriter.writeUleb128(directMethods.size());
449            offsetWriter.writeUleb128(virtualMethods.size());
450
451            writeEncodedFields(offsetWriter, staticFields);
452            writeEncodedFields(offsetWriter, instanceFields);
453            writeEncodedMethods(offsetWriter, directMethods);
454            writeEncodedMethods(offsetWriter, virtualMethods);
455        }
456
457        return nextIndex;
458    }
459
460    private void writeEncodedFields(@Nonnull DexDataWriter writer, @Nonnull Collection<? extends FieldKey> fields)
461            throws IOException {
462        int prevIndex = 0;
463        for (FieldKey key: fields) {
464            int index = fieldSection.getItemIndex(key);
465            writer.writeUleb128(index-prevIndex);
466            writer.writeUleb128(classSection.getFieldAccessFlags(key));
467            prevIndex = index;
468        }
469    }
470
471    private void writeEncodedMethods(@Nonnull DexDataWriter writer, @Nonnull Collection<? extends MethodKey> methods)
472            throws IOException {
473        int prevIndex = 0;
474        for (MethodKey key: methods) {
475            int index = methodSection.getItemIndex(key);
476            writer.writeUleb128(index-prevIndex);
477            writer.writeUleb128(classSection.getMethodAccessFlags(key));
478            writer.writeUleb128(classSection.getCodeItemOffset(key));
479            prevIndex = index;
480        }
481    }
482
483    private void writeTypeLists(@Nonnull DexDataWriter writer) throws IOException {
484        writer.align();
485        typeListSectionOffset = writer.getPosition();
486        for (Map.Entry<? extends TypeListKey, Integer> entry: typeListSection.getItems()) {
487            writer.align();
488            entry.setValue(writer.getPosition());
489
490            Collection<? extends TypeKey> types = typeListSection.getTypes(entry.getKey());
491            writer.writeInt(types.size());
492            for (TypeKey typeKey: types) {
493                writer.writeUshort(typeSection.getItemIndex(typeKey));
494            }
495        }
496    }
497
498    private static class EncodedArrayKey<EncodedValue> {
499        @Nonnull Collection<? extends EncodedValue> elements;
500
501        public EncodedArrayKey() {
502        }
503
504        @Override public int hashCode() {
505            return CollectionUtils.listHashCode(elements);
506        }
507
508        @Override public boolean equals(Object o) {
509            if (o instanceof EncodedArrayKey) {
510                EncodedArrayKey other = (EncodedArrayKey)o;
511                if (elements.size() != other.elements.size()) {
512                    return false;
513                }
514                return Iterables.elementsEqual(elements, other.elements);
515            }
516            return false;
517        }
518    }
519
520    private void writeEncodedArrays(@Nonnull DexDataWriter writer) throws IOException {
521        InternalEncodedValueWriter encodedValueWriter = new InternalEncodedValueWriter(writer);
522        encodedArraySectionOffset = writer.getPosition();
523
524        HashMap<EncodedArrayKey<EncodedValue>, Integer> internedItems = Maps.newHashMap();
525        EncodedArrayKey<EncodedValue> key = new EncodedArrayKey<EncodedValue>();
526
527        for (ClassKey classKey: classSection.getSortedClasses()) {
528            Collection <? extends EncodedValue> elements = classSection.getStaticInitializers(classKey);
529            if (elements != null && elements.size() > 0) {
530                key.elements = elements;
531                Integer prev = internedItems.get(key);
532                if (prev != null) {
533                    classSection.setEncodedArrayOffset(classKey, prev);
534                } else {
535                    int offset = writer.getPosition();
536                    internedItems.put(key, offset);
537                    classSection.setEncodedArrayOffset(classKey, offset);
538                    key = new EncodedArrayKey<EncodedValue>();
539
540                    numEncodedArrayItems++;
541
542                    writer.writeUleb128(elements.size());
543                    for (EncodedValue value: elements) {
544                        writeEncodedValue(encodedValueWriter, value);
545                    }
546                }
547            }
548        }
549    }
550
551    private void writeAnnotations(@Nonnull DexDataWriter writer) throws IOException {
552        InternalEncodedValueWriter encodedValueWriter = new InternalEncodedValueWriter(writer);
553
554        annotationSectionOffset = writer.getPosition();
555        for (Map.Entry<? extends AnnotationKey, Integer> entry: annotationSection.getItems()) {
556            entry.setValue(writer.getPosition());
557
558            AnnotationKey key = entry.getKey();
559
560            writer.writeUbyte(annotationSection.getVisibility(key));
561            writer.writeUleb128(typeSection.getItemIndex(annotationSection.getType(key)));
562
563            Collection<? extends AnnotationElement> elements = annotationSection.getElements(key);
564            writer.writeUleb128(elements.size());
565
566            for (AnnotationElement element: elements) {
567                writer.writeUleb128(stringSection.getItemIndex(annotationSection.getElementName(element)));
568                writeEncodedValue(encodedValueWriter, annotationSection.getElementValue(element));
569            }
570        }
571    }
572
573
574    private void writeAnnotationSets(@Nonnull DexDataWriter writer) throws IOException {
575        writer.align();
576        annotationSetSectionOffset = writer.getPosition();
577        for (Map.Entry<? extends AnnotationSetKey, Integer> entry: annotationSetSection.getItems()) {
578            Collection<? extends AnnotationKey> annotations = Ordering.from(BaseAnnotation.BY_TYPE)
579                    .immutableSortedCopy(annotationSetSection.getAnnotations(entry.getKey()));
580
581            writer.align();
582            entry.setValue(writer.getPosition());
583            writer.writeInt(annotations.size());
584            for (AnnotationKey annotationKey: annotations) {
585                writer.writeInt(annotationSection.getItemOffset(annotationKey));
586            }
587        }
588    }
589
590    private void writeAnnotationSetRefs(@Nonnull DexDataWriter writer) throws IOException {
591        writer.align();
592        annotationSetRefSectionOffset = writer.getPosition();
593        for (Map.Entry<? extends AnnotationSetRefKey, Integer> entry: annotationSetRefSection.getItems()) {
594            writer.align();
595            entry.setValue(writer.getPosition());
596
597            Collection<AnnotationSetKey> annotationSets = annotationSetRefSection.getAnnotationSets(entry.getKey());
598
599            writer.writeInt(annotationSets.size());
600            for (AnnotationSetKey annotationSetKey: annotationSets) {
601                writer.writeInt(annotationSetSection.getItemOffset(annotationSetKey));
602            }
603        }
604    }
605
606    private void writeAnnotationDirectories(@Nonnull DexDataWriter writer) throws IOException {
607        writer.align();
608        annotationDirectorySectionOffset = writer.getPosition();
609        HashMap<AnnotationSetKey, Integer> internedItems = Maps.newHashMap();
610
611        ByteBuffer tempBuffer = ByteBuffer.allocate(65536);
612        tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
613
614        for (ClassKey key: classSection.getSortedClasses()) {
615            // first, we write the field/method/parameter items to a temporary buffer, so that we can get a count
616            // of each type, and determine if we even need to write an annotation directory for this class
617
618            Collection<? extends FieldKey> staticFields = classSection.getSortedStaticFields(key);
619            Collection<? extends FieldKey> instanceFields = classSection.getSortedInstanceFields(key);
620            Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(key);
621            Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(key);
622
623            // this is how much space we'll need if every field and method has annotations.
624            int maxSize = (staticFields.size() + instanceFields.size()) * 8 +
625                    (directMethods.size() + virtualMethods.size()) * 16;
626            if (maxSize > tempBuffer.capacity()) {
627                tempBuffer = ByteBuffer.allocate(maxSize);
628                tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
629            }
630
631            tempBuffer.clear();
632
633            int fieldAnnotations = 0;
634            int methodAnnotations = 0;
635            int parameterAnnotations = 0;
636
637            // the following logic for merging fields/methods is rather verbose and repetitive, but it's intentionally
638            // so, for efficiency (rather than adding yet another layer of indirection, to simplify it)
639            {
640                Iterator<? extends FieldKey> staticIterator = staticFields.iterator();
641                Iterator<? extends FieldKey> instanceIterator = instanceFields.iterator();
642
643                FieldKey nextStaticField;
644                int nextStaticFieldIndex = 0;
645                AnnotationSetKey nextStaticFieldAnnotationSetKey = null;
646                FieldKey nextInstanceField;
647                int nextInstanceFieldIndex = 0;
648                AnnotationSetKey nextInstanceFieldAnnotationSetKey = null;
649
650                try {
651                    do {
652                        nextStaticField = staticIterator.next();
653                        nextStaticFieldIndex = fieldSection.getItemIndex(nextStaticField);
654                        nextStaticFieldAnnotationSetKey = classSection.getFieldAnnotations(nextStaticField);
655                    } while (nextStaticFieldAnnotationSetKey == null);
656                } catch (NoSuchElementException ex) {
657                    nextStaticField = null;
658                }
659                try {
660                    do {
661                        nextInstanceField = instanceIterator.next();
662                        nextInstanceFieldIndex = fieldSection.getItemIndex(nextInstanceField);
663                        nextInstanceFieldAnnotationSetKey =
664                                classSection.getFieldAnnotations(nextInstanceField);
665                    } while (nextInstanceFieldAnnotationSetKey == null);
666                } catch (NoSuchElementException ex) {
667                    nextInstanceField = null;
668                }
669
670                while (true) {
671                    int nextFieldIndex;
672                    AnnotationSetKey nextFieldAnnotationSetKey;
673
674                    if (nextStaticField == null) {
675                        if (nextInstanceField == null) {
676                            break;
677                        }
678
679                        nextFieldIndex = nextInstanceFieldIndex;
680                        nextFieldAnnotationSetKey = nextInstanceFieldAnnotationSetKey;
681                        try {
682                            do {
683                                nextInstanceField = instanceIterator.next();
684                                nextInstanceFieldIndex = fieldSection.getItemIndex(nextInstanceField);
685                                nextInstanceFieldAnnotationSetKey =
686                                        classSection.getFieldAnnotations(nextInstanceField);
687                            } while (nextInstanceFieldAnnotationSetKey == null);
688                        } catch (NoSuchElementException ex) {
689                            nextInstanceField = null;
690                        }
691                    } else {
692                        if (nextInstanceField == null || nextStaticFieldIndex < nextInstanceFieldIndex) {
693                            nextFieldIndex = nextStaticFieldIndex;
694                            nextFieldAnnotationSetKey = nextStaticFieldAnnotationSetKey;
695                            try {
696                                do {
697                                    nextStaticField = staticIterator.next();
698                                    nextStaticFieldIndex = fieldSection.getItemIndex(nextStaticField);
699                                    nextStaticFieldAnnotationSetKey =
700                                            classSection.getFieldAnnotations(nextStaticField);
701                                } while (nextStaticFieldAnnotationSetKey == null);
702                            } catch (NoSuchElementException ex) {
703                                nextStaticField = null;
704                            }
705                        } else {
706                            nextFieldIndex = nextInstanceFieldIndex;
707                            nextFieldAnnotationSetKey = nextInstanceFieldAnnotationSetKey;
708                            try {
709                                do {
710                                    nextInstanceField = instanceIterator.next();
711                                    nextInstanceFieldIndex = fieldSection.getItemIndex(nextInstanceField);
712                                    nextInstanceFieldAnnotationSetKey =
713                                            classSection.getFieldAnnotations(nextInstanceField);
714                                } while (nextInstanceFieldAnnotationSetKey == null);
715                            } catch (NoSuchElementException ex) {
716                                nextInstanceField = null;
717                            }
718                        }
719                    }
720
721                    fieldAnnotations++;
722                    tempBuffer.putInt(nextFieldIndex);
723                    tempBuffer.putInt(annotationSetSection.getItemOffset(nextFieldAnnotationSetKey));
724                }
725            }
726
727            {
728                // weeee! now do it all again, except for methods this time
729                Iterator<? extends MethodKey> directIterator = classSection.getSortedDirectMethods(key).iterator();
730                Iterator<? extends MethodKey> virtualIterator = classSection.getSortedVirtualMethods(key).iterator();
731
732                MethodKey nextDirectMethod;
733                int nextDirectMethodIndex = 0;
734                AnnotationSetKey nextDirectMethodAnnotationKey = null;
735                MethodKey nextVirtualMethod;
736                int nextVirtualMethodIndex = 0;
737                AnnotationSetKey nextVirtualMethodAnnotationKey = null;
738
739                try {
740                    do {
741                        nextDirectMethod = directIterator.next();
742                        nextDirectMethodIndex = methodSection.getItemIndex(nextDirectMethod);
743                        nextDirectMethodAnnotationKey = classSection.getMethodAnnotations(nextDirectMethod);
744                    } while (nextDirectMethodAnnotationKey == null);
745                } catch (NoSuchElementException ex) {
746                    nextDirectMethod = null;
747                }
748                try {
749                    do {
750                        nextVirtualMethod = virtualIterator.next();
751                        nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod);
752                        nextVirtualMethodAnnotationKey =
753                                classSection.getMethodAnnotations(nextVirtualMethod);
754                    } while (nextVirtualMethodAnnotationKey == null);
755                } catch (NoSuchElementException ex) {
756                    nextVirtualMethod = null;
757                }
758
759                while (true) {
760                    int nextMethodIndex;
761                    AnnotationSetKey nextAnnotationKey;
762
763                    if (nextDirectMethod == null) {
764                        if (nextVirtualMethod == null) {
765                            break;
766                        }
767
768                        nextMethodIndex = nextVirtualMethodIndex;
769                        nextAnnotationKey = nextVirtualMethodAnnotationKey;
770                        try {
771                            do {
772                                nextVirtualMethod = virtualIterator.next();
773                                nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod);
774                                nextVirtualMethodAnnotationKey =
775                                        classSection.getMethodAnnotations(nextVirtualMethod);
776                            } while (nextVirtualMethodAnnotationKey == null);
777                        } catch (NoSuchElementException ex) {
778                            nextVirtualMethod = null;
779                        }
780                    } else {
781                        if (nextVirtualMethod == null || nextDirectMethodIndex < nextVirtualMethodIndex) {
782                            nextMethodIndex = nextDirectMethodIndex;
783                            nextAnnotationKey = nextDirectMethodAnnotationKey;
784                            try {
785                                do {
786                                    nextDirectMethod = directIterator.next();
787                                    nextDirectMethodIndex = methodSection.getItemIndex(nextDirectMethod);
788                                    nextDirectMethodAnnotationKey =
789                                            classSection.getMethodAnnotations(nextDirectMethod);
790                                } while (nextDirectMethodAnnotationKey == null);
791                            } catch (NoSuchElementException ex) {
792                                nextDirectMethod = null;
793                            }
794                        } else {
795                            nextMethodIndex = nextVirtualMethodIndex;
796                            nextAnnotationKey = nextVirtualMethodAnnotationKey;
797                            try {
798                                do {
799                                    nextVirtualMethod = virtualIterator.next();
800                                    nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod);
801                                    nextVirtualMethodAnnotationKey =
802                                            classSection.getMethodAnnotations(nextVirtualMethod);
803                                } while (nextVirtualMethodAnnotationKey == null);
804                            } catch (NoSuchElementException ex) {
805                                nextVirtualMethod = null;
806                            }
807                        }
808                    }
809
810                    methodAnnotations++;
811                    tempBuffer.putInt(nextMethodIndex);
812                    tempBuffer.putInt(annotationSetSection.getItemOffset(nextAnnotationKey));
813                }
814            }
815
816            {
817                // AAAAAAAAAAAnd one final time, for parameter annotations
818                Iterator<? extends MethodKey> directIterator = classSection.getSortedDirectMethods(key).iterator();
819                Iterator<? extends MethodKey> virtualIterator = classSection.getSortedVirtualMethods(key).iterator();
820
821                MethodKey nextDirectMethod;
822                int nextDirectMethodIndex = 0;
823                AnnotationSetRefKey nextDirectMethodAnnotationKey = null;
824                MethodKey nextVirtualMethod;
825                int nextVirtualMethodIndex = 0;
826                AnnotationSetRefKey nextVirtualMethodAnnotationKey = null;
827
828                try {
829                    do {
830                        nextDirectMethod = directIterator.next();
831                        nextDirectMethodIndex = methodSection.getItemIndex(nextDirectMethod);
832                        nextDirectMethodAnnotationKey =
833                                classSection.getParameterAnnotations(nextDirectMethod);
834                    } while (nextDirectMethodAnnotationKey == null);
835                } catch (NoSuchElementException ex) {
836                    nextDirectMethod = null;
837                }
838                try {
839                    do {
840                        nextVirtualMethod = virtualIterator.next();
841                        nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod);
842                        nextVirtualMethodAnnotationKey =
843                                classSection.getParameterAnnotations(nextVirtualMethod);
844                    } while (nextVirtualMethodAnnotationKey == null);
845                } catch (NoSuchElementException ex) {
846                    nextVirtualMethod = null;
847                }
848
849                while (true) {
850                    int nextMethodIndex;
851                    AnnotationSetRefKey nextAnnotationKey;
852
853                    if (nextDirectMethod == null) {
854                        if (nextVirtualMethod == null) {
855                            break;
856                        }
857
858                        nextMethodIndex = nextVirtualMethodIndex;
859                        nextAnnotationKey = nextVirtualMethodAnnotationKey;
860                        try {
861                            do {
862                                nextVirtualMethod = virtualIterator.next();
863                                nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod);
864                                nextVirtualMethodAnnotationKey =
865                                        classSection.getParameterAnnotations(nextVirtualMethod);
866                            } while (nextVirtualMethodAnnotationKey == null);
867                        } catch (NoSuchElementException ex) {
868                            nextVirtualMethod = null;
869                        }
870                    } else {
871                        if (nextVirtualMethod == null || nextDirectMethodIndex < nextVirtualMethodIndex) {
872                            nextMethodIndex = nextDirectMethodIndex;
873                            nextAnnotationKey = nextDirectMethodAnnotationKey;
874                            try {
875                                do {
876                                    nextDirectMethod = directIterator.next();
877                                    nextDirectMethodIndex = methodSection.getItemIndex(nextDirectMethod);
878                                    nextDirectMethodAnnotationKey =
879                                            classSection.getParameterAnnotations(nextDirectMethod);
880                                } while (nextDirectMethodAnnotationKey == null);
881                            } catch (NoSuchElementException ex) {
882                                nextDirectMethod = null;
883                            }
884                        } else {
885                            nextMethodIndex = nextVirtualMethodIndex;
886                            nextAnnotationKey = nextVirtualMethodAnnotationKey;
887                            try {
888                                do {
889                                    nextVirtualMethod = virtualIterator.next();
890                                    nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod);
891                                    nextVirtualMethodAnnotationKey =
892                                            classSection.getParameterAnnotations(nextVirtualMethod);
893                                } while (nextVirtualMethodAnnotationKey == null);
894                            } catch (NoSuchElementException ex) {
895                                nextVirtualMethod = null;
896                            }
897                        }
898                    }
899
900                    parameterAnnotations++;
901                    tempBuffer.putInt(nextMethodIndex);
902                    tempBuffer.putInt(annotationSetRefSection.getItemOffset(nextAnnotationKey));
903                }
904            }
905
906            // now, we finally know how many field/method/parameter annotations were written to the temp buffer
907
908            AnnotationSetKey classAnnotationKey = classSection.getClassAnnotations(key);
909            if (fieldAnnotations == 0 && methodAnnotations == 0 && parameterAnnotations == 0) {
910                if (classAnnotationKey != null) {
911                    // This is an internable directory. Let's see if we've already written one like it
912                    Integer directoryOffset = internedItems.get(classAnnotationKey);
913                    if (directoryOffset != null) {
914                        classSection.setAnnotationDirectoryOffset(key, directoryOffset);
915                        continue;
916                    } else {
917                        internedItems.put(classAnnotationKey, writer.getPosition());
918                    }
919                } else {
920                    continue;
921                }
922            }
923
924            // yep, we need to write it out
925            numAnnotationDirectoryPoolItems++;
926            classSection.setAnnotationDirectoryOffset(key, writer.getPosition());
927
928            writer.writeInt(annotationSetSection.getNullableItemOffset(classAnnotationKey));
929            writer.writeInt(fieldAnnotations);
930            writer.writeInt(methodAnnotations);
931            writer.writeInt(parameterAnnotations);
932            writer.write(tempBuffer.array(), 0, tempBuffer.position());
933        }
934    }
935
936    private void writeDebugItems(@Nonnull DexDataWriter writer) throws IOException {
937        debugSectionOffset = writer.getPosition();
938        DebugWriter<StringKey, TypeKey> debugWriter =
939                new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, writer);
940
941        for (ClassKey classKey: classSection.getSortedClasses()) {
942            Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
943            Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
944
945            Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
946
947            for (MethodKey methodKey: methods) {
948                Iterable<? extends DebugItem> debugItems = classSection.getDebugItems(methodKey);
949                Iterable<? extends StringKey> parameterNames = classSection.getParameterNames(methodKey);
950
951                int parameterCount = 0;
952                if (parameterNames != null) {
953                    int index = 0;
954                    for (StringKey parameterName: parameterNames) {
955                        index++;
956                        if (parameterName != null) {
957                            parameterCount = index;
958                        }
959                    }
960                }
961
962                if (debugItems == null && parameterCount == 0) {
963                    continue;
964                }
965
966                numDebugInfoPoolItems++;
967
968                classSection.setDebugItemOffset(methodKey, writer.getPosition());
969                int startingLineNumber = 0;
970
971                if (debugItems != null) {
972                    for (org.jf.dexlib2.iface.debug.DebugItem debugItem: debugItems) {
973                        if (debugItem instanceof LineNumber) {
974                            startingLineNumber = ((LineNumber)debugItem).getLineNumber();
975                            break;
976                        }
977                    }
978                }
979                writer.writeUleb128(startingLineNumber);
980
981                writer.writeUleb128(parameterCount);
982                if (parameterNames != null) {
983                    int index = 0;
984                    for (StringKey parameterName: parameterNames) {
985                        if (index == parameterCount) {
986                            break;
987                        }
988                        index++;
989                        writer.writeUleb128(stringSection.getNullableItemIndex(parameterName) + 1);
990                    }
991                }
992
993                if (debugItems != null) {
994                    debugWriter.reset(startingLineNumber);
995
996                    for (DebugItem debugItem: debugItems) {
997                        classSection.writeDebugItem(debugWriter, debugItem);
998                    }
999                }
1000                // write an END_SEQUENCE opcode, to end the debug item
1001                writer.write(0);
1002            }
1003        }
1004    }
1005
1006    private void writeCodeItems(@Nonnull DexDataWriter writer) throws IOException {
1007        ByteArrayOutputStream ehBuf = new ByteArrayOutputStream();
1008
1009        writer.align();
1010        codeSectionOffset = writer.getPosition();
1011        for (ClassKey classKey: classSection.getSortedClasses()) {
1012            Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
1013            Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
1014
1015            Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
1016
1017            for (MethodKey methodKey: methods) {
1018                Iterable<? extends Insn> instructions = classSection.getInstructions(methodKey);
1019                int debugItemOffset = classSection.getDebugItemOffset(methodKey);
1020
1021                if (instructions == null && debugItemOffset == NO_OFFSET) {
1022                    continue;
1023                }
1024
1025                numCodeItemPoolItems++;
1026
1027                writer.align();
1028                classSection.setCodeItemOffset(methodKey, writer.getPosition());
1029
1030                writer.writeUshort(classSection.getRegisterCount(methodKey));
1031
1032                boolean isStatic = AccessFlags.STATIC.isSet(classSection.getMethodAccessFlags(methodKey));
1033                Collection<? extends TypeKey> parameters = typeListSection.getTypes(
1034                        protoSection.getParameters(methodSection.getPrototype(methodKey)));
1035
1036                List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = classSection.getTryBlocks(methodKey);
1037                writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic));
1038
1039                if (instructions != null) {
1040                    tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks);
1041
1042                    InstructionWriteUtil<Insn, StringRef> instrWriteUtil =
1043                            new InstructionWriteUtil<Insn, StringRef>(instructions, stringSection, instructionFactory);
1044                    writer.writeUshort(instrWriteUtil.getOutParamCount());
1045                    writer.writeUshort(tryBlocks.size());
1046                    writer.writeInt(debugItemOffset);
1047
1048                    InstructionWriter instructionWriter =
1049                            InstructionWriter.makeInstructionWriter(writer, stringSection, typeSection, fieldSection,
1050                                    methodSection);
1051
1052                    writer.writeInt(instrWriteUtil.getCodeUnitCount());
1053                    for (Insn instruction: instrWriteUtil.getInstructions()) {
1054                        switch (instruction.getOpcode().format) {
1055                            case Format10t:
1056                                instructionWriter.write((Instruction10t)instruction);
1057                                break;
1058                            case Format10x:
1059                                instructionWriter.write((Instruction10x)instruction);
1060                                break;
1061                            case Format11n:
1062                                instructionWriter.write((Instruction11n)instruction);
1063                                break;
1064                            case Format11x:
1065                                instructionWriter.write((Instruction11x)instruction);
1066                                break;
1067                            case Format12x:
1068                                instructionWriter.write((Instruction12x)instruction);
1069                                break;
1070                            case Format20bc:
1071                                instructionWriter.write((Instruction20bc)instruction);
1072                                break;
1073                            case Format20t:
1074                                instructionWriter.write((Instruction20t)instruction);
1075                                break;
1076                            case Format21c:
1077                                instructionWriter.write((Instruction21c)instruction);
1078                                break;
1079                            case Format21ih:
1080                                instructionWriter.write((Instruction21ih)instruction);
1081                                break;
1082                            case Format21lh:
1083                                instructionWriter.write((Instruction21lh)instruction);
1084                                break;
1085                            case Format21s:
1086                                instructionWriter.write((Instruction21s)instruction);
1087                                break;
1088                            case Format21t:
1089                                instructionWriter.write((Instruction21t)instruction);
1090                                break;
1091                            case Format22b:
1092                                instructionWriter.write((Instruction22b)instruction);
1093                                break;
1094                            case Format22c:
1095                                instructionWriter.write((Instruction22c)instruction);
1096                                break;
1097                            case Format22s:
1098                                instructionWriter.write((Instruction22s)instruction);
1099                                break;
1100                            case Format22t:
1101                                instructionWriter.write((Instruction22t)instruction);
1102                                break;
1103                            case Format22x:
1104                                instructionWriter.write((Instruction22x)instruction);
1105                                break;
1106                            case Format23x:
1107                                instructionWriter.write((Instruction23x)instruction);
1108                                break;
1109                            case Format30t:
1110                                instructionWriter.write((Instruction30t)instruction);
1111                                break;
1112                            case Format31c:
1113                                instructionWriter.write((Instruction31c)instruction);
1114                                break;
1115                            case Format31i:
1116                                instructionWriter.write((Instruction31i)instruction);
1117                                break;
1118                            case Format31t:
1119                                instructionWriter.write((Instruction31t)instruction);
1120                                break;
1121                            case Format32x:
1122                                instructionWriter.write((Instruction32x)instruction);
1123                                break;
1124                            case Format35c:
1125                                instructionWriter.write((Instruction35c)instruction);
1126                                break;
1127                            case Format3rc:
1128                                instructionWriter.write((Instruction3rc)instruction);
1129                                break;
1130                            case Format51l:
1131                                instructionWriter.write((Instruction51l)instruction);
1132                                break;
1133                            case ArrayPayload:
1134                                instructionWriter.write((ArrayPayload)instruction);
1135                                break;
1136                            case PackedSwitchPayload:
1137                                instructionWriter.write((PackedSwitchPayload)instruction);
1138                                break;
1139                            case SparseSwitchPayload:
1140                                instructionWriter.write((SparseSwitchPayload)instruction);
1141                                break;
1142                            default:
1143                                throw new ExceptionWithContext("Unsupported instruction format: %s",
1144                                        instruction.getOpcode().format);
1145                        }
1146                    }
1147
1148                    if (tryBlocks.size() > 0) {
1149                        writer.align();
1150
1151
1152
1153                        // filter out unique lists of exception handlers
1154                        Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = Maps.newHashMap();
1155                        for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
1156                            exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0);
1157                        }
1158                        DexDataWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size());
1159
1160                        for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
1161                            int startAddress = tryBlock.getStartCodeAddress();
1162                            int endAddress = startAddress + tryBlock.getCodeUnitCount();
1163
1164                            startAddress += instrWriteUtil.codeOffsetShift(startAddress);
1165                            endAddress += instrWriteUtil.codeOffsetShift(endAddress);
1166                            int tbCodeUnitCount = endAddress - startAddress;
1167
1168                            writer.writeInt(startAddress);
1169                            writer.writeUshort(tbCodeUnitCount);
1170
1171                            if (tryBlock.getExceptionHandlers().size() == 0) {
1172                                throw new ExceptionWithContext("No exception handlers for the try block!");
1173                            }
1174
1175                            Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers());
1176                            if (offset != 0) {
1177                                // exception handler has already been written out, just use it
1178                                writer.writeUshort(offset);
1179                            } else {
1180                                // if offset has not been set yet, we are about to write out a new exception handler
1181                                offset = ehBuf.size();
1182                                writer.writeUshort(offset);
1183                                exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset);
1184
1185                                // check if the last exception handler is a catch-all and adjust the size accordingly
1186                                int ehSize = tryBlock.getExceptionHandlers().size();
1187                                ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1);
1188                                if (ehLast.getExceptionType() == null) {
1189                                    ehSize = ehSize * (-1) + 1;
1190                                }
1191
1192                                // now let's layout the exception handlers, assuming that catch-all is always last
1193                                DexDataWriter.writeSleb128(ehBuf, ehSize);
1194                                for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) {
1195                                    TypeKey exceptionTypeKey = classSection.getExceptionType(eh);
1196
1197                                    int codeAddress = eh.getHandlerCodeAddress();
1198                                    codeAddress += instrWriteUtil.codeOffsetShift(codeAddress);
1199
1200                                    if (exceptionTypeKey != null) {
1201                                        //regular exception handling
1202                                        DexDataWriter.writeUleb128(ehBuf, typeSection.getItemIndex(exceptionTypeKey));
1203                                        DexDataWriter.writeUleb128(ehBuf, codeAddress);
1204                                    } else {
1205                                        //catch-all
1206                                        DexDataWriter.writeUleb128(ehBuf, codeAddress);
1207                                    }
1208                                }
1209                            }
1210                        }
1211
1212                        if (ehBuf.size() > 0) {
1213                            ehBuf.writeTo(writer);
1214                            ehBuf.reset();
1215                        }
1216                    }
1217                } else {
1218                    // no instructions, all we have is the debug item offset
1219                    writer.writeUshort(0);
1220                    writer.writeUshort(0);
1221                    writer.writeInt(debugItemOffset);
1222                    writer.writeInt(0);
1223                }
1224            }
1225        }
1226    }
1227
1228    private int calcNumItems() {
1229        int numItems = 0;
1230
1231        // header item
1232        numItems++;
1233
1234        if (stringSection.getItems().size() > 0) {
1235            numItems += 2; // index and data
1236        }
1237        if (typeSection.getItems().size()  > 0) {
1238            numItems++;
1239        }
1240        if (protoSection.getItems().size() > 0) {
1241            numItems++;
1242        }
1243        if (fieldSection.getItems().size() > 0) {
1244            numItems++;
1245        }
1246        if (methodSection.getItems().size() > 0) {
1247            numItems++;
1248        }
1249        if (typeListSection.getItems().size() > 0) {
1250            numItems++;
1251        }
1252        if (numEncodedArrayItems > 0) {
1253            numItems++;
1254        }
1255        if (annotationSection.getItems().size() > 0) {
1256            numItems++;
1257        }
1258        if (annotationSetSection.getItems().size() > 0) {
1259            numItems++;
1260        }
1261        if (annotationSetRefSection.getItems().size() > 0) {
1262            numItems++;
1263        }
1264        if (numAnnotationDirectoryPoolItems > 0) {
1265            numItems++;
1266        }
1267        if (numDebugInfoPoolItems > 0) {
1268            numItems++;
1269        }
1270        if (numCodeItemPoolItems > 0) {
1271            numItems++;
1272        }
1273        if (classSection.getItems().size() > 0) {
1274            numItems++;
1275        }
1276        if (numClassDataItems > 0) {
1277            numItems++;
1278        }
1279        // map item itself
1280        numItems++;
1281
1282        return numItems;
1283    }
1284
1285    private void writeMapItem(@Nonnull DexDataWriter writer) throws IOException{
1286        writer.align();
1287        mapSectionOffset = writer.getPosition();
1288        int numItems = calcNumItems();
1289
1290        writer.writeInt(numItems);
1291
1292        // index section
1293        writeMapItem(writer, ItemType.HEADER_ITEM, 1, 0);
1294        writeMapItem(writer, ItemType.STRING_ID_ITEM, stringSection.getItems().size(), stringIndexSectionOffset);
1295        writeMapItem(writer, ItemType.TYPE_ID_ITEM, typeSection.getItems().size(), typeSectionOffset);
1296        writeMapItem(writer, ItemType.PROTO_ID_ITEM, protoSection.getItems().size(), protoSectionOffset);
1297        writeMapItem(writer, ItemType.FIELD_ID_ITEM, fieldSection.getItems().size(), fieldSectionOffset);
1298        writeMapItem(writer, ItemType.METHOD_ID_ITEM, methodSection.getItems().size(), methodSectionOffset);
1299        writeMapItem(writer, ItemType.CLASS_DEF_ITEM, classSection.getItems().size(), classIndexSectionOffset);
1300
1301        // data section
1302        writeMapItem(writer, ItemType.STRING_DATA_ITEM, stringSection.getItems().size(), stringDataSectionOffset);
1303        writeMapItem(writer, ItemType.TYPE_LIST, typeListSection.getItems().size(), typeListSectionOffset);
1304        writeMapItem(writer, ItemType.ENCODED_ARRAY_ITEM, numEncodedArrayItems, encodedArraySectionOffset);
1305        writeMapItem(writer, ItemType.ANNOTATION_ITEM, annotationSection.getItems().size(), annotationSectionOffset);
1306        writeMapItem(writer, ItemType.ANNOTATION_SET_ITEM, annotationSetSection.getItems().size(),
1307                annotationSetSectionOffset);
1308        writeMapItem(writer, ItemType.ANNOTATION_SET_REF_LIST, annotationSetRefSection.getItems().size(),
1309                annotationSetRefSectionOffset);
1310        writeMapItem(writer, ItemType.ANNOTATION_DIRECTORY_ITEM, numAnnotationDirectoryPoolItems,
1311                annotationDirectorySectionOffset);
1312        writeMapItem(writer, ItemType.DEBUG_INFO_ITEM, numDebugInfoPoolItems, debugSectionOffset);
1313        writeMapItem(writer, ItemType.CODE_ITEM, numCodeItemPoolItems, codeSectionOffset);
1314        writeMapItem(writer, ItemType.CLASS_DATA_ITEM, numClassDataItems, classDataSectionOffset);
1315        writeMapItem(writer, ItemType.MAP_LIST, 1, mapSectionOffset);
1316    }
1317
1318    private void writeMapItem(@Nonnull DexDataWriter writer, int type, int size, int offset) throws IOException {
1319        if (size > 0) {
1320            writer.writeUshort(type);
1321            writer.writeUshort(0);
1322            writer.writeInt(size);
1323            writer.writeInt(offset);
1324        }
1325    }
1326
1327    private void writeHeader(@Nonnull DexDataWriter writer, int dataOffset, int fileSize) throws IOException {
1328        // TODO: need to determine which magic value to write
1329        writer.write(HeaderItem.MAGIC_VALUES[0]);
1330
1331        // checksum placeholder
1332        writer.writeInt(0);
1333
1334        // signature placeholder
1335        writer.write(new byte[20]);
1336
1337        writer.writeInt(fileSize);
1338        writer.writeInt(HeaderItem.ITEM_SIZE);
1339        writer.writeInt(HeaderItem.LITTLE_ENDIAN_TAG);
1340
1341        // link
1342        writer.writeInt(0);
1343        writer.writeInt(0);
1344
1345        // map
1346        writer.writeInt(mapSectionOffset);
1347
1348        // index sections
1349
1350        writeSectionInfo(writer, stringSection.getItems().size(), stringIndexSectionOffset);
1351        writeSectionInfo(writer, typeSection.getItems().size(), typeSectionOffset);
1352        writeSectionInfo(writer, protoSection.getItems().size(), protoSectionOffset);
1353        writeSectionInfo(writer, fieldSection.getItems().size(), fieldSectionOffset);
1354        writeSectionInfo(writer, methodSection.getItems().size(), methodSectionOffset);
1355        writeSectionInfo(writer, classSection.getItems().size(), classIndexSectionOffset);
1356
1357        // data section
1358        writer.writeInt(fileSize - dataOffset);
1359        writer.writeInt(dataOffset);
1360    }
1361
1362    private void writeSectionInfo(DexDataWriter writer, int numItems, int offset) throws IOException {
1363        writer.writeInt(numItems);
1364        if (numItems > 0) {
1365            writer.writeInt(offset);
1366        } else {
1367            writer.writeInt(0);
1368        }
1369    }
1370}
1371