DexWriter.java revision df8e4802115fadd6bb67ba405aba2db885abbd55
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.*; 47import org.jf.dexlib2.util.MethodUtil; 48import org.jf.dexlib2.writer.util.InstructionWriteUtil; 49import org.jf.dexlib2.writer.util.TryListBuilder; 50import org.jf.util.CollectionUtils; 51import org.jf.util.ExceptionWithContext; 52import org.jf.util.RandomAccessFileOutputStream; 53 54import javax.annotation.Nonnull; 55import javax.annotation.Nullable; 56import java.io.ByteArrayOutputStream; 57import java.io.IOException; 58import java.io.RandomAccessFile; 59import java.nio.ByteBuffer; 60import java.nio.ByteOrder; 61import java.nio.channels.FileChannel; 62import java.security.MessageDigest; 63import java.security.NoSuchAlgorithmException; 64import java.util.*; 65import java.util.Map.Entry; 66import java.util.zip.Adler32; 67 68public abstract class DexWriter< 69 StringKey extends CharSequence, StringRef extends StringReference, TypeKey extends CharSequence, 70 TypeRef extends TypeReference, ProtoKey extends Comparable<ProtoKey>, 71 FieldRefKey extends FieldReference, MethodRefKey extends MethodReference, 72 BaseReference extends Reference, 73 ClassKey extends Comparable<? super ClassKey>, 74 AnnotationKey extends Annotation, AnnotationSetKey, 75 TypeListKey, 76 FieldKey, MethodKey, 77 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 final int api; 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 numAnnotationSetRefItems = 0; 106 protected int numAnnotationDirectoryItems = 0; 107 protected int numDebugInfoItems = 0; 108 protected int numCodeItemItems = 0; 109 protected int numClassDataItems = 0; 110 111 protected final InstructionFactory<? extends Insn, BaseReference> instructionFactory; 112 113 protected final StringSection<StringKey, StringRef> stringSection; 114 protected final TypeSection<StringKey, TypeKey, TypeRef> typeSection; 115 protected final ProtoSection<StringKey, TypeKey, ProtoKey, TypeListKey> protoSection; 116 protected final FieldSection<StringKey, TypeKey, FieldRefKey, FieldKey> fieldSection; 117 protected final MethodSection<StringKey, TypeKey, ProtoKey, MethodRefKey, MethodKey> methodSection; 118 protected final ClassSection<StringKey, TypeKey, TypeListKey, ClassKey, FieldKey, MethodKey, AnnotationSetKey, 119 EncodedValue, DebugItem, Insn, ExceptionHandler> classSection; 120 121 protected final TypeListSection<TypeKey, TypeListKey> typeListSection; 122 protected final AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement, EncodedValue> annotationSection; 123 protected final AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection; 124 125 protected DexWriter(int api, 126 InstructionFactory<? extends Insn, BaseReference> instructionFactory, 127 StringSection<StringKey, StringRef> stringSection, 128 TypeSection<StringKey, TypeKey, TypeRef> typeSection, 129 ProtoSection<StringKey, TypeKey, ProtoKey, TypeListKey> protoSection, 130 FieldSection<StringKey, TypeKey, FieldRefKey, FieldKey> fieldSection, 131 MethodSection<StringKey, TypeKey, ProtoKey, MethodRefKey, MethodKey> methodSection, 132 ClassSection<StringKey, TypeKey, TypeListKey, ClassKey, FieldKey, MethodKey, AnnotationSetKey, 133 EncodedValue, DebugItem, Insn, ExceptionHandler> classSection, 134 TypeListSection<TypeKey, TypeListKey> typeListSection, 135 AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement, 136 EncodedValue> annotationSection, 137 AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection) { 138 this.api = api; 139 this.instructionFactory = instructionFactory; 140 this.stringSection = stringSection; 141 this.typeSection = typeSection; 142 this.protoSection = protoSection; 143 this.fieldSection = fieldSection; 144 this.methodSection = methodSection; 145 this.classSection = classSection; 146 this.typeListSection = typeListSection; 147 this.annotationSection = annotationSection; 148 this.annotationSetSection = annotationSetSection; 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 recurse and double-write it 400 entry.setValue(0); 401 402 // first, try to write the superclass 403 Map.Entry<? extends ClassKey, Integer> superEntry = 404 classSection.getClassEntryByType(classSection.getSuperclass(key)); 405 nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, superEntry); 406 407 // then, try to write interfaces 408 for (TypeKey interfaceTypeKey: typeListSection.getTypes(classSection.getSortedInterfaces(key))) { 409 Map.Entry<? extends ClassKey, Integer> interfaceEntry = classSection.getClassEntryByType(interfaceTypeKey); 410 nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, interfaceEntry); 411 } 412 413 // now set the index for real 414 entry.setValue(nextIndex++); 415 416 // and finally, write the class itself 417 // first, the class_def_item 418 indexWriter.writeInt(typeSection.getItemIndex(classSection.getType(key))); 419 indexWriter.writeInt(classSection.getAccessFlags(key)); 420 indexWriter.writeInt(typeSection.getNullableItemIndex(classSection.getSuperclass(key))); 421 indexWriter.writeInt(typeListSection.getNullableItemOffset(classSection.getSortedInterfaces(key))); 422 indexWriter.writeInt(stringSection.getNullableItemIndex(classSection.getSourceFile(key))); 423 indexWriter.writeInt(classSection.getAnnotationDirectoryOffset(key)); 424 425 Collection<? extends FieldKey> staticFields = classSection.getSortedStaticFields(key); 426 Collection<? extends FieldKey> instanceFields = classSection.getSortedInstanceFields(key); 427 Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(key); 428 Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(key); 429 boolean classHasData = staticFields.size() > 0 || 430 instanceFields.size() > 0 || 431 directMethods.size() > 0 || 432 virtualMethods.size() > 0; 433 434 if (classHasData) { 435 indexWriter.writeInt(offsetWriter.getPosition()); 436 } else { 437 indexWriter.writeInt(0); 438 } 439 440 indexWriter.writeInt(classSection.getEncodedArrayOffset(key)); 441 442 // now write the class_data_item 443 if (classHasData) { 444 numClassDataItems++; 445 446 offsetWriter.writeUleb128(staticFields.size()); 447 offsetWriter.writeUleb128(instanceFields.size()); 448 offsetWriter.writeUleb128(directMethods.size()); 449 offsetWriter.writeUleb128(virtualMethods.size()); 450 451 writeEncodedFields(offsetWriter, staticFields); 452 writeEncodedFields(offsetWriter, instanceFields); 453 writeEncodedMethods(offsetWriter, directMethods); 454 writeEncodedMethods(offsetWriter, virtualMethods); 455 } 456 457 return nextIndex; 458 } 459 460 private void writeEncodedFields(@Nonnull DexDataWriter writer, @Nonnull Collection<? extends FieldKey> fields) 461 throws IOException { 462 int prevIndex = 0; 463 for (FieldKey key: fields) { 464 int index = fieldSection.getFieldIndex(key); 465 writer.writeUleb128(index-prevIndex); 466 writer.writeUleb128(classSection.getFieldAccessFlags(key)); 467 prevIndex = index; 468 } 469 } 470 471 private void writeEncodedMethods(@Nonnull DexDataWriter writer, @Nonnull Collection<? extends MethodKey> methods) 472 throws IOException { 473 int prevIndex = 0; 474 for (MethodKey key: methods) { 475 int index = methodSection.getMethodIndex(key); 476 writer.writeUleb128(index-prevIndex); 477 writer.writeUleb128(classSection.getMethodAccessFlags(key)); 478 writer.writeUleb128(classSection.getCodeItemOffset(key)); 479 prevIndex = index; 480 } 481 } 482 483 private void writeTypeLists(@Nonnull DexDataWriter writer) throws IOException { 484 writer.align(); 485 typeListSectionOffset = writer.getPosition(); 486 for (Map.Entry<? extends TypeListKey, Integer> entry: typeListSection.getItems()) { 487 writer.align(); 488 entry.setValue(writer.getPosition()); 489 490 Collection<? extends TypeKey> types = typeListSection.getTypes(entry.getKey()); 491 writer.writeInt(types.size()); 492 for (TypeKey typeKey: types) { 493 writer.writeUshort(typeSection.getItemIndex(typeKey)); 494 } 495 } 496 } 497 498 private static class EncodedArrayKey<EncodedValue> { 499 @Nonnull Collection<? extends EncodedValue> elements; 500 501 public EncodedArrayKey() { 502 } 503 504 @Override public int hashCode() { 505 return CollectionUtils.listHashCode(elements); 506 } 507 508 @Override public boolean equals(Object o) { 509 if (o instanceof EncodedArrayKey) { 510 EncodedArrayKey other = (EncodedArrayKey)o; 511 if (elements.size() != other.elements.size()) { 512 return false; 513 } 514 return Iterables.elementsEqual(elements, other.elements); 515 } 516 return false; 517 } 518 } 519 520 private void writeEncodedArrays(@Nonnull DexDataWriter writer) throws IOException { 521 InternalEncodedValueWriter encodedValueWriter = new InternalEncodedValueWriter(writer); 522 encodedArraySectionOffset = writer.getPosition(); 523 524 HashMap<EncodedArrayKey<EncodedValue>, Integer> internedItems = Maps.newHashMap(); 525 EncodedArrayKey<EncodedValue> key = new EncodedArrayKey<EncodedValue>(); 526 527 for (ClassKey classKey: classSection.getSortedClasses()) { 528 Collection <? extends EncodedValue> elements = classSection.getStaticInitializers(classKey); 529 if (elements != null && elements.size() > 0) { 530 key.elements = elements; 531 Integer prev = internedItems.get(key); 532 if (prev != null) { 533 classSection.setEncodedArrayOffset(classKey, prev); 534 } else { 535 int offset = writer.getPosition(); 536 internedItems.put(key, offset); 537 classSection.setEncodedArrayOffset(classKey, offset); 538 key = new EncodedArrayKey<EncodedValue>(); 539 540 numEncodedArrayItems++; 541 542 writer.writeUleb128(elements.size()); 543 for (EncodedValue value: elements) { 544 writeEncodedValue(encodedValueWriter, value); 545 } 546 } 547 } 548 } 549 } 550 551 private void writeAnnotations(@Nonnull DexDataWriter writer) throws IOException { 552 InternalEncodedValueWriter encodedValueWriter = new InternalEncodedValueWriter(writer); 553 554 annotationSectionOffset = writer.getPosition(); 555 for (Map.Entry<? extends AnnotationKey, Integer> entry: annotationSection.getItems()) { 556 entry.setValue(writer.getPosition()); 557 558 AnnotationKey key = entry.getKey(); 559 560 writer.writeUbyte(annotationSection.getVisibility(key)); 561 writer.writeUleb128(typeSection.getItemIndex(annotationSection.getType(key))); 562 563 Collection<? extends AnnotationElement> elements = 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 HashMap<List<? extends AnnotationSetKey>, Integer> internedItems = Maps.newHashMap(); 594 595 for (ClassKey classKey: classSection.getSortedClasses()) { 596 for (MethodKey methodKey: classSection.getSortedMethods(classKey)) { 597 List<? extends AnnotationSetKey> parameterAnnotations = classSection.getParameterAnnotations(methodKey); 598 if (parameterAnnotations != null) { 599 Integer prev = internedItems.get(parameterAnnotations); 600 if (prev != null) { 601 classSection.setAnnotationSetRefListOffset(methodKey, prev); 602 } else { 603 writer.align(); 604 int position = writer.getPosition(); 605 classSection.setAnnotationSetRefListOffset(methodKey, position); 606 internedItems.put(parameterAnnotations, position); 607 608 numAnnotationSetRefItems++; 609 610 writer.writeInt(parameterAnnotations.size()); 611 for (AnnotationSetKey annotationSetKey: parameterAnnotations) { 612 writer.writeInt(annotationSetSection.getItemOffset(annotationSetKey)); 613 } 614 } 615 } 616 } 617 } 618 } 619 620 private void writeAnnotationDirectories(@Nonnull DexDataWriter writer) throws IOException { 621 writer.align(); 622 annotationDirectorySectionOffset = writer.getPosition(); 623 HashMap<AnnotationSetKey, Integer> internedItems = Maps.newHashMap(); 624 625 ByteBuffer tempBuffer = ByteBuffer.allocate(65536); 626 tempBuffer.order(ByteOrder.LITTLE_ENDIAN); 627 628 for (ClassKey key: classSection.getSortedClasses()) { 629 // first, we write the field/method/parameter items to a temporary buffer, so that we can get a count 630 // of each type, and determine if we even need to write an annotation directory for this class 631 632 Collection<? extends FieldKey> fields = classSection.getSortedFields(key); 633 Collection<? extends MethodKey> methods = classSection.getSortedMethods(key); 634 635 // this is how much space we'll need if every field and method has annotations. 636 int maxSize = fields.size() * 8 + methods.size() * 16; 637 if (maxSize > tempBuffer.capacity()) { 638 tempBuffer = ByteBuffer.allocate(maxSize); 639 tempBuffer.order(ByteOrder.LITTLE_ENDIAN); 640 } 641 642 tempBuffer.clear(); 643 644 int fieldAnnotations = 0; 645 int methodAnnotations = 0; 646 int parameterAnnotations = 0; 647 648 for (FieldKey field: fields) { 649 AnnotationSetKey fieldAnnotationsKey = classSection.getFieldAnnotations(field); 650 if (fieldAnnotationsKey != null) { 651 fieldAnnotations++; 652 tempBuffer.putInt(fieldSection.getFieldIndex(field)); 653 tempBuffer.putInt(annotationSetSection.getItemOffset(fieldAnnotationsKey)); 654 } 655 } 656 657 for (MethodKey method: methods) { 658 AnnotationSetKey methodAnnotationsKey = classSection.getMethodAnnotations(method); 659 if (methodAnnotationsKey != null) { 660 methodAnnotations++; 661 tempBuffer.putInt(methodSection.getMethodIndex(method)); 662 tempBuffer.putInt(annotationSetSection.getItemOffset(methodAnnotationsKey)); 663 } 664 } 665 666 for (MethodKey method: methods) { 667 int offset = classSection.getAnnotationSetRefListOffset(method); 668 if (offset != DexWriter.NO_OFFSET) { 669 methodAnnotations++; 670 tempBuffer.putInt(methodSection.getMethodIndex(method)); 671 tempBuffer.putInt(offset); 672 } 673 } 674 675 // now, we finally know how many field/method/parameter annotations were written to the temp buffer 676 677 AnnotationSetKey classAnnotationKey = classSection.getClassAnnotations(key); 678 if (fieldAnnotations == 0 && methodAnnotations == 0 && parameterAnnotations == 0) { 679 if (classAnnotationKey != null) { 680 // This is an internable directory. Let's see if we've already written one like it 681 Integer directoryOffset = internedItems.get(classAnnotationKey); 682 if (directoryOffset != null) { 683 classSection.setAnnotationDirectoryOffset(key, directoryOffset); 684 continue; 685 } else { 686 internedItems.put(classAnnotationKey, writer.getPosition()); 687 } 688 } else { 689 continue; 690 } 691 } 692 693 // yep, we need to write it out 694 numAnnotationDirectoryItems++; 695 classSection.setAnnotationDirectoryOffset(key, writer.getPosition()); 696 697 writer.writeInt(annotationSetSection.getNullableItemOffset(classAnnotationKey)); 698 writer.writeInt(fieldAnnotations); 699 writer.writeInt(methodAnnotations); 700 writer.writeInt(parameterAnnotations); 701 writer.write(tempBuffer.array(), 0, tempBuffer.position()); 702 } 703 } 704 705 private void writeDebugItems(@Nonnull DexDataWriter writer) throws IOException { 706 debugSectionOffset = writer.getPosition(); 707 DebugWriter<StringKey, TypeKey> debugWriter = 708 new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, writer); 709 710 for (ClassKey classKey: classSection.getSortedClasses()) { 711 Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey); 712 Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey); 713 714 Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods); 715 716 for (MethodKey methodKey: methods) { 717 Iterable<? extends DebugItem> debugItems = classSection.getDebugItems(methodKey); 718 Iterable<? extends StringKey> parameterNames = classSection.getParameterNames(methodKey); 719 720 int parameterCount = 0; 721 if (parameterNames != null) { 722 int index = 0; 723 for (StringKey parameterName: parameterNames) { 724 index++; 725 if (parameterName != null) { 726 parameterCount = index; 727 } 728 } 729 } 730 731 if (debugItems == null && parameterCount == 0) { 732 continue; 733 } 734 735 numDebugInfoItems++; 736 737 classSection.setDebugItemOffset(methodKey, writer.getPosition()); 738 int startingLineNumber = 0; 739 740 if (debugItems != null) { 741 for (org.jf.dexlib2.iface.debug.DebugItem debugItem: debugItems) { 742 if (debugItem instanceof LineNumber) { 743 startingLineNumber = ((LineNumber)debugItem).getLineNumber(); 744 break; 745 } 746 } 747 } 748 writer.writeUleb128(startingLineNumber); 749 750 writer.writeUleb128(parameterCount); 751 if (parameterNames != null) { 752 int index = 0; 753 for (StringKey parameterName: parameterNames) { 754 if (index == parameterCount) { 755 break; 756 } 757 index++; 758 writer.writeUleb128(stringSection.getNullableItemIndex(parameterName) + 1); 759 } 760 } 761 762 if (debugItems != null) { 763 debugWriter.reset(startingLineNumber); 764 765 for (DebugItem debugItem: debugItems) { 766 classSection.writeDebugItem(debugWriter, debugItem); 767 } 768 } 769 // write an END_SEQUENCE opcode, to end the debug item 770 writer.write(0); 771 } 772 } 773 } 774 775 private void writeCodeItems(@Nonnull DexDataWriter writer) throws IOException { 776 ByteArrayOutputStream ehBuf = new ByteArrayOutputStream(); 777 778 writer.align(); 779 codeSectionOffset = writer.getPosition(); 780 for (ClassKey classKey: classSection.getSortedClasses()) { 781 Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey); 782 Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey); 783 784 Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods); 785 786 for (MethodKey methodKey: methods) { 787 Iterable<? extends Insn> instructions = classSection.getInstructions(methodKey); 788 int debugItemOffset = classSection.getDebugItemOffset(methodKey); 789 790 if (instructions == null && debugItemOffset == NO_OFFSET) { 791 continue; 792 } 793 794 numCodeItemItems++; 795 796 writer.align(); 797 classSection.setCodeItemOffset(methodKey, writer.getPosition()); 798 799 writer.writeUshort(classSection.getRegisterCount(methodKey)); 800 801 boolean isStatic = AccessFlags.STATIC.isSet(classSection.getMethodAccessFlags(methodKey)); 802 Collection<? extends TypeKey> parameters = typeListSection.getTypes( 803 protoSection.getParameters(methodSection.getPrototype(methodKey))); 804 805 List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = classSection.getTryBlocks(methodKey); 806 writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic)); 807 808 if (instructions != null) { 809 tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks); 810 811 InstructionWriteUtil<Insn, StringRef, BaseReference> instrWriteUtil = 812 new InstructionWriteUtil<Insn, StringRef, BaseReference>(instructions, stringSection, instructionFactory); 813 writer.writeUshort(instrWriteUtil.getOutParamCount()); 814 writer.writeUshort(tryBlocks.size()); 815 writer.writeInt(debugItemOffset); 816 817 InstructionWriter instructionWriter = 818 InstructionWriter.makeInstructionWriter(writer, stringSection, typeSection, fieldSection, 819 methodSection); 820 821 writer.writeInt(instrWriteUtil.getCodeUnitCount()); 822 for (Insn instruction: instrWriteUtil.getInstructions()) { 823 switch (instruction.getOpcode().format) { 824 case Format10t: 825 instructionWriter.write((Instruction10t)instruction); 826 break; 827 case Format10x: 828 instructionWriter.write((Instruction10x)instruction); 829 break; 830 case Format11n: 831 instructionWriter.write((Instruction11n)instruction); 832 break; 833 case Format11x: 834 instructionWriter.write((Instruction11x)instruction); 835 break; 836 case Format12x: 837 instructionWriter.write((Instruction12x)instruction); 838 break; 839 case Format20bc: 840 instructionWriter.write((Instruction20bc)instruction); 841 break; 842 case Format20t: 843 instructionWriter.write((Instruction20t)instruction); 844 break; 845 case Format21c: 846 instructionWriter.write((Instruction21c)instruction); 847 break; 848 case Format21ih: 849 instructionWriter.write((Instruction21ih)instruction); 850 break; 851 case Format21lh: 852 instructionWriter.write((Instruction21lh)instruction); 853 break; 854 case Format21s: 855 instructionWriter.write((Instruction21s)instruction); 856 break; 857 case Format21t: 858 instructionWriter.write((Instruction21t)instruction); 859 break; 860 case Format22b: 861 instructionWriter.write((Instruction22b)instruction); 862 break; 863 case Format22c: 864 instructionWriter.write((Instruction22c)instruction); 865 break; 866 case Format22s: 867 instructionWriter.write((Instruction22s)instruction); 868 break; 869 case Format22t: 870 instructionWriter.write((Instruction22t)instruction); 871 break; 872 case Format22x: 873 instructionWriter.write((Instruction22x)instruction); 874 break; 875 case Format23x: 876 instructionWriter.write((Instruction23x)instruction); 877 break; 878 case Format30t: 879 instructionWriter.write((Instruction30t)instruction); 880 break; 881 case Format31c: 882 instructionWriter.write((Instruction31c)instruction); 883 break; 884 case Format31i: 885 instructionWriter.write((Instruction31i)instruction); 886 break; 887 case Format31t: 888 instructionWriter.write((Instruction31t)instruction); 889 break; 890 case Format32x: 891 instructionWriter.write((Instruction32x)instruction); 892 break; 893 case Format35c: 894 instructionWriter.write((Instruction35c)instruction); 895 break; 896 case Format3rc: 897 instructionWriter.write((Instruction3rc)instruction); 898 break; 899 case Format51l: 900 instructionWriter.write((Instruction51l)instruction); 901 break; 902 case ArrayPayload: 903 instructionWriter.write((ArrayPayload)instruction); 904 break; 905 case PackedSwitchPayload: 906 instructionWriter.write((PackedSwitchPayload)instruction); 907 break; 908 case SparseSwitchPayload: 909 instructionWriter.write((SparseSwitchPayload)instruction); 910 break; 911 default: 912 throw new ExceptionWithContext("Unsupported instruction format: %s", 913 instruction.getOpcode().format); 914 } 915 } 916 917 if (tryBlocks.size() > 0) { 918 writer.align(); 919 920 921 922 // filter out unique lists of exception handlers 923 Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = Maps.newHashMap(); 924 for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) { 925 exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0); 926 } 927 DexDataWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size()); 928 929 for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) { 930 int startAddress = tryBlock.getStartCodeAddress(); 931 int endAddress = startAddress + tryBlock.getCodeUnitCount(); 932 933 startAddress += instrWriteUtil.codeOffsetShift(startAddress); 934 endAddress += instrWriteUtil.codeOffsetShift(endAddress); 935 int tbCodeUnitCount = endAddress - startAddress; 936 937 writer.writeInt(startAddress); 938 writer.writeUshort(tbCodeUnitCount); 939 940 if (tryBlock.getExceptionHandlers().size() == 0) { 941 throw new ExceptionWithContext("No exception handlers for the try block!"); 942 } 943 944 Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers()); 945 if (offset != 0) { 946 // exception handler has already been written out, just use it 947 writer.writeUshort(offset); 948 } else { 949 // if offset has not been set yet, we are about to write out a new exception handler 950 offset = ehBuf.size(); 951 writer.writeUshort(offset); 952 exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset); 953 954 // check if the last exception handler is a catch-all and adjust the size accordingly 955 int ehSize = tryBlock.getExceptionHandlers().size(); 956 ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1); 957 if (ehLast.getExceptionType() == null) { 958 ehSize = ehSize * (-1) + 1; 959 } 960 961 // now let's layout the exception handlers, assuming that catch-all is always last 962 DexDataWriter.writeSleb128(ehBuf, ehSize); 963 for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) { 964 TypeKey exceptionTypeKey = classSection.getExceptionType(eh); 965 966 int codeAddress = eh.getHandlerCodeAddress(); 967 codeAddress += instrWriteUtil.codeOffsetShift(codeAddress); 968 969 if (exceptionTypeKey != null) { 970 //regular exception handling 971 DexDataWriter.writeUleb128(ehBuf, typeSection.getItemIndex(exceptionTypeKey)); 972 DexDataWriter.writeUleb128(ehBuf, codeAddress); 973 } else { 974 //catch-all 975 DexDataWriter.writeUleb128(ehBuf, codeAddress); 976 } 977 } 978 } 979 } 980 981 if (ehBuf.size() > 0) { 982 ehBuf.writeTo(writer); 983 ehBuf.reset(); 984 } 985 } 986 } else { 987 // no instructions, all we have is the debug item offset 988 writer.writeUshort(0); 989 writer.writeUshort(0); 990 writer.writeInt(debugItemOffset); 991 writer.writeInt(0); 992 } 993 } 994 } 995 } 996 997 private int calcNumItems() { 998 int numItems = 0; 999 1000 // header item 1001 numItems++; 1002 1003 if (stringSection.getItems().size() > 0) { 1004 numItems += 2; // index and data 1005 } 1006 if (typeSection.getItems().size() > 0) { 1007 numItems++; 1008 } 1009 if (protoSection.getItems().size() > 0) { 1010 numItems++; 1011 } 1012 if (fieldSection.getItems().size() > 0) { 1013 numItems++; 1014 } 1015 if (methodSection.getItems().size() > 0) { 1016 numItems++; 1017 } 1018 if (typeListSection.getItems().size() > 0) { 1019 numItems++; 1020 } 1021 if (numEncodedArrayItems > 0) { 1022 numItems++; 1023 } 1024 if (annotationSection.getItems().size() > 0) { 1025 numItems++; 1026 } 1027 if (annotationSetSection.getItems().size() > 0) { 1028 numItems++; 1029 } 1030 if (numAnnotationSetRefItems > 0) { 1031 numItems++; 1032 } 1033 if (numAnnotationDirectoryItems > 0) { 1034 numItems++; 1035 } 1036 if (numDebugInfoItems > 0) { 1037 numItems++; 1038 } 1039 if (numCodeItemItems > 0) { 1040 numItems++; 1041 } 1042 if (classSection.getItems().size() > 0) { 1043 numItems++; 1044 } 1045 if (numClassDataItems > 0) { 1046 numItems++; 1047 } 1048 // map item itself 1049 numItems++; 1050 1051 return numItems; 1052 } 1053 1054 private void writeMapItem(@Nonnull DexDataWriter writer) throws IOException{ 1055 writer.align(); 1056 mapSectionOffset = writer.getPosition(); 1057 int numItems = calcNumItems(); 1058 1059 writer.writeInt(numItems); 1060 1061 // index section 1062 writeMapItem(writer, ItemType.HEADER_ITEM, 1, 0); 1063 writeMapItem(writer, ItemType.STRING_ID_ITEM, stringSection.getItems().size(), stringIndexSectionOffset); 1064 writeMapItem(writer, ItemType.TYPE_ID_ITEM, typeSection.getItems().size(), typeSectionOffset); 1065 writeMapItem(writer, ItemType.PROTO_ID_ITEM, protoSection.getItems().size(), protoSectionOffset); 1066 writeMapItem(writer, ItemType.FIELD_ID_ITEM, fieldSection.getItems().size(), fieldSectionOffset); 1067 writeMapItem(writer, ItemType.METHOD_ID_ITEM, methodSection.getItems().size(), methodSectionOffset); 1068 writeMapItem(writer, ItemType.CLASS_DEF_ITEM, classSection.getItems().size(), classIndexSectionOffset); 1069 1070 // data section 1071 writeMapItem(writer, ItemType.STRING_DATA_ITEM, stringSection.getItems().size(), stringDataSectionOffset); 1072 writeMapItem(writer, ItemType.TYPE_LIST, typeListSection.getItems().size(), typeListSectionOffset); 1073 writeMapItem(writer, ItemType.ENCODED_ARRAY_ITEM, numEncodedArrayItems, encodedArraySectionOffset); 1074 writeMapItem(writer, ItemType.ANNOTATION_ITEM, annotationSection.getItems().size(), annotationSectionOffset); 1075 writeMapItem(writer, ItemType.ANNOTATION_SET_ITEM, annotationSetSection.getItems().size(), 1076 annotationSetSectionOffset); 1077 writeMapItem(writer, ItemType.ANNOTATION_SET_REF_LIST, numAnnotationSetRefItems, annotationSetRefSectionOffset); 1078 writeMapItem(writer, ItemType.ANNOTATION_DIRECTORY_ITEM, numAnnotationDirectoryItems, 1079 annotationDirectorySectionOffset); 1080 writeMapItem(writer, ItemType.DEBUG_INFO_ITEM, numDebugInfoItems, debugSectionOffset); 1081 writeMapItem(writer, ItemType.CODE_ITEM, numCodeItemItems, codeSectionOffset); 1082 writeMapItem(writer, ItemType.CLASS_DATA_ITEM, numClassDataItems, classDataSectionOffset); 1083 writeMapItem(writer, ItemType.MAP_LIST, 1, mapSectionOffset); 1084 } 1085 1086 private void writeMapItem(@Nonnull DexDataWriter writer, int type, int size, int offset) throws IOException { 1087 if (size > 0) { 1088 writer.writeUshort(type); 1089 writer.writeUshort(0); 1090 writer.writeInt(size); 1091 writer.writeInt(offset); 1092 } 1093 } 1094 1095 private void writeHeader(@Nonnull DexDataWriter writer, int dataOffset, int fileSize) throws IOException { 1096 if (api < 14) { 1097 writer.write(HeaderItem.MAGIC_VALUES[0]); 1098 } else { 1099 writer.write(HeaderItem.MAGIC_VALUES[1]); 1100 } 1101 1102 // checksum placeholder 1103 writer.writeInt(0); 1104 1105 // signature placeholder 1106 writer.write(new byte[20]); 1107 1108 writer.writeInt(fileSize); 1109 writer.writeInt(HeaderItem.ITEM_SIZE); 1110 writer.writeInt(HeaderItem.LITTLE_ENDIAN_TAG); 1111 1112 // link 1113 writer.writeInt(0); 1114 writer.writeInt(0); 1115 1116 // map 1117 writer.writeInt(mapSectionOffset); 1118 1119 // index sections 1120 1121 writeSectionInfo(writer, stringSection.getItems().size(), stringIndexSectionOffset); 1122 writeSectionInfo(writer, typeSection.getItems().size(), typeSectionOffset); 1123 writeSectionInfo(writer, protoSection.getItems().size(), protoSectionOffset); 1124 writeSectionInfo(writer, fieldSection.getItems().size(), fieldSectionOffset); 1125 writeSectionInfo(writer, methodSection.getItems().size(), methodSectionOffset); 1126 writeSectionInfo(writer, classSection.getItems().size(), classIndexSectionOffset); 1127 1128 // data section 1129 writer.writeInt(fileSize - dataOffset); 1130 writer.writeInt(dataOffset); 1131 } 1132 1133 private void writeSectionInfo(DexDataWriter writer, int numItems, int offset) throws IOException { 1134 writer.writeInt(numItems); 1135 if (numItems > 0) { 1136 writer.writeInt(offset); 1137 } else { 1138 writer.writeInt(0); 1139 } 1140 } 1141} 1142