DexWriter.java revision df8e4802115fadd6bb67ba405aba2db885abbd55
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.*;
47import org.jf.dexlib2.util.MethodUtil;
48import org.jf.dexlib2.writer.util.InstructionWriteUtil;
49import org.jf.dexlib2.writer.util.TryListBuilder;
50import org.jf.util.CollectionUtils;
51import org.jf.util.ExceptionWithContext;
52import org.jf.util.RandomAccessFileOutputStream;
53
54import javax.annotation.Nonnull;
55import javax.annotation.Nullable;
56import java.io.ByteArrayOutputStream;
57import java.io.IOException;
58import java.io.RandomAccessFile;
59import java.nio.ByteBuffer;
60import java.nio.ByteOrder;
61import java.nio.channels.FileChannel;
62import java.security.MessageDigest;
63import java.security.NoSuchAlgorithmException;
64import java.util.*;
65import java.util.Map.Entry;
66import java.util.zip.Adler32;
67
68public abstract class DexWriter<
69        StringKey extends CharSequence, StringRef extends StringReference, TypeKey extends CharSequence,
70        TypeRef extends TypeReference, ProtoKey extends Comparable<ProtoKey>,
71        FieldRefKey extends FieldReference, MethodRefKey extends MethodReference,
72        BaseReference extends Reference,
73        ClassKey extends Comparable<? super ClassKey>,
74        AnnotationKey extends Annotation, AnnotationSetKey,
75        TypeListKey,
76        FieldKey, MethodKey,
77        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 final int api;
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 numAnnotationSetRefItems = 0;
106    protected int numAnnotationDirectoryItems = 0;
107    protected int numDebugInfoItems = 0;
108    protected int numCodeItemItems = 0;
109    protected int numClassDataItems = 0;
110
111    protected final InstructionFactory<? extends Insn, BaseReference> instructionFactory;
112
113    protected final StringSection<StringKey, StringRef> stringSection;
114    protected final TypeSection<StringKey, TypeKey, TypeRef> typeSection;
115    protected final ProtoSection<StringKey, TypeKey, ProtoKey, TypeListKey> protoSection;
116    protected final FieldSection<StringKey, TypeKey, FieldRefKey, FieldKey> fieldSection;
117    protected final MethodSection<StringKey, TypeKey, ProtoKey, MethodRefKey, MethodKey> methodSection;
118    protected final ClassSection<StringKey, TypeKey, TypeListKey, ClassKey, FieldKey, MethodKey, AnnotationSetKey,
119            EncodedValue, DebugItem, Insn, ExceptionHandler> classSection;
120
121    protected final TypeListSection<TypeKey, TypeListKey> typeListSection;
122    protected final AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement, EncodedValue> annotationSection;
123    protected final AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection;
124
125    protected DexWriter(int api,
126                        InstructionFactory<? extends Insn, BaseReference> instructionFactory,
127                        StringSection<StringKey, StringRef> stringSection,
128                        TypeSection<StringKey, TypeKey, TypeRef> typeSection,
129                        ProtoSection<StringKey, TypeKey, ProtoKey, TypeListKey> protoSection,
130                        FieldSection<StringKey, TypeKey, FieldRefKey, FieldKey> fieldSection,
131                        MethodSection<StringKey, TypeKey, ProtoKey, MethodRefKey, MethodKey> methodSection,
132                        ClassSection<StringKey, TypeKey, TypeListKey, ClassKey, FieldKey, MethodKey, AnnotationSetKey,
133                                EncodedValue, DebugItem, Insn, ExceptionHandler> classSection,
134                        TypeListSection<TypeKey, TypeListKey> typeListSection,
135                        AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement,
136                                EncodedValue> annotationSection,
137                        AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection) {
138        this.api = api;
139        this.instructionFactory = instructionFactory;
140        this.stringSection = stringSection;
141        this.typeSection = typeSection;
142        this.protoSection = protoSection;
143        this.fieldSection = fieldSection;
144        this.methodSection = methodSection;
145        this.classSection = classSection;
146        this.typeListSection = typeListSection;
147        this.annotationSection = annotationSection;
148        this.annotationSetSection = annotationSetSection;
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 recurse 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.getFieldIndex(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.getMethodIndex(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        HashMap<List<? extends AnnotationSetKey>, Integer> internedItems = Maps.newHashMap();
594
595        for (ClassKey classKey: classSection.getSortedClasses()) {
596            for (MethodKey methodKey: classSection.getSortedMethods(classKey)) {
597                List<? extends AnnotationSetKey> parameterAnnotations = classSection.getParameterAnnotations(methodKey);
598                if (parameterAnnotations != null) {
599                    Integer prev = internedItems.get(parameterAnnotations);
600                    if (prev != null) {
601                        classSection.setAnnotationSetRefListOffset(methodKey, prev);
602                    } else {
603                        writer.align();
604                        int position = writer.getPosition();
605                        classSection.setAnnotationSetRefListOffset(methodKey, position);
606                        internedItems.put(parameterAnnotations, position);
607
608                        numAnnotationSetRefItems++;
609
610                        writer.writeInt(parameterAnnotations.size());
611                        for (AnnotationSetKey annotationSetKey: parameterAnnotations) {
612                            writer.writeInt(annotationSetSection.getItemOffset(annotationSetKey));
613                        }
614                    }
615                }
616            }
617        }
618    }
619
620    private void writeAnnotationDirectories(@Nonnull DexDataWriter writer) throws IOException {
621        writer.align();
622        annotationDirectorySectionOffset = writer.getPosition();
623        HashMap<AnnotationSetKey, Integer> internedItems = Maps.newHashMap();
624
625        ByteBuffer tempBuffer = ByteBuffer.allocate(65536);
626        tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
627
628        for (ClassKey key: classSection.getSortedClasses()) {
629            // first, we write the field/method/parameter items to a temporary buffer, so that we can get a count
630            // of each type, and determine if we even need to write an annotation directory for this class
631
632            Collection<? extends FieldKey> fields = classSection.getSortedFields(key);
633            Collection<? extends MethodKey> methods = classSection.getSortedMethods(key);
634
635            // this is how much space we'll need if every field and method has annotations.
636            int maxSize = fields.size() * 8 + methods.size() * 16;
637            if (maxSize > tempBuffer.capacity()) {
638                tempBuffer = ByteBuffer.allocate(maxSize);
639                tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
640            }
641
642            tempBuffer.clear();
643
644            int fieldAnnotations = 0;
645            int methodAnnotations = 0;
646            int parameterAnnotations = 0;
647
648            for (FieldKey field: fields) {
649                AnnotationSetKey fieldAnnotationsKey = classSection.getFieldAnnotations(field);
650                if (fieldAnnotationsKey != null) {
651                    fieldAnnotations++;
652                    tempBuffer.putInt(fieldSection.getFieldIndex(field));
653                    tempBuffer.putInt(annotationSetSection.getItemOffset(fieldAnnotationsKey));
654                }
655            }
656
657            for (MethodKey method: methods) {
658                AnnotationSetKey methodAnnotationsKey = classSection.getMethodAnnotations(method);
659                if (methodAnnotationsKey != null) {
660                    methodAnnotations++;
661                    tempBuffer.putInt(methodSection.getMethodIndex(method));
662                    tempBuffer.putInt(annotationSetSection.getItemOffset(methodAnnotationsKey));
663                }
664            }
665
666            for (MethodKey method: methods) {
667                int offset = classSection.getAnnotationSetRefListOffset(method);
668                if (offset != DexWriter.NO_OFFSET) {
669                    methodAnnotations++;
670                    tempBuffer.putInt(methodSection.getMethodIndex(method));
671                    tempBuffer.putInt(offset);
672                }
673            }
674
675            // now, we finally know how many field/method/parameter annotations were written to the temp buffer
676
677            AnnotationSetKey classAnnotationKey = classSection.getClassAnnotations(key);
678            if (fieldAnnotations == 0 && methodAnnotations == 0 && parameterAnnotations == 0) {
679                if (classAnnotationKey != null) {
680                    // This is an internable directory. Let's see if we've already written one like it
681                    Integer directoryOffset = internedItems.get(classAnnotationKey);
682                    if (directoryOffset != null) {
683                        classSection.setAnnotationDirectoryOffset(key, directoryOffset);
684                        continue;
685                    } else {
686                        internedItems.put(classAnnotationKey, writer.getPosition());
687                    }
688                } else {
689                    continue;
690                }
691            }
692
693            // yep, we need to write it out
694            numAnnotationDirectoryItems++;
695            classSection.setAnnotationDirectoryOffset(key, writer.getPosition());
696
697            writer.writeInt(annotationSetSection.getNullableItemOffset(classAnnotationKey));
698            writer.writeInt(fieldAnnotations);
699            writer.writeInt(methodAnnotations);
700            writer.writeInt(parameterAnnotations);
701            writer.write(tempBuffer.array(), 0, tempBuffer.position());
702        }
703    }
704
705    private void writeDebugItems(@Nonnull DexDataWriter writer) throws IOException {
706        debugSectionOffset = writer.getPosition();
707        DebugWriter<StringKey, TypeKey> debugWriter =
708                new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, writer);
709
710        for (ClassKey classKey: classSection.getSortedClasses()) {
711            Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
712            Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
713
714            Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
715
716            for (MethodKey methodKey: methods) {
717                Iterable<? extends DebugItem> debugItems = classSection.getDebugItems(methodKey);
718                Iterable<? extends StringKey> parameterNames = classSection.getParameterNames(methodKey);
719
720                int parameterCount = 0;
721                if (parameterNames != null) {
722                    int index = 0;
723                    for (StringKey parameterName: parameterNames) {
724                        index++;
725                        if (parameterName != null) {
726                            parameterCount = index;
727                        }
728                    }
729                }
730
731                if (debugItems == null && parameterCount == 0) {
732                    continue;
733                }
734
735                numDebugInfoItems++;
736
737                classSection.setDebugItemOffset(methodKey, writer.getPosition());
738                int startingLineNumber = 0;
739
740                if (debugItems != null) {
741                    for (org.jf.dexlib2.iface.debug.DebugItem debugItem: debugItems) {
742                        if (debugItem instanceof LineNumber) {
743                            startingLineNumber = ((LineNumber)debugItem).getLineNumber();
744                            break;
745                        }
746                    }
747                }
748                writer.writeUleb128(startingLineNumber);
749
750                writer.writeUleb128(parameterCount);
751                if (parameterNames != null) {
752                    int index = 0;
753                    for (StringKey parameterName: parameterNames) {
754                        if (index == parameterCount) {
755                            break;
756                        }
757                        index++;
758                        writer.writeUleb128(stringSection.getNullableItemIndex(parameterName) + 1);
759                    }
760                }
761
762                if (debugItems != null) {
763                    debugWriter.reset(startingLineNumber);
764
765                    for (DebugItem debugItem: debugItems) {
766                        classSection.writeDebugItem(debugWriter, debugItem);
767                    }
768                }
769                // write an END_SEQUENCE opcode, to end the debug item
770                writer.write(0);
771            }
772        }
773    }
774
775    private void writeCodeItems(@Nonnull DexDataWriter writer) throws IOException {
776        ByteArrayOutputStream ehBuf = new ByteArrayOutputStream();
777
778        writer.align();
779        codeSectionOffset = writer.getPosition();
780        for (ClassKey classKey: classSection.getSortedClasses()) {
781            Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
782            Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
783
784            Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
785
786            for (MethodKey methodKey: methods) {
787                Iterable<? extends Insn> instructions = classSection.getInstructions(methodKey);
788                int debugItemOffset = classSection.getDebugItemOffset(methodKey);
789
790                if (instructions == null && debugItemOffset == NO_OFFSET) {
791                    continue;
792                }
793
794                numCodeItemItems++;
795
796                writer.align();
797                classSection.setCodeItemOffset(methodKey, writer.getPosition());
798
799                writer.writeUshort(classSection.getRegisterCount(methodKey));
800
801                boolean isStatic = AccessFlags.STATIC.isSet(classSection.getMethodAccessFlags(methodKey));
802                Collection<? extends TypeKey> parameters = typeListSection.getTypes(
803                        protoSection.getParameters(methodSection.getPrototype(methodKey)));
804
805                List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = classSection.getTryBlocks(methodKey);
806                writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic));
807
808                if (instructions != null) {
809                    tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks);
810
811                    InstructionWriteUtil<Insn, StringRef, BaseReference> instrWriteUtil =
812                            new InstructionWriteUtil<Insn, StringRef, BaseReference>(instructions, stringSection, instructionFactory);
813                    writer.writeUshort(instrWriteUtil.getOutParamCount());
814                    writer.writeUshort(tryBlocks.size());
815                    writer.writeInt(debugItemOffset);
816
817                    InstructionWriter instructionWriter =
818                            InstructionWriter.makeInstructionWriter(writer, stringSection, typeSection, fieldSection,
819                                    methodSection);
820
821                    writer.writeInt(instrWriteUtil.getCodeUnitCount());
822                    for (Insn instruction: instrWriteUtil.getInstructions()) {
823                        switch (instruction.getOpcode().format) {
824                            case Format10t:
825                                instructionWriter.write((Instruction10t)instruction);
826                                break;
827                            case Format10x:
828                                instructionWriter.write((Instruction10x)instruction);
829                                break;
830                            case Format11n:
831                                instructionWriter.write((Instruction11n)instruction);
832                                break;
833                            case Format11x:
834                                instructionWriter.write((Instruction11x)instruction);
835                                break;
836                            case Format12x:
837                                instructionWriter.write((Instruction12x)instruction);
838                                break;
839                            case Format20bc:
840                                instructionWriter.write((Instruction20bc)instruction);
841                                break;
842                            case Format20t:
843                                instructionWriter.write((Instruction20t)instruction);
844                                break;
845                            case Format21c:
846                                instructionWriter.write((Instruction21c)instruction);
847                                break;
848                            case Format21ih:
849                                instructionWriter.write((Instruction21ih)instruction);
850                                break;
851                            case Format21lh:
852                                instructionWriter.write((Instruction21lh)instruction);
853                                break;
854                            case Format21s:
855                                instructionWriter.write((Instruction21s)instruction);
856                                break;
857                            case Format21t:
858                                instructionWriter.write((Instruction21t)instruction);
859                                break;
860                            case Format22b:
861                                instructionWriter.write((Instruction22b)instruction);
862                                break;
863                            case Format22c:
864                                instructionWriter.write((Instruction22c)instruction);
865                                break;
866                            case Format22s:
867                                instructionWriter.write((Instruction22s)instruction);
868                                break;
869                            case Format22t:
870                                instructionWriter.write((Instruction22t)instruction);
871                                break;
872                            case Format22x:
873                                instructionWriter.write((Instruction22x)instruction);
874                                break;
875                            case Format23x:
876                                instructionWriter.write((Instruction23x)instruction);
877                                break;
878                            case Format30t:
879                                instructionWriter.write((Instruction30t)instruction);
880                                break;
881                            case Format31c:
882                                instructionWriter.write((Instruction31c)instruction);
883                                break;
884                            case Format31i:
885                                instructionWriter.write((Instruction31i)instruction);
886                                break;
887                            case Format31t:
888                                instructionWriter.write((Instruction31t)instruction);
889                                break;
890                            case Format32x:
891                                instructionWriter.write((Instruction32x)instruction);
892                                break;
893                            case Format35c:
894                                instructionWriter.write((Instruction35c)instruction);
895                                break;
896                            case Format3rc:
897                                instructionWriter.write((Instruction3rc)instruction);
898                                break;
899                            case Format51l:
900                                instructionWriter.write((Instruction51l)instruction);
901                                break;
902                            case ArrayPayload:
903                                instructionWriter.write((ArrayPayload)instruction);
904                                break;
905                            case PackedSwitchPayload:
906                                instructionWriter.write((PackedSwitchPayload)instruction);
907                                break;
908                            case SparseSwitchPayload:
909                                instructionWriter.write((SparseSwitchPayload)instruction);
910                                break;
911                            default:
912                                throw new ExceptionWithContext("Unsupported instruction format: %s",
913                                        instruction.getOpcode().format);
914                        }
915                    }
916
917                    if (tryBlocks.size() > 0) {
918                        writer.align();
919
920
921
922                        // filter out unique lists of exception handlers
923                        Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = Maps.newHashMap();
924                        for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
925                            exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0);
926                        }
927                        DexDataWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size());
928
929                        for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
930                            int startAddress = tryBlock.getStartCodeAddress();
931                            int endAddress = startAddress + tryBlock.getCodeUnitCount();
932
933                            startAddress += instrWriteUtil.codeOffsetShift(startAddress);
934                            endAddress += instrWriteUtil.codeOffsetShift(endAddress);
935                            int tbCodeUnitCount = endAddress - startAddress;
936
937                            writer.writeInt(startAddress);
938                            writer.writeUshort(tbCodeUnitCount);
939
940                            if (tryBlock.getExceptionHandlers().size() == 0) {
941                                throw new ExceptionWithContext("No exception handlers for the try block!");
942                            }
943
944                            Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers());
945                            if (offset != 0) {
946                                // exception handler has already been written out, just use it
947                                writer.writeUshort(offset);
948                            } else {
949                                // if offset has not been set yet, we are about to write out a new exception handler
950                                offset = ehBuf.size();
951                                writer.writeUshort(offset);
952                                exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset);
953
954                                // check if the last exception handler is a catch-all and adjust the size accordingly
955                                int ehSize = tryBlock.getExceptionHandlers().size();
956                                ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1);
957                                if (ehLast.getExceptionType() == null) {
958                                    ehSize = ehSize * (-1) + 1;
959                                }
960
961                                // now let's layout the exception handlers, assuming that catch-all is always last
962                                DexDataWriter.writeSleb128(ehBuf, ehSize);
963                                for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) {
964                                    TypeKey exceptionTypeKey = classSection.getExceptionType(eh);
965
966                                    int codeAddress = eh.getHandlerCodeAddress();
967                                    codeAddress += instrWriteUtil.codeOffsetShift(codeAddress);
968
969                                    if (exceptionTypeKey != null) {
970                                        //regular exception handling
971                                        DexDataWriter.writeUleb128(ehBuf, typeSection.getItemIndex(exceptionTypeKey));
972                                        DexDataWriter.writeUleb128(ehBuf, codeAddress);
973                                    } else {
974                                        //catch-all
975                                        DexDataWriter.writeUleb128(ehBuf, codeAddress);
976                                    }
977                                }
978                            }
979                        }
980
981                        if (ehBuf.size() > 0) {
982                            ehBuf.writeTo(writer);
983                            ehBuf.reset();
984                        }
985                    }
986                } else {
987                    // no instructions, all we have is the debug item offset
988                    writer.writeUshort(0);
989                    writer.writeUshort(0);
990                    writer.writeInt(debugItemOffset);
991                    writer.writeInt(0);
992                }
993            }
994        }
995    }
996
997    private int calcNumItems() {
998        int numItems = 0;
999
1000        // header item
1001        numItems++;
1002
1003        if (stringSection.getItems().size() > 0) {
1004            numItems += 2; // index and data
1005        }
1006        if (typeSection.getItems().size()  > 0) {
1007            numItems++;
1008        }
1009        if (protoSection.getItems().size() > 0) {
1010            numItems++;
1011        }
1012        if (fieldSection.getItems().size() > 0) {
1013            numItems++;
1014        }
1015        if (methodSection.getItems().size() > 0) {
1016            numItems++;
1017        }
1018        if (typeListSection.getItems().size() > 0) {
1019            numItems++;
1020        }
1021        if (numEncodedArrayItems > 0) {
1022            numItems++;
1023        }
1024        if (annotationSection.getItems().size() > 0) {
1025            numItems++;
1026        }
1027        if (annotationSetSection.getItems().size() > 0) {
1028            numItems++;
1029        }
1030        if (numAnnotationSetRefItems > 0) {
1031            numItems++;
1032        }
1033        if (numAnnotationDirectoryItems > 0) {
1034            numItems++;
1035        }
1036        if (numDebugInfoItems > 0) {
1037            numItems++;
1038        }
1039        if (numCodeItemItems > 0) {
1040            numItems++;
1041        }
1042        if (classSection.getItems().size() > 0) {
1043            numItems++;
1044        }
1045        if (numClassDataItems > 0) {
1046            numItems++;
1047        }
1048        // map item itself
1049        numItems++;
1050
1051        return numItems;
1052    }
1053
1054    private void writeMapItem(@Nonnull DexDataWriter writer) throws IOException{
1055        writer.align();
1056        mapSectionOffset = writer.getPosition();
1057        int numItems = calcNumItems();
1058
1059        writer.writeInt(numItems);
1060
1061        // index section
1062        writeMapItem(writer, ItemType.HEADER_ITEM, 1, 0);
1063        writeMapItem(writer, ItemType.STRING_ID_ITEM, stringSection.getItems().size(), stringIndexSectionOffset);
1064        writeMapItem(writer, ItemType.TYPE_ID_ITEM, typeSection.getItems().size(), typeSectionOffset);
1065        writeMapItem(writer, ItemType.PROTO_ID_ITEM, protoSection.getItems().size(), protoSectionOffset);
1066        writeMapItem(writer, ItemType.FIELD_ID_ITEM, fieldSection.getItems().size(), fieldSectionOffset);
1067        writeMapItem(writer, ItemType.METHOD_ID_ITEM, methodSection.getItems().size(), methodSectionOffset);
1068        writeMapItem(writer, ItemType.CLASS_DEF_ITEM, classSection.getItems().size(), classIndexSectionOffset);
1069
1070        // data section
1071        writeMapItem(writer, ItemType.STRING_DATA_ITEM, stringSection.getItems().size(), stringDataSectionOffset);
1072        writeMapItem(writer, ItemType.TYPE_LIST, typeListSection.getItems().size(), typeListSectionOffset);
1073        writeMapItem(writer, ItemType.ENCODED_ARRAY_ITEM, numEncodedArrayItems, encodedArraySectionOffset);
1074        writeMapItem(writer, ItemType.ANNOTATION_ITEM, annotationSection.getItems().size(), annotationSectionOffset);
1075        writeMapItem(writer, ItemType.ANNOTATION_SET_ITEM, annotationSetSection.getItems().size(),
1076                annotationSetSectionOffset);
1077        writeMapItem(writer, ItemType.ANNOTATION_SET_REF_LIST, numAnnotationSetRefItems, annotationSetRefSectionOffset);
1078        writeMapItem(writer, ItemType.ANNOTATION_DIRECTORY_ITEM, numAnnotationDirectoryItems,
1079                annotationDirectorySectionOffset);
1080        writeMapItem(writer, ItemType.DEBUG_INFO_ITEM, numDebugInfoItems, debugSectionOffset);
1081        writeMapItem(writer, ItemType.CODE_ITEM, numCodeItemItems, codeSectionOffset);
1082        writeMapItem(writer, ItemType.CLASS_DATA_ITEM, numClassDataItems, classDataSectionOffset);
1083        writeMapItem(writer, ItemType.MAP_LIST, 1, mapSectionOffset);
1084    }
1085
1086    private void writeMapItem(@Nonnull DexDataWriter writer, int type, int size, int offset) throws IOException {
1087        if (size > 0) {
1088            writer.writeUshort(type);
1089            writer.writeUshort(0);
1090            writer.writeInt(size);
1091            writer.writeInt(offset);
1092        }
1093    }
1094
1095    private void writeHeader(@Nonnull DexDataWriter writer, int dataOffset, int fileSize) throws IOException {
1096        if (api < 14) {
1097            writer.write(HeaderItem.MAGIC_VALUES[0]);
1098        } else {
1099            writer.write(HeaderItem.MAGIC_VALUES[1]);
1100        }
1101
1102        // checksum placeholder
1103        writer.writeInt(0);
1104
1105        // signature placeholder
1106        writer.write(new byte[20]);
1107
1108        writer.writeInt(fileSize);
1109        writer.writeInt(HeaderItem.ITEM_SIZE);
1110        writer.writeInt(HeaderItem.LITTLE_ENDIAN_TAG);
1111
1112        // link
1113        writer.writeInt(0);
1114        writer.writeInt(0);
1115
1116        // map
1117        writer.writeInt(mapSectionOffset);
1118
1119        // index sections
1120
1121        writeSectionInfo(writer, stringSection.getItems().size(), stringIndexSectionOffset);
1122        writeSectionInfo(writer, typeSection.getItems().size(), typeSectionOffset);
1123        writeSectionInfo(writer, protoSection.getItems().size(), protoSectionOffset);
1124        writeSectionInfo(writer, fieldSection.getItems().size(), fieldSectionOffset);
1125        writeSectionInfo(writer, methodSection.getItems().size(), methodSectionOffset);
1126        writeSectionInfo(writer, classSection.getItems().size(), classIndexSectionOffset);
1127
1128        // data section
1129        writer.writeInt(fileSize - dataOffset);
1130        writer.writeInt(dataOffset);
1131    }
1132
1133    private void writeSectionInfo(DexDataWriter writer, int numItems, int offset) throws IOException {
1134        writer.writeInt(numItems);
1135        if (numItems > 0) {
1136            writer.writeInt(offset);
1137        } else {
1138            writer.writeInt(0);
1139        }
1140    }
1141}
1142