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