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