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