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