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