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