• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013, Google LLC
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google LLC nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 package com.android.tools.smali.dexlib2.writer;
32 
33 import com.android.tools.smali.dexlib2.AccessFlags;
34 import com.android.tools.smali.dexlib2.HiddenApiRestriction;
35 import com.android.tools.smali.dexlib2.MethodHandleType;
36 import com.android.tools.smali.dexlib2.Opcode;
37 import com.android.tools.smali.dexlib2.Opcodes;
38 import com.android.tools.smali.dexlib2.ReferenceType;
39 import com.android.tools.smali.dexlib2.dexbacked.raw.CallSiteIdItem;
40 import com.android.tools.smali.dexlib2.dexbacked.raw.ClassDefItem;
41 import com.android.tools.smali.dexlib2.dexbacked.raw.FieldIdItem;
42 import com.android.tools.smali.dexlib2.dexbacked.raw.HeaderItem;
43 import com.android.tools.smali.dexlib2.dexbacked.raw.HiddenApiClassDataItem;
44 import com.android.tools.smali.dexlib2.dexbacked.raw.ItemType;
45 import com.android.tools.smali.dexlib2.dexbacked.raw.MethodHandleItem;
46 import com.android.tools.smali.dexlib2.dexbacked.raw.MethodIdItem;
47 import com.android.tools.smali.dexlib2.dexbacked.raw.ProtoIdItem;
48 import com.android.tools.smali.dexlib2.dexbacked.raw.StringIdItem;
49 import com.android.tools.smali.dexlib2.dexbacked.raw.TypeIdItem;
50 import com.android.tools.smali.dexlib2.iface.instruction.formats.ArrayPayload;
51 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction10t;
52 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction10x;
53 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11n;
54 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x;
55 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction12x;
56 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction20bc;
57 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction20t;
58 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c;
59 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21ih;
60 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21lh;
61 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21s;
62 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21t;
63 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22b;
64 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c;
65 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22cs;
66 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22s;
67 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22t;
68 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22x;
69 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction23x;
70 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction30t;
71 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31c;
72 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i;
73 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31t;
74 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction32x;
75 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c;
76 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35mi;
77 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35ms;
78 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc;
79 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rmi;
80 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rms;
81 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction45cc;
82 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction4rcc;
83 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction51l;
84 import com.android.tools.smali.dexlib2.iface.instruction.formats.PackedSwitchPayload;
85 import com.android.tools.smali.dexlib2.iface.instruction.formats.SparseSwitchPayload;
86 import com.android.tools.smali.dexlib2.iface.reference.CallSiteReference;
87 import com.android.tools.smali.dexlib2.iface.reference.FieldReference;
88 import com.android.tools.smali.dexlib2.iface.reference.MethodHandleReference;
89 import com.android.tools.smali.dexlib2.iface.reference.MethodProtoReference;
90 import com.android.tools.smali.dexlib2.iface.reference.MethodReference;
91 import com.android.tools.smali.dexlib2.iface.reference.StringReference;
92 import com.android.tools.smali.dexlib2.iface.reference.TypeReference;
93 import com.android.tools.smali.dexlib2.base.BaseAnnotation;
94 import com.android.tools.smali.dexlib2.base.BaseAnnotationElement;
95 import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation;
96 import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction31c;
97 import com.android.tools.smali.dexlib2.formatter.DexFormatter;
98 import com.android.tools.smali.dexlib2.iface.Annotation;
99 import com.android.tools.smali.dexlib2.iface.ExceptionHandler;
100 import com.android.tools.smali.dexlib2.iface.TryBlock;
101 import com.android.tools.smali.dexlib2.iface.debug.DebugItem;
102 import com.android.tools.smali.dexlib2.iface.debug.LineNumber;
103 import com.android.tools.smali.dexlib2.iface.instruction.Instruction;
104 import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction;
105 import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction;
106 import com.android.tools.smali.dexlib2.iface.instruction.VariableRegisterInstruction;
107 import com.android.tools.smali.dexlib2.util.InstructionUtil;
108 import com.android.tools.smali.dexlib2.util.MethodUtil;
109 import com.android.tools.smali.dexlib2.writer.io.DeferredOutputStream;
110 import com.android.tools.smali.dexlib2.writer.io.DeferredOutputStreamFactory;
111 import com.android.tools.smali.dexlib2.writer.io.DexDataStore;
112 import com.android.tools.smali.dexlib2.writer.io.MemoryDeferredOutputStream;
113 import com.android.tools.smali.dexlib2.writer.util.TryListBuilder;
114 import com.android.tools.smali.util.ChainedIterator;
115 import com.android.tools.smali.util.CollectionUtils;
116 import com.android.tools.smali.util.ExceptionWithContext;
117 import com.android.tools.smali.util.IteratorUtils;
118 
119 import java.util.ArrayList;
120 import java.util.Collection;
121 import java.util.Collections;
122 import java.util.Comparator;
123 import java.util.HashMap;
124 import java.util.List;
125 import java.util.Map;
126 import java.util.Set;
127 import javax.annotation.Nonnull;
128 import javax.annotation.Nullable;
129 import java.io.ByteArrayOutputStream;
130 import java.io.IOException;
131 import java.io.InputStream;
132 import java.io.OutputStream;
133 import java.nio.Buffer;
134 import java.nio.ByteBuffer;
135 import java.nio.ByteOrder;
136 import java.security.MessageDigest;
137 import java.security.NoSuchAlgorithmException;
138 import java.util.Map.Entry;
139 import java.util.zip.Adler32;
140 
141 public abstract class DexWriter<
142         StringKey extends CharSequence, StringRef extends StringReference, TypeKey extends CharSequence,
143         TypeRef extends TypeReference, ProtoRefKey extends MethodProtoReference,
144         FieldRefKey extends FieldReference, MethodRefKey extends MethodReference,
145         ClassKey extends Comparable<? super ClassKey>,
146         CallSiteKey extends CallSiteReference,
147         MethodHandleKey extends MethodHandleReference,
148         AnnotationKey extends Annotation, AnnotationSetKey,
149         TypeListKey,
150         FieldKey, MethodKey,
151         EncodedArrayKey,
152         EncodedValue,
153         AnnotationElement extends com.android.tools.smali.dexlib2.iface.AnnotationElement,
154         StringSectionType extends StringSection<StringKey, StringRef>,
155         TypeSectionType extends TypeSection<StringKey, TypeKey, TypeRef>,
156         ProtoSectionType extends ProtoSection<StringKey, TypeKey, ProtoRefKey, TypeListKey>,
157         FieldSectionType extends FieldSection<StringKey, TypeKey, FieldRefKey, FieldKey>,
158         MethodSectionType extends MethodSection<StringKey, TypeKey, ProtoRefKey, MethodRefKey, MethodKey>,
159         ClassSectionType extends ClassSection<StringKey, TypeKey, TypeListKey, ClassKey, FieldKey, MethodKey,
160                 AnnotationSetKey, EncodedArrayKey>,
161         CallSiteSectionType extends CallSiteSection<CallSiteKey, EncodedArrayKey>,
162         MethodHandleSectionType extends MethodHandleSection<MethodHandleKey, FieldRefKey, MethodRefKey>,
163         TypeListSectionType extends TypeListSection<TypeKey, TypeListKey>,
164         AnnotationSectionType extends AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement,
165                 EncodedValue>,
166         AnnotationSetSectionType extends AnnotationSetSection<AnnotationKey, AnnotationSetKey>,
167         EncodedArraySectionType extends EncodedArraySection<EncodedArrayKey, EncodedValue>> {
168     public static final int NO_INDEX = -1;
169     public static final int NO_OFFSET = 0;
170 
171     public static final int MAX_POOL_SIZE = (1 << 16);
172 
173     protected final Opcodes opcodes;
174 
175     protected int stringIndexSectionOffset = NO_OFFSET;
176     protected int typeSectionOffset = NO_OFFSET;
177     protected int protoSectionOffset = NO_OFFSET;
178     protected int fieldSectionOffset = NO_OFFSET;
179     protected int methodSectionOffset = NO_OFFSET;
180     protected int classIndexSectionOffset = NO_OFFSET;
181     protected int callSiteSectionOffset = NO_OFFSET;
182     protected int methodHandleSectionOffset = NO_OFFSET;
183 
184     protected int stringDataSectionOffset = NO_OFFSET;
185     protected int classDataSectionOffset = NO_OFFSET;
186     protected int typeListSectionOffset = NO_OFFSET;
187     protected int encodedArraySectionOffset = NO_OFFSET;
188     protected int annotationSectionOffset = NO_OFFSET;
189     protected int annotationSetSectionOffset = NO_OFFSET;
190     protected int annotationSetRefSectionOffset = NO_OFFSET;
191     protected int annotationDirectorySectionOffset = NO_OFFSET;
192     protected int debugSectionOffset = NO_OFFSET;
193     protected int codeSectionOffset = NO_OFFSET;
194     protected int hiddenApiRestrictionsOffset = NO_OFFSET;
195     protected int mapSectionOffset = NO_OFFSET;
196 
197     protected boolean hasHiddenApiRestrictions = false;
198 
199     protected int numAnnotationSetRefItems = 0;
200     protected int numAnnotationDirectoryItems = 0;
201     protected int numDebugInfoItems = 0;
202     protected int numCodeItemItems = 0;
203     protected int numClassDataItems = 0;
204 
205     // The sections defined here must be kept in sync with these section arrays:
206     // - DexWriter.overflowableSections
207     // - DexPool.sections
208 
209     public final StringSectionType stringSection;
210     public final TypeSectionType typeSection;
211     public final ProtoSectionType protoSection;
212     public final FieldSectionType fieldSection;
213     public final MethodSectionType methodSection;
214     public final ClassSectionType classSection;
215     public final CallSiteSectionType callSiteSection;
216     public final MethodHandleSectionType methodHandleSection;
217 
218     public final TypeListSectionType typeListSection;
219     public final AnnotationSectionType annotationSection;
220     public final AnnotationSetSectionType annotationSetSection;
221     public final EncodedArraySectionType encodedArraySection;
222 
223     private final IndexSection<?>[] overflowableSections;
224 
225     private final Map<DebugInfoCache, Integer> debugInfoCaches = new HashMap<>();
226 
DexWriter(Opcodes opcodes)227     protected DexWriter(Opcodes opcodes) {
228         this.opcodes = opcodes;
229 
230         SectionProvider sectionProvider = getSectionProvider();
231         this.stringSection = sectionProvider.getStringSection();
232         this.typeSection = sectionProvider.getTypeSection();
233         this.protoSection = sectionProvider.getProtoSection();
234         this.fieldSection = sectionProvider.getFieldSection();
235         this.methodSection = sectionProvider.getMethodSection();
236         this.classSection = sectionProvider.getClassSection();
237         this.callSiteSection = sectionProvider.getCallSiteSection();
238         this.methodHandleSection = sectionProvider.getMethodHandleSection();
239         this.typeListSection = sectionProvider.getTypeListSection();
240         this.annotationSection = sectionProvider.getAnnotationSection();
241         this.annotationSetSection = sectionProvider.getAnnotationSetSection();
242         this.encodedArraySection = sectionProvider.getEncodedArraySection();
243 
244         overflowableSections = new IndexSection<?>[] {
245                 //stringSection,            // supports jumbo indexes
246                 typeSection,
247                 protoSection,
248                 fieldSection,
249                 methodSection,
250                 //classSection,             // redundant check: cannot be larger than typeSection
251                 callSiteSection,
252                 methodHandleSection,
253         };
254     }
255 
getSectionProvider()256     @Nonnull protected abstract SectionProvider getSectionProvider();
257 
writeEncodedValue(@onnull InternalEncodedValueWriter writer, @Nonnull EncodedValue encodedValue)258     protected abstract void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer,
259                                               @Nonnull EncodedValue encodedValue) throws IOException;
260 
261     private Comparator<Map.Entry<? extends CallSiteKey, Integer>> callSiteComparator =
262             new Comparator<Entry<? extends CallSiteKey, Integer>>() {
263                 @Override
264                 public int compare(Entry<? extends CallSiteKey, Integer> o1, Entry<? extends CallSiteKey, Integer> o2) {
265                     int offset1 = encodedArraySection.getItemOffset(callSiteSection.getEncodedCallSite(o1.getKey()));
266                     int offset2 = encodedArraySection.getItemOffset(callSiteSection.getEncodedCallSite(o2.getKey()));
267                     return Integer.compare(offset1, offset2);
268                 }
269             };
270 
271     private static Comparator<Map.Entry> toStringKeyComparator =
272             new Comparator<Map.Entry>() {
273                 @Override public int compare(Entry o1, Entry o2) {
274                     return o1.getKey().toString().compareTo(o2.getKey().toString());
275                 }
276             };
277 
comparableKeyComparator()278     private static <T extends Comparable<? super T>> Comparator<Map.Entry<? extends T, ?>> comparableKeyComparator() {
279         return new Comparator<Entry<? extends T, ?>>() {
280             @Override public int compare(Entry<? extends T, ?> o1, Entry<? extends T, ?> o2) {
281                 return o1.getKey().compareTo(o2.getKey());
282             }
283         };
284     }
285 
286     private static <T extends Comparable<? super T>> Comparator<Entry<?, ? extends T>> comparableValueComparator() {
287         return new Comparator<Entry<?, ? extends T>>() {
288             @Override public int compare(Entry<?, ? extends T> o1, Entry<?, ? extends T> o2) {
289                 return o1.getValue().compareTo(o2.getValue());
290             }
291         };
292     }
293 
294     protected class InternalEncodedValueWriter extends EncodedValueWriter<StringKey, TypeKey, FieldRefKey, MethodRefKey,
295             AnnotationElement, ProtoRefKey, MethodHandleKey, EncodedValue> {
296         private InternalEncodedValueWriter(@Nonnull DexDataWriter writer) {
297             super(writer, stringSection, typeSection, fieldSection, methodSection, protoSection, methodHandleSection,
298                     annotationSection);
299         }
300 
301         @Override protected void writeEncodedValue(@Nonnull EncodedValue encodedValue) throws IOException {
302             DexWriter.this.writeEncodedValue(this, encodedValue);
303         }
304     }
305 
306     private int getDataSectionOffset() {
307         return HeaderItem.ITEM_SIZE +
308                 stringSection.getItemCount() * StringIdItem.ITEM_SIZE +
309                 typeSection.getItemCount() * TypeIdItem.ITEM_SIZE +
310                 protoSection.getItemCount() * ProtoIdItem.ITEM_SIZE +
311                 fieldSection.getItemCount() * FieldIdItem.ITEM_SIZE +
312                 methodSection.getItemCount() * MethodIdItem.ITEM_SIZE +
313                 classSection.getItemCount() * ClassDefItem.ITEM_SIZE +
314                 callSiteSection.getItemCount() * CallSiteIdItem.ITEM_SIZE +
315                 methodHandleSection.getItemCount() * MethodHandleItem.ITEM_SIZE;
316     }
317 
318     @Nonnull
319     public List<String> getMethodReferences() {
320         List<String> methodReferences = new ArrayList<>();
321         for (Entry<? extends MethodRefKey, Integer> methodReference: methodSection.getItems()) {
322             methodReferences.add(DexFormatter.INSTANCE.getMethodDescriptor(methodReference.getKey()));
323         }
324         return methodReferences;
325     }
326 
327     @Nonnull
328     public List<String> getFieldReferences() {
329         List<String> fieldReferences = new ArrayList<>();
330         for (Entry<? extends FieldRefKey, Integer> fieldReference: fieldSection.getItems()) {
331             fieldReferences.add(DexFormatter.INSTANCE.getFieldDescriptor(fieldReference.getKey()));
332         }
333         return fieldReferences;
334     }
335 
336     @Nonnull
337     public List<String> getTypeReferences() {
338         List<String> classReferences = new ArrayList<>();
339         for (Entry<? extends TypeKey, Integer> typeReference: typeSection.getItems()) {
340             classReferences.add(typeReference.getKey().toString());
341         }
342         return classReferences;
343     }
344 
345     /**
346      * Checks whether any of the size-sensitive constant pools have overflowed and have more than 64Ki entries.
347      *
348      * Note that even if this returns true, it may still be possible to successfully write the dex file, if the
349      * overflowed items are not referenced anywhere that uses a 16-bit index.
350      *
351      * @return true if any of the size-sensitive constant pools have overflowed
352      */
353     public boolean hasOverflowed() {
354         return hasOverflowed(MAX_POOL_SIZE);
355     }
356 
357     /**
358      * Checks whether any of the size-sensitive constant pools have more than the supplied maximum number of entries.
359      *
360      * @param maxPoolSize the maximum number of entries allowed in any of the size-sensitive constant pools
361      * @return true if any of the size-sensitive constant pools have overflowed the supplied size limit
362      */
363     public boolean hasOverflowed(int maxPoolSize) {
364         for (IndexSection section: overflowableSections) {
365             if (section.getItemCount() > maxPoolSize) return true;
366         }
367         return false;
368     }
369 
370     public void writeTo(@Nonnull DexDataStore dest) throws IOException {
371         this.writeTo(dest, MemoryDeferredOutputStream.getFactory());
372     }
373 
374     public void writeTo(@Nonnull DexDataStore dest,
375                         @Nonnull DeferredOutputStreamFactory tempFactory) throws IOException {
376         try {
377             int dataSectionOffset = getDataSectionOffset();
378 
379             try (DexDataWriter headerWriter = outputAt(dest, 0);
380                  DexDataWriter indexWriter = outputAt(dest, HeaderItem.ITEM_SIZE);
381                  DexDataWriter offsetWriter = outputAt(dest, dataSectionOffset)) {
382                 writeStrings(indexWriter, offsetWriter);
383                 writeTypes(indexWriter);
384                 writeTypeLists(offsetWriter);
385                 writeProtos(indexWriter);
386                 writeFields(indexWriter);
387                 writeMethods(indexWriter);
388 
389                 // encoded arrays depend on method handles..
390                 try (DexDataWriter methodHandleWriter = outputAt(dest, indexWriter.getPosition() +
391                         classSection.getItemCount() * ClassDefItem.ITEM_SIZE +
392                         callSiteSection.getItemCount() * CallSiteIdItem.ITEM_SIZE)) {
393                     writeMethodHandles(methodHandleWriter);
394                 }
395 
396                 // call sites depend on encoded arrays..
397                 writeEncodedArrays(offsetWriter);
398 
399                 // class defs depend on method handles and call sites..
400                 try (DexDataWriter callSiteWriter = outputAt(dest, indexWriter.getPosition() +
401                         classSection.getItemCount() * ClassDefItem.ITEM_SIZE)) {
402                     writeCallSites(callSiteWriter);
403                 }
404 
405                 writeAnnotations(offsetWriter);
406                 writeAnnotationSets(offsetWriter);
407                 writeAnnotationSetRefs(offsetWriter);
408                 writeAnnotationDirectories(offsetWriter);
409                 writeDebugAndCodeItems(offsetWriter, tempFactory.makeDeferredOutputStream());
410                 writeClasses(dest, indexWriter, offsetWriter);
411 
412                 writeMapItem(offsetWriter);
413                 writeHeader(headerWriter, dataSectionOffset, offsetWriter.getPosition());
414             }
415             updateSignature(dest);
416             updateChecksum(dest);
417         } finally {
418             dest.close();
419         }
420     }
421 
422     private void updateSignature(@Nonnull DexDataStore dataStore) throws IOException {
423         MessageDigest md;
424         try {
425             md = MessageDigest.getInstance("SHA-1");
426         } catch (NoSuchAlgorithmException ex) {
427             throw new RuntimeException(ex);
428         }
429 
430         byte[] buffer = new byte[4 * 1024];
431         InputStream input = dataStore.readAt(HeaderItem.SIGNATURE_DATA_START_OFFSET);
432         int bytesRead = input.read(buffer);
433         while (bytesRead >= 0) {
434             md.update(buffer, 0, bytesRead);
435             bytesRead = input.read(buffer);
436         }
437 
438         byte[] signature = md.digest();
439         if (signature.length != HeaderItem.SIGNATURE_SIZE) {
440             throw new RuntimeException("unexpected digest write: " + signature.length + " bytes");
441         }
442 
443         // write signature
444         OutputStream output = dataStore.outputAt(HeaderItem.SIGNATURE_OFFSET);
445         output.write(signature);
446         output.close();
447     }
448 
449     private void updateChecksum(@Nonnull DexDataStore dataStore) throws IOException {
450         Adler32 a32 = new Adler32();
451 
452         byte[] buffer = new byte[4 * 1024];
453         InputStream input = dataStore.readAt(HeaderItem.CHECKSUM_DATA_START_OFFSET);
454         int bytesRead = input.read(buffer);
455         while (bytesRead >= 0) {
456             a32.update(buffer, 0, bytesRead);
457             bytesRead = input.read(buffer);
458         }
459 
460         // write checksum, utilizing logic in DexWriter to write the integer value properly
461         OutputStream output = dataStore.outputAt(HeaderItem.CHECKSUM_OFFSET);
462         DexDataWriter.writeInt(output, (int)a32.getValue());
463         output.close();
464     }
465 
466     private static DexDataWriter outputAt(DexDataStore dataStore, int filePosition) throws IOException {
467         return new DexDataWriter(dataStore.outputAt(filePosition), filePosition);
468     }
469 
470     private void writeStrings(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter) throws IOException {
471         stringIndexSectionOffset = indexWriter.getPosition();
472         stringDataSectionOffset = offsetWriter.getPosition();
473         int index = 0;
474         List<Entry<? extends StringKey, Integer>> stringEntries = new ArrayList<>(stringSection.getItems());
475         Collections.sort(stringEntries, toStringKeyComparator);
476 
477         for (Map.Entry<? extends StringKey, Integer>  entry: stringEntries) {
478             entry.setValue(index++);
479             indexWriter.writeInt(offsetWriter.getPosition());
480             String stringValue = entry.getKey().toString();
481             offsetWriter.writeUleb128(stringValue.length());
482             offsetWriter.writeString(stringValue);
483             offsetWriter.write(0);
484         }
485     }
486 
487     private void writeTypes(@Nonnull DexDataWriter writer) throws IOException {
488         typeSectionOffset = writer.getPosition();
489         int index = 0;
490 
491         List<Map.Entry<? extends TypeKey, Integer>> typeEntries = new ArrayList<>(typeSection.getItems());
492         Collections.sort(typeEntries, toStringKeyComparator);
493 
494         for (Map.Entry<? extends TypeKey, Integer> entry : typeEntries) {
495             entry.setValue(index++);
496             writer.writeInt(stringSection.getItemIndex(typeSection.getString(entry.getKey())));
497         }
498     }
499 
500     private void writeProtos(@Nonnull DexDataWriter writer) throws IOException {
501         protoSectionOffset = writer.getPosition();
502         int index = 0;
503 
504         List<Map.Entry<? extends ProtoRefKey, Integer>> protoEntries = new ArrayList<>(protoSection.getItems());
505         Collections.sort(protoEntries, DexWriter.<ProtoRefKey>comparableKeyComparator());
506 
507         for (Map.Entry<? extends ProtoRefKey, Integer> entry: protoEntries) {
508             entry.setValue(index++);
509             ProtoRefKey key = entry.getKey();
510             writer.writeInt(stringSection.getItemIndex(protoSection.getShorty(key)));
511             writer.writeInt(typeSection.getItemIndex(protoSection.getReturnType(key)));
512             writer.writeInt(typeListSection.getNullableItemOffset(protoSection.getParameters(key)));
513         }
514     }
515 
516     private void writeFields(@Nonnull DexDataWriter writer) throws IOException {
517         fieldSectionOffset = writer.getPosition();
518         int index = 0;
519 
520         List<Map.Entry<? extends FieldRefKey, Integer>> fieldEntries = new ArrayList<>(fieldSection.getItems());
521         Collections.sort(fieldEntries, DexWriter.<FieldRefKey>comparableKeyComparator());
522 
523         for (Map.Entry<? extends FieldRefKey, Integer> entry: fieldEntries) {
524             entry.setValue(index++);
525             FieldRefKey key = entry.getKey();
526             writer.writeUshort(typeSection.getItemIndex(fieldSection.getDefiningClass(key)));
527             writer.writeUshort(typeSection.getItemIndex(fieldSection.getFieldType(key)));
528             writer.writeInt(stringSection.getItemIndex(fieldSection.getName(key)));
529         }
530     }
531 
532     private void writeMethods(@Nonnull DexDataWriter writer) throws IOException {
533         methodSectionOffset = writer.getPosition();
534         int index = 0;
535 
536         List<Map.Entry<? extends MethodRefKey, Integer>> methodEntries = new ArrayList<>(methodSection.getItems());
537         Collections.sort(methodEntries, DexWriter.<MethodRefKey>comparableKeyComparator());
538 
539         for (Map.Entry<? extends MethodRefKey, Integer> entry: methodEntries) {
540             entry.setValue(index++);
541             MethodRefKey key = entry.getKey();
542             writer.writeUshort(typeSection.getItemIndex(methodSection.getDefiningClass(key)));
543             writer.writeUshort(protoSection.getItemIndex(methodSection.getPrototype(key)));
544             writer.writeInt(stringSection.getItemIndex(methodSection.getName(key)));
545         }
546     }
547 
548     private void writeClasses(@Nonnull DexDataStore dataStore, @Nonnull DexDataWriter indexWriter,
549                               @Nonnull DexDataWriter offsetWriter) throws IOException {
550         classIndexSectionOffset = indexWriter.getPosition();
551         classDataSectionOffset = offsetWriter.getPosition();
552 
553         List<Map.Entry<? extends ClassKey, Integer>> classEntriesKeySorted = new ArrayList<>(classSection.getItems());
554         Collections.sort(classEntriesKeySorted, DexWriter.<ClassKey>comparableKeyComparator());
555 
556         int index = 0;
557         for (Map.Entry<? extends ClassKey, Integer> key: classEntriesKeySorted) {
558             index = writeClass(indexWriter, offsetWriter, index, key);
559         }
560 
561         if (!shouldWriteHiddenApiRestrictions()) {
562             return;
563         }
564 
565         offsetWriter.align();
566         hiddenApiRestrictionsOffset = offsetWriter.getPosition();
567 
568         List<Map.Entry<? extends ClassKey, Integer>> classEntriesValueSorted = new ArrayList<>(classSection.getItems());
569         classEntriesValueSorted.sort(DexWriter.comparableValueComparator());
570         RestrictionsWriter restrictionsWriter = new RestrictionsWriter(dataStore, offsetWriter, classEntriesValueSorted.size());
571 
572         try {
573             for (Map.Entry<? extends ClassKey, Integer> key : classEntriesValueSorted) {
574 
575                 for (FieldKey fieldKey : classSection.getSortedStaticFields(key.getKey())) {
576                     restrictionsWriter.writeRestriction(classSection.getFieldHiddenApiRestrictions(fieldKey));
577                 }
578 
579                 for (FieldKey fieldKey : classSection.getSortedInstanceFields(key.getKey())) {
580                     restrictionsWriter.writeRestriction(classSection.getFieldHiddenApiRestrictions(fieldKey));
581                 }
582 
583                 for (MethodKey methodKey : classSection.getSortedDirectMethods(key.getKey())) {
584                     restrictionsWriter.writeRestriction(classSection.getMethodHiddenApiRestrictions(methodKey));
585                 }
586 
587                 for (MethodKey methodKey : classSection.getSortedVirtualMethods(key.getKey())) {
588                     restrictionsWriter.writeRestriction(classSection.getMethodHiddenApiRestrictions(methodKey));
589                 }
590 
591                 restrictionsWriter.finishClass();
592             }
593         } finally {
594             restrictionsWriter.close();
595         }
596     }
597 
598     private boolean shouldWriteHiddenApiRestrictions() {
599         return hasHiddenApiRestrictions && opcodes.api >= 29;
600     }
601 
602     private static class RestrictionsWriter {
603         private final int startOffset;
604 
605         private final DexDataStore dataStore;
606         private final DexDataWriter offsetsWriter;
607         private final DexDataWriter restrictionsWriter;
608 
609         private boolean writeRestrictionsForClass = false;
610         private int pendingBlankEntries = 0;
611 
612         public RestrictionsWriter(DexDataStore dataStore, DexDataWriter offsetWriter, int numClasses)
613                 throws IOException {
614             this.startOffset = offsetWriter.getPosition();
615             this.dataStore = dataStore;
616             this.restrictionsWriter = offsetWriter;
617 
618             int offsetsSize = numClasses * HiddenApiClassDataItem.OFFSET_ITEM_SIZE;
619 
620             // We don't know the size yet, so skip over it
621             restrictionsWriter.writeInt(0);
622 
623             this.offsetsWriter = outputAt(dataStore, restrictionsWriter.getPosition());
624 
625             // Skip over the offsets
626             for (int i=0; i<offsetsSize; i++) {
627                 restrictionsWriter.write(0);
628             }
629             restrictionsWriter.flush();
630         }
631 
632         public void finishClass() throws IOException {
633             if (!writeRestrictionsForClass) {
634                 // Normally the offset gets written when the first non-blank restriction gets written. If only blank
635                 // restrictions were added, nothing actually gets written, and we write out a blank offset here.
636                 offsetsWriter.writeInt(0);
637             }
638 
639             writeRestrictionsForClass = false;
640             pendingBlankEntries = 0;
641         }
642 
643         private void addBlankEntry() throws IOException {
644             if (writeRestrictionsForClass) {
645                 restrictionsWriter.writeUleb128(HiddenApiRestriction.WHITELIST.getValue());
646             } else {
647                 pendingBlankEntries++;
648             }
649         }
650 
651         public void writeRestriction(@Nonnull Set<HiddenApiRestriction> hiddenApiRestrictions) throws IOException {
652             if (hiddenApiRestrictions.isEmpty()) {
653                 addBlankEntry();
654                 return;
655             }
656 
657             if (!writeRestrictionsForClass) {
658                 writeRestrictionsForClass = true;
659                 offsetsWriter.writeInt(restrictionsWriter.getPosition() - startOffset);
660 
661                 for (int i = 0; i < pendingBlankEntries; i++) {
662                     restrictionsWriter.writeUleb128(HiddenApiRestriction.WHITELIST.getValue());
663                 }
664                 pendingBlankEntries = 0;
665             }
666             restrictionsWriter.writeUleb128(HiddenApiRestriction.combineFlags(hiddenApiRestrictions));
667         }
668 
669         public void close() throws IOException {
670             offsetsWriter.close();
671             try (DexDataWriter writer = outputAt(dataStore, startOffset)) {
672                 writer.writeInt(restrictionsWriter.getPosition() - startOffset);
673             }
674         }
675     }
676 
677     /**
678      * Writes out the class_def_item and class_data_item for the given class.
679      *
680      * This will recursively write out any unwritten superclass/interface before writing the class itself, as per the
681      * dex specification.
682      *
683      * @return the index for the next class to be written
684      */
685     private int writeClass(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter,
686                            int nextIndex, @Nullable Map.Entry<? extends ClassKey, Integer> entry) throws IOException {
687         if (entry == null) {
688             // class does not exist in this dex file, cannot write it
689             return nextIndex;
690         }
691 
692         if (entry.getValue() != NO_INDEX) {
693             // class has already been written, no need to write it
694             return nextIndex;
695         }
696 
697         ClassKey key = entry.getKey();
698 
699         // set a bogus index, to make sure we don't recurse and double-write it
700         entry.setValue(0);
701 
702         // first, try to write the superclass
703         Map.Entry<? extends ClassKey, Integer> superEntry =
704                 classSection.getClassEntryByType(classSection.getSuperclass(key));
705         nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, superEntry);
706 
707         // then, try to write interfaces
708         for (TypeKey interfaceTypeKey: typeListSection.getTypes(classSection.getInterfaces(key))) {
709             Map.Entry<? extends ClassKey, Integer> interfaceEntry = classSection.getClassEntryByType(interfaceTypeKey);
710             nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, interfaceEntry);
711         }
712 
713         // now set the index for real
714         entry.setValue(nextIndex++);
715 
716         // and finally, write the class itself
717         // first, the class_def_item
718         indexWriter.writeInt(typeSection.getItemIndex(classSection.getType(key)));
719         indexWriter.writeInt(classSection.getAccessFlags(key));
720         indexWriter.writeInt(typeSection.getNullableItemIndex(classSection.getSuperclass(key)));
721         indexWriter.writeInt(typeListSection.getNullableItemOffset(classSection.getInterfaces(key)));
722         indexWriter.writeInt(stringSection.getNullableItemIndex(classSection.getSourceFile(key)));
723         indexWriter.writeInt(classSection.getAnnotationDirectoryOffset(key));
724 
725         Collection<? extends FieldKey> staticFields = classSection.getSortedStaticFields(key);
726         Collection<? extends FieldKey> instanceFields = classSection.getSortedInstanceFields(key);
727         Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(key);
728         Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(key);
729         boolean classHasData = staticFields.size() > 0 ||
730                 instanceFields.size() > 0 ||
731                 directMethods.size() > 0 ||
732                 virtualMethods.size() > 0;
733 
734         if (classHasData) {
735             indexWriter.writeInt(offsetWriter.getPosition());
736         } else {
737             indexWriter.writeInt(NO_OFFSET);
738         }
739 
740         EncodedArrayKey staticInitializers = classSection.getStaticInitializers(key);
741         if (staticInitializers != null) {
742             indexWriter.writeInt(encodedArraySection.getItemOffset(staticInitializers));
743         } else {
744             indexWriter.writeInt(NO_OFFSET);
745         }
746 
747         // now write the class_data_item
748         if (classHasData) {
749             numClassDataItems++;
750 
751             offsetWriter.writeUleb128(staticFields.size());
752             offsetWriter.writeUleb128(instanceFields.size());
753             offsetWriter.writeUleb128(directMethods.size());
754             offsetWriter.writeUleb128(virtualMethods.size());
755 
756             writeEncodedFields(offsetWriter, staticFields);
757             writeEncodedFields(offsetWriter, instanceFields);
758             writeEncodedMethods(offsetWriter, directMethods);
759             writeEncodedMethods(offsetWriter, virtualMethods);
760         }
761 
762         return nextIndex;
763     }
764 
765     private void writeCallSites(DexDataWriter writer) throws IOException {
766         callSiteSectionOffset = writer.getPosition();
767 
768         List<Map.Entry<? extends CallSiteKey, Integer>> callSiteEntries =
769             new ArrayList<>(callSiteSection.getItems());
770         Collections.sort(callSiteEntries, callSiteComparator);
771 
772         int index = 0;
773 
774         for (Map.Entry<? extends CallSiteKey, Integer> callSite: callSiteEntries) {
775             callSite.setValue(index++);
776             writer.writeInt(encodedArraySection.getItemOffset(callSiteSection.getEncodedCallSite(callSite.getKey())));
777         }
778     }
779 
780     private void writeMethodHandles(DexDataWriter writer) throws IOException {
781         methodHandleSectionOffset = writer.getPosition();
782 
783         int index = 0;
784 
785         for (Entry<? extends MethodHandleKey, Integer> entry: methodHandleSection.getItems()) {
786             entry.setValue(index++);
787             MethodHandleKey methodHandleReference = entry.getKey();
788             writer.writeUshort(methodHandleReference.getMethodHandleType());
789             writer.writeUshort(0);
790             int memberIndex;
791             switch (methodHandleReference.getMethodHandleType()) {
792                 case MethodHandleType.STATIC_PUT:
793                 case MethodHandleType.STATIC_GET:
794                 case MethodHandleType.INSTANCE_PUT:
795                 case MethodHandleType.INSTANCE_GET:
796                     memberIndex = fieldSection.getItemIndex(
797                             methodHandleSection.getFieldReference(methodHandleReference));
798                     break;
799                 case MethodHandleType.INVOKE_STATIC:
800                 case MethodHandleType.INVOKE_INSTANCE:
801                 case MethodHandleType.INVOKE_CONSTRUCTOR:
802                 case MethodHandleType.INVOKE_DIRECT:
803                 case MethodHandleType.INVOKE_INTERFACE:
804                     memberIndex = methodSection.getItemIndex(
805                             methodHandleSection.getMethodReference(methodHandleReference));
806                     break;
807                 default:
808                     throw new ExceptionWithContext("Invalid method handle type: %d",
809                             methodHandleReference.getMethodHandleType());
810             }
811 
812             writer.writeUshort(memberIndex);
813             writer.writeUshort(0);
814         }
815     }
816 
817     private void writeEncodedFields(@Nonnull DexDataWriter writer, @Nonnull Collection<? extends FieldKey> fields)
818             throws IOException {
819         int prevIndex = 0;
820         for (FieldKey key: fields) {
821             int index = fieldSection.getFieldIndex(key);
822             if (!classSection.getFieldHiddenApiRestrictions(key).isEmpty()) {
823                 hasHiddenApiRestrictions = true;
824             }
825             writer.writeUleb128(index - prevIndex);
826             writer.writeUleb128(classSection.getFieldAccessFlags(key));
827             prevIndex = index;
828         }
829     }
830 
831     private void writeEncodedMethods(@Nonnull DexDataWriter writer, @Nonnull Collection<? extends MethodKey> methods)
832             throws IOException {
833         int prevIndex = 0;
834         for (MethodKey key: methods) {
835             int index = methodSection.getMethodIndex(key);
836             if (!classSection.getMethodHiddenApiRestrictions(key).isEmpty()) {
837                 hasHiddenApiRestrictions = true;
838             }
839             writer.writeUleb128(index-prevIndex);
840             writer.writeUleb128(classSection.getMethodAccessFlags(key));
841             writer.writeUleb128(classSection.getCodeItemOffset(key));
842             prevIndex = index;
843         }
844     }
845 
846     private void writeTypeLists(@Nonnull DexDataWriter writer) throws IOException {
847         writer.align();
848         typeListSectionOffset = writer.getPosition();
849         for (Map.Entry<? extends TypeListKey, Integer> entry: typeListSection.getItems()) {
850             writer.align();
851             entry.setValue(writer.getPosition());
852 
853             Collection<? extends TypeKey> types = typeListSection.getTypes(entry.getKey());
854             writer.writeInt(types.size());
855             for (TypeKey typeKey: types) {
856                 writer.writeUshort(typeSection.getItemIndex(typeKey));
857             }
858         }
859     }
860 
861     private void writeEncodedArrays(@Nonnull DexDataWriter writer) throws IOException {
862         InternalEncodedValueWriter encodedValueWriter = new InternalEncodedValueWriter(writer);
863         encodedArraySectionOffset = writer.getPosition();
864 
865         for (Map.Entry<? extends EncodedArrayKey, Integer> entry: encodedArraySection.getItems()) {
866             entry.setValue(writer.getPosition());
867             List<? extends EncodedValue> encodedArray = encodedArraySection.getEncodedValueList(entry.getKey());
868             writer.writeUleb128(encodedArray.size());
869             for (EncodedValue value: encodedArray) {
870                 writeEncodedValue(encodedValueWriter, value);
871             }
872         }
873     }
874 
875     private void writeAnnotations(@Nonnull DexDataWriter writer) throws IOException {
876         InternalEncodedValueWriter encodedValueWriter = new InternalEncodedValueWriter(writer);
877 
878         annotationSectionOffset = writer.getPosition();
879         for (Map.Entry<? extends AnnotationKey, Integer> entry: annotationSection.getItems()) {
880             entry.setValue(writer.getPosition());
881 
882             AnnotationKey key = entry.getKey();
883 
884             writer.writeUbyte(annotationSection.getVisibility(key));
885             writer.writeUleb128(typeSection.getItemIndex(annotationSection.getType(key)));
886 
887             Collection<? extends AnnotationElement> elements = CollectionUtils.immutableSortedCopy(
888                 annotationSection.getElements(key), BaseAnnotationElement.BY_NAME);
889 
890             writer.writeUleb128(elements.size());
891 
892             for (AnnotationElement element: elements) {
893                 writer.writeUleb128(stringSection.getItemIndex(annotationSection.getElementName(element)));
894                 writeEncodedValue(encodedValueWriter, annotationSection.getElementValue(element));
895             }
896         }
897     }
898 
899     private void writeAnnotationSets(@Nonnull DexDataWriter writer) throws IOException {
900         writer.align();
901         annotationSetSectionOffset = writer.getPosition();
902         if (shouldCreateEmptyAnnotationSet()) {
903             writer.writeInt(0);
904         }
905         for (Map.Entry<? extends AnnotationSetKey, Integer> entry: annotationSetSection.getItems()) {
906             Collection<? extends AnnotationKey> annotations = CollectionUtils.immutableSortedCopy(
907                     annotationSetSection.getAnnotations(entry.getKey()), BaseAnnotation.BY_TYPE);
908 
909             writer.align();
910             entry.setValue(writer.getPosition());
911             writer.writeInt(annotations.size());
912             for (AnnotationKey annotationKey: annotations) {
913                 writer.writeInt(annotationSection.getItemOffset(annotationKey));
914             }
915         }
916     }
917 
918     private void writeAnnotationSetRefs(@Nonnull DexDataWriter writer) throws IOException {
919         writer.align();
920         annotationSetRefSectionOffset = writer.getPosition();
921         HashMap<List<? extends AnnotationSetKey>, Integer> internedItems = new HashMap<>();
922 
923         for (ClassKey classKey: classSection.getSortedClasses()) {
924             for (MethodKey methodKey: classSection.getSortedMethods(classKey)) {
925                 List<? extends AnnotationSetKey> parameterAnnotations = classSection.getParameterAnnotations(methodKey);
926                 if (parameterAnnotations != null) {
927                     Integer prev = internedItems.get(parameterAnnotations);
928                     if (prev != null) {
929                         classSection.setAnnotationSetRefListOffset(methodKey, prev);
930                     } else {
931                         writer.align();
932                         int position = writer.getPosition();
933                         classSection.setAnnotationSetRefListOffset(methodKey, position);
934                         internedItems.put(parameterAnnotations, position);
935 
936                         numAnnotationSetRefItems++;
937 
938                         writer.writeInt(parameterAnnotations.size());
939                         for (AnnotationSetKey annotationSetKey: parameterAnnotations) {
940                             if (annotationSetSection.getAnnotations(annotationSetKey).size() > 0) {
941                                 writer.writeInt(annotationSetSection.getItemOffset(annotationSetKey));
942                             } else if (shouldCreateEmptyAnnotationSet()) {
943                                 writer.writeInt(annotationSetSectionOffset);
944                             } else {
945                                 writer.writeInt(NO_OFFSET);
946                             }
947                         }
948                     }
949                 }
950             }
951         }
952     }
953 
954     private void writeAnnotationDirectories(@Nonnull DexDataWriter writer) throws IOException {
955         writer.align();
956         annotationDirectorySectionOffset = writer.getPosition();
957         HashMap<AnnotationSetKey, Integer> internedItems = new HashMap<>();
958 
959         ByteBuffer tempBuffer = ByteBuffer.allocate(65536);
960         tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
961 
962         for (ClassKey key: classSection.getSortedClasses()) {
963             // first, we write the field/method/parameter items to a temporary buffer, so that we can get a count
964             // of each type, and determine if we even need to write an annotation directory for this class
965 
966             Collection<? extends FieldKey> fields = classSection.getSortedFields(key);
967             Collection<? extends MethodKey> methods = classSection.getSortedMethods(key);
968 
969             // this is how much space we'll need if every field and method has annotations.
970             int maxSize = fields.size() * 8 + methods.size() * 16;
971             if (maxSize > tempBuffer.capacity()) {
972                 tempBuffer = ByteBuffer.allocate(maxSize);
973                 tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
974             }
975 
976             ((Buffer) tempBuffer).clear();
977 
978             int fieldAnnotations = 0;
979             int methodAnnotations = 0;
980             int parameterAnnotations = 0;
981 
982             for (FieldKey field: fields) {
983                 AnnotationSetKey fieldAnnotationsKey = classSection.getFieldAnnotations(field);
984                 if (fieldAnnotationsKey != null) {
985                     fieldAnnotations++;
986                     tempBuffer.putInt(fieldSection.getFieldIndex(field));
987                     tempBuffer.putInt(annotationSetSection.getItemOffset(fieldAnnotationsKey));
988                 }
989             }
990 
991             for (MethodKey method: methods) {
992                 AnnotationSetKey methodAnnotationsKey = classSection.getMethodAnnotations(method);
993                 if (methodAnnotationsKey != null) {
994                     methodAnnotations++;
995                     tempBuffer.putInt(methodSection.getMethodIndex(method));
996                     tempBuffer.putInt(annotationSetSection.getItemOffset(methodAnnotationsKey));
997                 }
998             }
999 
1000             for (MethodKey method: methods) {
1001                 int offset = classSection.getAnnotationSetRefListOffset(method);
1002                 if (offset != DexWriter.NO_OFFSET) {
1003                     parameterAnnotations++;
1004                     tempBuffer.putInt(methodSection.getMethodIndex(method));
1005                     tempBuffer.putInt(offset);
1006                 }
1007             }
1008 
1009             // now, we finally know how many field/method/parameter annotations were written to the temp buffer
1010 
1011             AnnotationSetKey classAnnotationKey = classSection.getClassAnnotations(key);
1012             if (fieldAnnotations == 0 && methodAnnotations == 0 && parameterAnnotations == 0) {
1013                 if (classAnnotationKey != null) {
1014                     // This is an internable directory. Let's see if we've already written one like it
1015                     Integer directoryOffset = internedItems.get(classAnnotationKey);
1016                     if (directoryOffset != null) {
1017                         classSection.setAnnotationDirectoryOffset(key, directoryOffset);
1018                         continue;
1019                     } else {
1020                         internedItems.put(classAnnotationKey, writer.getPosition());
1021                     }
1022                 } else {
1023                     continue;
1024                 }
1025             }
1026 
1027             // yep, we need to write it out
1028             numAnnotationDirectoryItems++;
1029             classSection.setAnnotationDirectoryOffset(key, writer.getPosition());
1030 
1031             writer.writeInt(annotationSetSection.getNullableItemOffset(classAnnotationKey));
1032             writer.writeInt(fieldAnnotations);
1033             writer.writeInt(methodAnnotations);
1034             writer.writeInt(parameterAnnotations);
1035             writer.write(tempBuffer.array(), 0, tempBuffer.position());
1036         }
1037     }
1038 
1039     private static class CodeItemOffset<MethodKey> {
1040         @Nonnull MethodKey method;
1041         int codeOffset;
1042 
1043         private CodeItemOffset(@Nonnull MethodKey method, int codeOffset) {
1044             this.codeOffset = codeOffset;
1045             this.method = method;
1046         }
1047     }
1048 
1049     private void writeDebugAndCodeItems(@Nonnull DexDataWriter offsetWriter,
1050                                         @Nonnull DeferredOutputStream temp) throws IOException {
1051         ByteArrayOutputStream ehBuf = new ByteArrayOutputStream();
1052         debugSectionOffset = offsetWriter.getPosition();
1053 
1054         DexDataWriter codeWriter = new DexDataWriter(temp, 0);
1055 
1056         List<CodeItemOffset<MethodKey>> codeOffsets = new ArrayList<>();
1057 
1058         for (ClassKey classKey: classSection.getSortedClasses()) {
1059             Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
1060             Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
1061 
1062             Iterable<MethodKey> methods = new ChainedIterator<MethodKey>(
1063                 (Collection<MethodKey>)directMethods, (Collection<MethodKey>)virtualMethods);
1064 
1065             for (MethodKey methodKey: methods) {
1066                 List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks =
1067                         classSection.getTryBlocks(methodKey);
1068                 Iterable<? extends Instruction> instructions = classSection.getInstructions(methodKey);
1069                 Iterable<? extends DebugItem> debugItems = classSection.getDebugItems(methodKey);
1070 
1071                 if (instructions != null && stringSection.hasJumboIndexes()) {
1072                     boolean needsFix = false;
1073                     for (Instruction instruction: instructions) {
1074                         if (instruction.getOpcode() == Opcode.CONST_STRING) {
1075                             if (stringSection.getItemIndex(
1076                                     (StringRef)((ReferenceInstruction)instruction).getReference()) >= 65536) {
1077                                 needsFix = true;
1078                                 break;
1079                             }
1080                         }
1081                     }
1082 
1083                     if (needsFix) {
1084                         MutableMethodImplementation mutableMethodImplementation =
1085                                 classSection.makeMutableMethodImplementation(methodKey);
1086                         fixInstructions(mutableMethodImplementation);
1087 
1088                         instructions = mutableMethodImplementation.getInstructions();
1089                         tryBlocks = mutableMethodImplementation.getTryBlocks();
1090                         debugItems = mutableMethodImplementation.getDebugItems();
1091                     }
1092                 }
1093 
1094                 int debugItemOffset = writeDebugItem(offsetWriter, classSection.getParameterNames(methodKey), debugItems);
1095                 int codeItemOffset;
1096                 try {
1097                     codeItemOffset = writeCodeItem(
1098                             codeWriter, ehBuf, methodKey, tryBlocks, instructions, debugItemOffset);
1099                 } catch (RuntimeException ex) {
1100                     throw new ExceptionWithContext(ex, "Exception occurred while writing code_item for method %s",
1101                             methodSection.getMethodReference(methodKey));
1102                 }
1103 
1104                 if (codeItemOffset != -1) {
1105                     codeOffsets.add(new CodeItemOffset<MethodKey>(methodKey, codeItemOffset));
1106                 }
1107             }
1108         }
1109 
1110         offsetWriter.align();
1111         codeSectionOffset = offsetWriter.getPosition();
1112 
1113         codeWriter.close();
1114         temp.writeTo(offsetWriter);
1115         temp.close();
1116 
1117         for (CodeItemOffset<MethodKey> codeOffset: codeOffsets) {
1118             classSection.setCodeItemOffset(codeOffset.method, codeSectionOffset + codeOffset.codeOffset);
1119         }
1120     }
1121 
1122     private void fixInstructions(@Nonnull MutableMethodImplementation methodImplementation) {
1123         List<? extends Instruction> instructions = methodImplementation.getInstructions();
1124 
1125         for (int i=0; i<instructions.size(); i++) {
1126             Instruction instruction = instructions.get(i);
1127 
1128             if (instruction.getOpcode() == Opcode.CONST_STRING) {
1129                 if (stringSection.getItemIndex(
1130                         (StringRef)((ReferenceInstruction)instruction).getReference()) >= 65536) {
1131                     methodImplementation.replaceInstruction(i, new BuilderInstruction31c(Opcode.CONST_STRING_JUMBO,
1132                             ((OneRegisterInstruction)instruction).getRegisterA(),
1133                             ((ReferenceInstruction)instruction).getReference()));
1134                 }
1135             }
1136         }
1137     }
1138 
1139     private int writeDebugItem(@Nonnull DexDataWriter writer,
1140                                @Nullable Iterable<? extends StringKey> parameterNames,
1141                                @Nullable Iterable<? extends DebugItem> debugItems) throws IOException {
1142         int parameterCount = 0;
1143         int lastNamedParameterIndex = -1;
1144         if (parameterNames != null) {
1145             parameterCount = IteratorUtils.size(parameterNames);
1146             int index = 0;
1147             for (StringKey parameterName: parameterNames) {
1148                 if (parameterName != null) {
1149                     lastNamedParameterIndex = index;
1150                 }
1151                 index++;
1152             }
1153         }
1154 
1155 
1156         if (lastNamedParameterIndex == -1 && (debugItems == null
1157                 || !debugItems.iterator().hasNext())) {
1158             return NO_OFFSET;
1159         }
1160 
1161         int debugItemOffset = writer.getPosition();
1162         int startingLineNumber = 0;
1163 
1164         if (debugItems != null) {
1165             for (DebugItem debugItem: debugItems) {
1166                 if (debugItem instanceof LineNumber) {
1167                     startingLineNumber = ((LineNumber)debugItem).getLineNumber();
1168                     break;
1169                 }
1170             }
1171         }
1172 
1173         ByteArrayOutputStream tempByteOutput = new ByteArrayOutputStream();
1174         DexDataWriter tempDataWriter = new DexDataWriter(tempByteOutput, 0, 64);
1175         DebugWriter tempDebugWriter = new DebugWriter(stringSection, typeSection, tempDataWriter);
1176 
1177         tempDataWriter.writeUleb128(startingLineNumber);
1178 
1179         tempDataWriter.writeUleb128(parameterCount);
1180         if (parameterNames != null) {
1181             int index = 0;
1182             for (StringKey parameterName: parameterNames) {
1183                 if (index == parameterCount) {
1184                     break;
1185                 }
1186                 index++;
1187                 tempDataWriter.writeUleb128(stringSection.getNullableItemIndex(parameterName) + 1);
1188             }
1189         }
1190 
1191         if (debugItems != null) {
1192             tempDebugWriter.reset(startingLineNumber);
1193 
1194             for (DebugItem debugItem: debugItems) {
1195                 classSection.writeDebugItem(tempDebugWriter, debugItem);
1196             }
1197         }
1198         // write an END_SEQUENCE opcode, to end the debug item
1199         tempDataWriter.write(0);
1200 
1201         tempDataWriter.flush();
1202         byte[] debugInfo = tempByteOutput.toByteArray();
1203         DebugInfoCache wrapBytes = new DebugInfoCache(debugInfo);
1204         int cacheBytes = debugInfoCaches.getOrDefault(wrapBytes, -1);
1205         if (cacheBytes >= 0) {
1206             return cacheBytes;
1207         } else {
1208             writer.write(debugInfo);
1209             debugInfoCaches.put(wrapBytes, debugItemOffset);
1210             numDebugInfoItems++;
1211             return debugItemOffset;
1212         }
1213     }
1214 
1215     private int writeCodeItem(@Nonnull DexDataWriter writer,
1216                               @Nonnull ByteArrayOutputStream ehBuf,
1217                               @Nonnull MethodKey methodKey,
1218                               @Nonnull List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks,
1219                               @Nullable Iterable<? extends Instruction> instructions,
1220                               int debugItemOffset) throws IOException {
1221         if (instructions == null && debugItemOffset == NO_OFFSET) {
1222             return -1;
1223         }
1224 
1225         numCodeItemItems++;
1226 
1227         writer.align();
1228 
1229         int codeItemOffset = writer.getPosition();
1230 
1231         writer.writeUshort(classSection.getRegisterCount(methodKey));
1232 
1233         boolean isStatic = AccessFlags.STATIC.isSet(classSection.getMethodAccessFlags(methodKey));
1234         Collection<? extends TypeKey> parameters = typeListSection.getTypes(
1235                 protoSection.getParameters(methodSection.getPrototype(methodKey)));
1236 
1237         writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic));
1238 
1239         if (instructions != null) {
1240             tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks);
1241 
1242             int outParamCount = 0;
1243             int codeUnitCount = 0;
1244             for (Instruction instruction: instructions) {
1245                 codeUnitCount += instruction.getCodeUnits();
1246                 if (instruction.getOpcode().referenceType == ReferenceType.METHOD) {
1247                     ReferenceInstruction refInsn = (ReferenceInstruction)instruction;
1248                     MethodReference methodRef = (MethodReference)refInsn.getReference();
1249                     Opcode opcode = instruction.getOpcode();
1250                     int paramCount;
1251                     if (InstructionUtil.isInvokePolymorphic(opcode)) {
1252                         paramCount = ((VariableRegisterInstruction)instruction).getRegisterCount();
1253                     } else {
1254                         paramCount = MethodUtil.getParameterRegisterCount(methodRef, InstructionUtil.isInvokeStatic(opcode));
1255                     }
1256                     if (paramCount > outParamCount) {
1257                         outParamCount = paramCount;
1258                     }
1259                 }
1260             }
1261 
1262             writer.writeUshort(outParamCount);
1263             writer.writeUshort(tryBlocks.size());
1264             writer.writeInt(debugItemOffset);
1265 
1266             InstructionWriter instructionWriter =
1267                     InstructionWriter.makeInstructionWriter(opcodes, writer, stringSection, typeSection, fieldSection,
1268                             methodSection, protoSection, methodHandleSection, callSiteSection);
1269 
1270             writer.writeInt(codeUnitCount);
1271             int codeOffset = 0;
1272             for (Instruction instruction: instructions) {
1273                 try {
1274                     switch (instruction.getOpcode().format) {
1275                         case Format10t:
1276                             instructionWriter.write((Instruction10t)instruction);
1277                             break;
1278                         case Format10x:
1279                             instructionWriter.write((Instruction10x)instruction);
1280                             break;
1281                         case Format11n:
1282                             instructionWriter.write((Instruction11n)instruction);
1283                             break;
1284                         case Format11x:
1285                             instructionWriter.write((Instruction11x)instruction);
1286                             break;
1287                         case Format12x:
1288                             instructionWriter.write((Instruction12x)instruction);
1289                             break;
1290                         case Format20bc:
1291                             instructionWriter.write((Instruction20bc)instruction);
1292                             break;
1293                         case Format20t:
1294                             instructionWriter.write((Instruction20t)instruction);
1295                             break;
1296                         case Format21c:
1297                             instructionWriter.write((Instruction21c)instruction);
1298                             break;
1299                         case Format21ih:
1300                             instructionWriter.write((Instruction21ih)instruction);
1301                             break;
1302                         case Format21lh:
1303                             instructionWriter.write((Instruction21lh)instruction);
1304                             break;
1305                         case Format21s:
1306                             instructionWriter.write((Instruction21s)instruction);
1307                             break;
1308                         case Format21t:
1309                             instructionWriter.write((Instruction21t)instruction);
1310                             break;
1311                         case Format22b:
1312                             instructionWriter.write((Instruction22b)instruction);
1313                             break;
1314                         case Format22c:
1315                             instructionWriter.write((Instruction22c)instruction);
1316                             break;
1317                         case Format22cs:
1318                             instructionWriter.write((Instruction22cs)instruction);
1319                             break;
1320                         case Format22s:
1321                             instructionWriter.write((Instruction22s)instruction);
1322                             break;
1323                         case Format22t:
1324                             instructionWriter.write((Instruction22t)instruction);
1325                             break;
1326                         case Format22x:
1327                             instructionWriter.write((Instruction22x)instruction);
1328                             break;
1329                         case Format23x:
1330                             instructionWriter.write((Instruction23x)instruction);
1331                             break;
1332                         case Format30t:
1333                             instructionWriter.write((Instruction30t)instruction);
1334                             break;
1335                         case Format31c:
1336                             instructionWriter.write((Instruction31c)instruction);
1337                             break;
1338                         case Format31i:
1339                             instructionWriter.write((Instruction31i)instruction);
1340                             break;
1341                         case Format31t:
1342                             instructionWriter.write((Instruction31t)instruction);
1343                             break;
1344                         case Format32x:
1345                             instructionWriter.write((Instruction32x)instruction);
1346                             break;
1347                         case Format35c:
1348                             instructionWriter.write((Instruction35c)instruction);
1349                             break;
1350                         case Format35mi:
1351                             instructionWriter.write((Instruction35mi)instruction);
1352                             break;
1353                         case Format35ms:
1354                             instructionWriter.write((Instruction35ms)instruction);
1355                             break;
1356                         case Format3rc:
1357                             instructionWriter.write((Instruction3rc)instruction);
1358                             break;
1359                         case Format3rmi:
1360                             instructionWriter.write((Instruction3rmi)instruction);
1361                             break;
1362                         case Format3rms:
1363                             instructionWriter.write((Instruction3rms)instruction);
1364                             break;
1365                         case Format45cc:
1366                             instructionWriter.write((Instruction45cc)instruction);
1367                             break;
1368                         case Format4rcc:
1369                             instructionWriter.write((Instruction4rcc)instruction);
1370                             break;
1371                         case Format51l:
1372                             instructionWriter.write((Instruction51l)instruction);
1373                             break;
1374                         case ArrayPayload:
1375                             instructionWriter.write((ArrayPayload)instruction);
1376                             break;
1377                         case PackedSwitchPayload:
1378                             instructionWriter.write((PackedSwitchPayload)instruction);
1379                             break;
1380                         case SparseSwitchPayload:
1381                             instructionWriter.write((SparseSwitchPayload)instruction);
1382                             break;
1383                         default:
1384                             throw new ExceptionWithContext("Unsupported instruction format: %s",
1385                                     instruction.getOpcode().format);
1386                     }
1387                 } catch (RuntimeException ex) {
1388                     throw new ExceptionWithContext(ex, "Error while writing instruction at code offset 0x%x", codeOffset);
1389                 }
1390                 codeOffset += instruction.getCodeUnits();
1391             }
1392 
1393             if (tryBlocks.size() > 0) {
1394                 writer.align();
1395 
1396                 // filter out unique lists of exception handlers
1397                 Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = new HashMap<>();
1398                 for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
1399                     exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0);
1400                 }
1401                 DexDataWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size());
1402 
1403                 for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
1404                     int startAddress = tryBlock.getStartCodeAddress();
1405                     int endAddress = startAddress + tryBlock.getCodeUnitCount();
1406 
1407                     int tbCodeUnitCount = endAddress - startAddress;
1408 
1409                     writer.writeInt(startAddress);
1410                     writer.writeUshort(tbCodeUnitCount);
1411 
1412                     if (tryBlock.getExceptionHandlers().size() == 0) {
1413                         throw new ExceptionWithContext("No exception handlers for the try block!");
1414                     }
1415 
1416                     Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers());
1417                     if (offset != 0) {
1418                         // exception handler has already been written out, just use it
1419                         writer.writeUshort(offset);
1420                     } else {
1421                         // if offset has not been set yet, we are about to write out a new exception handler
1422                         offset = ehBuf.size();
1423                         writer.writeUshort(offset);
1424                         exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset);
1425 
1426                         // check if the last exception handler is a catch-all and adjust the size accordingly
1427                         int ehSize = tryBlock.getExceptionHandlers().size();
1428                         ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1);
1429                         if (ehLast.getExceptionType() == null) {
1430                             ehSize = ehSize * (-1) + 1;
1431                         }
1432 
1433                         // now let's layout the exception handlers, assuming that catch-all is always last
1434                         DexDataWriter.writeSleb128(ehBuf, ehSize);
1435                         for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) {
1436                             TypeKey exceptionTypeKey = classSection.getExceptionType(eh);
1437 
1438                             int codeAddress = eh.getHandlerCodeAddress();
1439 
1440                             if (exceptionTypeKey != null) {
1441                                 //regular exception handling
1442                                 DexDataWriter.writeUleb128(ehBuf, typeSection.getItemIndex(exceptionTypeKey));
1443                                 DexDataWriter.writeUleb128(ehBuf, codeAddress);
1444                             } else {
1445                                 //catch-all
1446                                 DexDataWriter.writeUleb128(ehBuf, codeAddress);
1447                             }
1448                         }
1449                     }
1450                 }
1451 
1452                 if (ehBuf.size() > 0) {
1453                     ehBuf.writeTo(writer);
1454                     ehBuf.reset();
1455                 }
1456             }
1457         } else {
1458             // no instructions, all we have is the debug item offset
1459             writer.writeUshort(0);
1460             writer.writeUshort(0);
1461             writer.writeInt(debugItemOffset);
1462             writer.writeInt(0);
1463         }
1464 
1465         return codeItemOffset;
1466     }
1467 
1468     private int calcNumItems() {
1469         int numItems = 0;
1470 
1471         // header item
1472         numItems++;
1473 
1474         if (stringSection.getItems().size() > 0) {
1475             numItems += 2; // index and data
1476         }
1477         if (typeSection.getItems().size()  > 0) {
1478             numItems++;
1479         }
1480         if (protoSection.getItems().size() > 0) {
1481             numItems++;
1482         }
1483         if (fieldSection.getItems().size() > 0) {
1484             numItems++;
1485         }
1486         if (methodSection.getItems().size() > 0) {
1487             numItems++;
1488         }
1489         if (callSiteSection.getItems().size() > 0) {
1490             numItems++;
1491         }
1492         if (methodHandleSection.getItems().size() > 0) {
1493             numItems++;
1494         }
1495         if (typeListSection.getItems().size() > 0) {
1496             numItems++;
1497         }
1498         if (encodedArraySection.getItems().size() > 0) {
1499             numItems++;
1500         }
1501         if (annotationSection.getItems().size() > 0) {
1502             numItems++;
1503         }
1504         if (annotationSetSection.getItems().size() > 0 || shouldCreateEmptyAnnotationSet()) {
1505             numItems++;
1506         }
1507         if (numAnnotationSetRefItems > 0) {
1508             numItems++;
1509         }
1510         if (numAnnotationDirectoryItems > 0) {
1511             numItems++;
1512         }
1513         if (numDebugInfoItems > 0) {
1514             numItems++;
1515         }
1516         if (numCodeItemItems > 0) {
1517             numItems++;
1518         }
1519         if (classSection.getItems().size() > 0) {
1520             numItems++;
1521         }
1522         if (numClassDataItems > 0) {
1523             numItems++;
1524         }
1525         if (shouldWriteHiddenApiRestrictions()) {
1526             numItems++;
1527         }
1528         // map item itself
1529         numItems++;
1530 
1531         return numItems;
1532     }
1533 
1534     private void writeMapItem(@Nonnull DexDataWriter writer) throws IOException{
1535         writer.align();
1536         mapSectionOffset = writer.getPosition();
1537         int numItems = calcNumItems();
1538 
1539         writer.writeInt(numItems);
1540 
1541         // index section
1542         writeMapItem(writer, ItemType.HEADER_ITEM, 1, 0);
1543         writeMapItem(writer, ItemType.STRING_ID_ITEM, stringSection.getItems().size(), stringIndexSectionOffset);
1544         writeMapItem(writer, ItemType.TYPE_ID_ITEM, typeSection.getItems().size(), typeSectionOffset);
1545         writeMapItem(writer, ItemType.PROTO_ID_ITEM, protoSection.getItems().size(), protoSectionOffset);
1546         writeMapItem(writer, ItemType.FIELD_ID_ITEM, fieldSection.getItems().size(), fieldSectionOffset);
1547         writeMapItem(writer, ItemType.METHOD_ID_ITEM, methodSection.getItems().size(), methodSectionOffset);
1548         writeMapItem(writer, ItemType.CLASS_DEF_ITEM, classSection.getItems().size(), classIndexSectionOffset);
1549         writeMapItem(writer, ItemType.CALL_SITE_ID_ITEM, callSiteSection.getItems().size(), callSiteSectionOffset);
1550         writeMapItem(writer, ItemType.METHOD_HANDLE_ITEM, methodHandleSection.getItems().size(),
1551                 methodHandleSectionOffset);
1552 
1553         // data section
1554         writeMapItem(writer, ItemType.STRING_DATA_ITEM, stringSection.getItems().size(), stringDataSectionOffset);
1555         writeMapItem(writer, ItemType.TYPE_LIST, typeListSection.getItems().size(), typeListSectionOffset);
1556         writeMapItem(writer, ItemType.ENCODED_ARRAY_ITEM, encodedArraySection.getItems().size(),
1557                 encodedArraySectionOffset);
1558         writeMapItem(writer, ItemType.ANNOTATION_ITEM, annotationSection.getItems().size(), annotationSectionOffset);
1559         writeMapItem(writer, ItemType.ANNOTATION_SET_ITEM,
1560                 annotationSetSection.getItems().size() + (shouldCreateEmptyAnnotationSet() ? 1 : 0),
1561                 annotationSetSectionOffset);
1562         writeMapItem(writer, ItemType.ANNOTATION_SET_REF_LIST, numAnnotationSetRefItems, annotationSetRefSectionOffset);
1563         writeMapItem(writer, ItemType.ANNOTATION_DIRECTORY_ITEM, numAnnotationDirectoryItems,
1564                 annotationDirectorySectionOffset);
1565         writeMapItem(writer, ItemType.DEBUG_INFO_ITEM, numDebugInfoItems, debugSectionOffset);
1566         writeMapItem(writer, ItemType.CODE_ITEM, numCodeItemItems, codeSectionOffset);
1567         writeMapItem(writer, ItemType.CLASS_DATA_ITEM, numClassDataItems, classDataSectionOffset);
1568 
1569         if (shouldWriteHiddenApiRestrictions()) {
1570             writeMapItem(writer, ItemType.HIDDENAPI_CLASS_DATA_ITEM, 1, hiddenApiRestrictionsOffset);
1571         }
1572 
1573         writeMapItem(writer, ItemType.MAP_LIST, 1, mapSectionOffset);
1574     }
1575 
1576     private void writeMapItem(@Nonnull DexDataWriter writer, int type, int size, int offset) throws IOException {
1577         if (size > 0) {
1578             writer.writeUshort(type);
1579             writer.writeUshort(0);
1580             writer.writeInt(size);
1581             writer.writeInt(offset);
1582         }
1583     }
1584 
1585     private void writeHeader(@Nonnull DexDataWriter writer, int dataOffset, int fileSize) throws IOException {
1586         // Write the appropriate header.
1587         writer.write(HeaderItem.getMagicForApi(opcodes.api));
1588 
1589         // checksum placeholder
1590         writer.writeInt(0);
1591 
1592         // signature placeholder
1593         writer.write(new byte[20]);
1594 
1595         writer.writeInt(fileSize);
1596         writer.writeInt(HeaderItem.ITEM_SIZE);
1597         writer.writeInt(HeaderItem.LITTLE_ENDIAN_TAG);
1598 
1599         // link
1600         writer.writeInt(0);
1601         writer.writeInt(0);
1602 
1603         // map
1604         writer.writeInt(mapSectionOffset);
1605 
1606         // index sections
1607 
1608         writeSectionInfo(writer, stringSection.getItems().size(), stringIndexSectionOffset);
1609         writeSectionInfo(writer, typeSection.getItems().size(), typeSectionOffset);
1610         writeSectionInfo(writer, protoSection.getItems().size(), protoSectionOffset);
1611         writeSectionInfo(writer, fieldSection.getItems().size(), fieldSectionOffset);
1612         writeSectionInfo(writer, methodSection.getItems().size(), methodSectionOffset);
1613         writeSectionInfo(writer, classSection.getItems().size(), classIndexSectionOffset);
1614 
1615         // data section
1616         writer.writeInt(fileSize - dataOffset);
1617         writer.writeInt(dataOffset);
1618     }
1619 
1620     private void writeSectionInfo(DexDataWriter writer, int numItems, int offset) throws IOException {
1621         writer.writeInt(numItems);
1622         if (numItems > 0) {
1623             writer.writeInt(offset);
1624         } else {
1625             writer.writeInt(0);
1626         }
1627     }
1628 
1629     private boolean shouldCreateEmptyAnnotationSet() {
1630         // Workaround for a crash in Dalvik VM before Jelly Bean MR1 (4.2)
1631         // which is triggered by NO_OFFSET in parameter annotation list.
1632         // (https://code.google.com/p/android/issues/detail?id=35304)
1633         return (opcodes.api < 17);
1634     }
1635 
1636     public abstract class SectionProvider {
1637         @Nonnull public abstract StringSectionType getStringSection();
1638         @Nonnull public abstract TypeSectionType getTypeSection();
1639         @Nonnull public abstract ProtoSectionType getProtoSection();
1640         @Nonnull public abstract FieldSectionType getFieldSection();
1641         @Nonnull public abstract MethodSectionType getMethodSection();
1642         @Nonnull public abstract ClassSectionType getClassSection();
1643         @Nonnull public abstract CallSiteSectionType getCallSiteSection();
1644         @Nonnull public abstract MethodHandleSectionType getMethodHandleSection();
1645         @Nonnull public abstract TypeListSectionType getTypeListSection();
1646         @Nonnull public abstract AnnotationSectionType getAnnotationSection();
1647         @Nonnull public abstract AnnotationSetSectionType getAnnotationSetSection();
1648         @Nonnull public abstract EncodedArraySectionType getEncodedArraySection();
1649     }
1650 }
1651