1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dex; 18 19 import com.android.dex.Code.CatchHandler; 20 import com.android.dex.Code.Try; 21 import com.android.dex.MethodHandle.MethodHandleType; 22 import com.android.dex.util.ByteInput; 23 import com.android.dex.util.ByteOutput; 24 import com.android.dex.util.FileUtils; 25 import java.io.ByteArrayOutputStream; 26 import java.io.File; 27 import java.io.FileInputStream; 28 import java.io.FileOutputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.OutputStream; 32 import java.io.UTFDataFormatException; 33 import java.nio.ByteBuffer; 34 import java.nio.ByteOrder; 35 import java.security.MessageDigest; 36 import java.security.NoSuchAlgorithmException; 37 import java.util.AbstractList; 38 import java.util.Collections; 39 import java.util.Iterator; 40 import java.util.List; 41 import java.util.NoSuchElementException; 42 import java.util.RandomAccess; 43 import java.util.zip.Adler32; 44 import java.util.zip.ZipEntry; 45 import java.util.zip.ZipFile; 46 47 /** 48 * The bytes of a dex file in memory for reading and writing. All int offsets 49 * are unsigned. 50 */ 51 public final class Dex { 52 private static final int CHECKSUM_OFFSET = 8; 53 private static final int CHECKSUM_SIZE = 4; 54 private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE; 55 private static final int SIGNATURE_SIZE = 20; 56 // Provided as a convenience to avoid a memory allocation to benefit Dalvik. 57 // Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik. 58 static final short[] EMPTY_SHORT_ARRAY = new short[0]; 59 60 private ByteBuffer data; 61 private final TableOfContents tableOfContents = new TableOfContents(); 62 private int nextSectionStart = 0; 63 private final StringTable strings = new StringTable(); 64 private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable(); 65 private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable(); 66 private final ProtoIdTable protoIds = new ProtoIdTable(); 67 private final FieldIdTable fieldIds = new FieldIdTable(); 68 private final MethodIdTable methodIds = new MethodIdTable(); 69 70 /** 71 * Creates a new dex that reads from {@code data}. It is an error to modify 72 * {@code data} after using it to create a dex buffer. 73 */ Dex(byte[] data)74 public Dex(byte[] data) throws IOException { 75 this(ByteBuffer.wrap(data)); 76 } 77 Dex(ByteBuffer data)78 private Dex(ByteBuffer data) throws IOException { 79 this.data = data; 80 this.data.order(ByteOrder.LITTLE_ENDIAN); 81 this.tableOfContents.readFrom(this); 82 } 83 84 /** 85 * Creates a new empty dex of the specified size. 86 */ Dex(int byteCount)87 public Dex(int byteCount) throws IOException { 88 this.data = ByteBuffer.wrap(new byte[byteCount]); 89 this.data.order(ByteOrder.LITTLE_ENDIAN); 90 } 91 92 /** 93 * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}. 94 */ Dex(InputStream in)95 public Dex(InputStream in) throws IOException { 96 try { 97 loadFrom(in); 98 } finally { 99 in.close(); 100 } 101 } 102 103 /** 104 * Creates a new dex buffer from the dex file {@code file}. 105 */ Dex(File file)106 public Dex(File file) throws IOException { 107 if (FileUtils.hasArchiveSuffix(file.getName())) { 108 ZipFile zipFile = new ZipFile(file); 109 ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME); 110 if (entry != null) { 111 try (InputStream inputStream = zipFile.getInputStream(entry)) { 112 loadFrom(inputStream); 113 } 114 zipFile.close(); 115 } else { 116 throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file); 117 } 118 } else if (file.getName().endsWith(".dex")) { 119 try (InputStream inputStream = new FileInputStream(file)) { 120 loadFrom(inputStream); 121 } 122 } else { 123 throw new DexException("unknown output extension: " + file); 124 } 125 } 126 127 /** 128 * It is the caller's responsibility to close {@code in}. 129 */ loadFrom(InputStream in)130 private void loadFrom(InputStream in) throws IOException { 131 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 132 byte[] buffer = new byte[8192]; 133 134 int count; 135 while ((count = in.read(buffer)) != -1) { 136 bytesOut.write(buffer, 0, count); 137 } 138 139 this.data = ByteBuffer.wrap(bytesOut.toByteArray()); 140 this.data.order(ByteOrder.LITTLE_ENDIAN); 141 this.tableOfContents.readFrom(this); 142 } 143 checkBounds(int index, int length)144 private static void checkBounds(int index, int length) { 145 if (index < 0 || index >= length) { 146 throw new IndexOutOfBoundsException("index:" + index + ", length=" + length); 147 } 148 } 149 writeTo(OutputStream out)150 public void writeTo(OutputStream out) throws IOException { 151 byte[] buffer = new byte[8192]; 152 ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe 153 data.clear(); 154 while (data.hasRemaining()) { 155 int count = Math.min(buffer.length, data.remaining()); 156 data.get(buffer, 0, count); 157 out.write(buffer, 0, count); 158 } 159 } 160 writeTo(File dexOut)161 public void writeTo(File dexOut) throws IOException { 162 try (OutputStream out = new FileOutputStream(dexOut)) { 163 writeTo(out); 164 } 165 } 166 getTableOfContents()167 public TableOfContents getTableOfContents() { 168 return tableOfContents; 169 } 170 open(int position)171 public Section open(int position) { 172 if (position < 0 || position >= data.capacity()) { 173 throw new IllegalArgumentException("position=" + position 174 + " length=" + data.capacity()); 175 } 176 ByteBuffer sectionData = data.duplicate(); 177 sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary? 178 sectionData.position(position); 179 sectionData.limit(data.capacity()); 180 return new Section("section", sectionData); 181 } 182 appendSection(int maxByteCount, String name)183 public Section appendSection(int maxByteCount, String name) { 184 if ((maxByteCount & 3) != 0) { 185 throw new IllegalStateException("Not four byte aligned!"); 186 } 187 int limit = nextSectionStart + maxByteCount; 188 ByteBuffer sectionData = data.duplicate(); 189 sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary? 190 sectionData.position(nextSectionStart); 191 sectionData.limit(limit); 192 Section result = new Section(name, sectionData); 193 nextSectionStart = limit; 194 return result; 195 } 196 getLength()197 public int getLength() { 198 return data.capacity(); 199 } 200 getNextSectionStart()201 public int getNextSectionStart() { 202 return nextSectionStart; 203 } 204 205 /** 206 * Returns a copy of the the bytes of this dex. 207 */ getBytes()208 public byte[] getBytes() { 209 ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe 210 byte[] result = new byte[data.capacity()]; 211 data.position(0); 212 data.get(result); 213 return result; 214 } 215 strings()216 public List<String> strings() { 217 return strings; 218 } 219 typeIds()220 public List<Integer> typeIds() { 221 return typeIds; 222 } 223 typeNames()224 public List<String> typeNames() { 225 return typeNames; 226 } 227 protoIds()228 public List<ProtoId> protoIds() { 229 return protoIds; 230 } 231 fieldIds()232 public List<FieldId> fieldIds() { 233 return fieldIds; 234 } 235 methodIds()236 public List<MethodId> methodIds() { 237 return methodIds; 238 } 239 classDefs()240 public Iterable<ClassDef> classDefs() { 241 return new ClassDefIterable(); 242 } 243 readTypeList(int offset)244 public TypeList readTypeList(int offset) { 245 if (offset == 0) { 246 return TypeList.EMPTY; 247 } 248 return open(offset).readTypeList(); 249 } 250 readClassData(ClassDef classDef)251 public ClassData readClassData(ClassDef classDef) { 252 int offset = classDef.getClassDataOffset(); 253 if (offset == 0) { 254 throw new IllegalArgumentException("offset == 0"); 255 } 256 return open(offset).readClassData(); 257 } 258 readCode(ClassData.Method method)259 public Code readCode(ClassData.Method method) { 260 int offset = method.getCodeOffset(); 261 if (offset == 0) { 262 throw new IllegalArgumentException("offset == 0"); 263 } 264 return open(offset).readCode(); 265 } 266 267 /** 268 * Returns the signature of all but the first 32 bytes of this dex. The 269 * first 32 bytes of dex files are not specified to be included in the 270 * signature. 271 */ computeSignature()272 public byte[] computeSignature() throws IOException { 273 MessageDigest digest; 274 try { 275 digest = MessageDigest.getInstance("SHA-1"); 276 } catch (NoSuchAlgorithmException e) { 277 throw new AssertionError(); 278 } 279 byte[] buffer = new byte[8192]; 280 ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe 281 data.limit(data.capacity()); 282 data.position(SIGNATURE_OFFSET + SIGNATURE_SIZE); 283 while (data.hasRemaining()) { 284 int count = Math.min(buffer.length, data.remaining()); 285 data.get(buffer, 0, count); 286 digest.update(buffer, 0, count); 287 } 288 return digest.digest(); 289 } 290 291 /** 292 * Returns the checksum of all but the first 12 bytes of {@code dex}. 293 */ computeChecksum()294 public int computeChecksum() throws IOException { 295 Adler32 adler32 = new Adler32(); 296 byte[] buffer = new byte[8192]; 297 ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe 298 data.limit(data.capacity()); 299 data.position(CHECKSUM_OFFSET + CHECKSUM_SIZE); 300 while (data.hasRemaining()) { 301 int count = Math.min(buffer.length, data.remaining()); 302 data.get(buffer, 0, count); 303 adler32.update(buffer, 0, count); 304 } 305 return (int) adler32.getValue(); 306 } 307 308 /** 309 * Generates the signature and checksum of the dex file {@code out} and 310 * writes them to the file. 311 */ writeHashes()312 public void writeHashes() throws IOException { 313 open(SIGNATURE_OFFSET).write(computeSignature()); 314 open(CHECKSUM_OFFSET).writeInt(computeChecksum()); 315 } 316 317 /** 318 * Look up a descriptor index from a type index. Cheaper than: 319 * {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();} 320 */ descriptorIndexFromTypeIndex(int typeIndex)321 public int descriptorIndexFromTypeIndex(int typeIndex) { 322 checkBounds(typeIndex, tableOfContents.typeIds.size); 323 int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex); 324 return data.getInt(position); 325 } 326 327 328 public final class Section implements ByteInput, ByteOutput { 329 private final String name; 330 private final ByteBuffer data; 331 private final int initialPosition; 332 Section(String name, ByteBuffer data)333 private Section(String name, ByteBuffer data) { 334 this.name = name; 335 this.data = data; 336 this.initialPosition = data.position(); 337 } 338 getPosition()339 public int getPosition() { 340 return data.position(); 341 } 342 readInt()343 public int readInt() { 344 return data.getInt(); 345 } 346 readShort()347 public short readShort() { 348 return data.getShort(); 349 } 350 readUnsignedShort()351 public int readUnsignedShort() { 352 return readShort() & 0xffff; 353 } 354 355 @Override readByte()356 public byte readByte() { 357 return data.get(); 358 } 359 readByteArray(int length)360 public byte[] readByteArray(int length) { 361 byte[] result = new byte[length]; 362 data.get(result); 363 return result; 364 } 365 readShortArray(int length)366 public short[] readShortArray(int length) { 367 if (length == 0) { 368 return EMPTY_SHORT_ARRAY; 369 } 370 short[] result = new short[length]; 371 for (int i = 0; i < length; i++) { 372 result[i] = readShort(); 373 } 374 return result; 375 } 376 readUleb128()377 public int readUleb128() { 378 return Leb128.readUnsignedLeb128(this); 379 } 380 readUleb128p1()381 public int readUleb128p1() { 382 return Leb128.readUnsignedLeb128(this) - 1; 383 } 384 readSleb128()385 public int readSleb128() { 386 return Leb128.readSignedLeb128(this); 387 } 388 writeUleb128p1(int i)389 public void writeUleb128p1(int i) { 390 writeUleb128(i + 1); 391 } 392 readTypeList()393 public TypeList readTypeList() { 394 int size = readInt(); 395 short[] types = readShortArray(size); 396 alignToFourBytes(); 397 return new TypeList(Dex.this, types); 398 } 399 readString()400 public String readString() { 401 int offset = readInt(); 402 int savedPosition = data.position(); 403 int savedLimit = data.limit(); 404 data.position(offset); 405 data.limit(data.capacity()); 406 try { 407 int expectedLength = readUleb128(); 408 String result = Mutf8.decode(this, new char[expectedLength]); 409 if (result.length() != expectedLength) { 410 throw new DexException("Declared length " + expectedLength 411 + " doesn't match decoded length of " + result.length()); 412 } 413 return result; 414 } catch (UTFDataFormatException e) { 415 throw new DexException(e); 416 } finally { 417 data.position(savedPosition); 418 data.limit(savedLimit); 419 } 420 } 421 readFieldId()422 public FieldId readFieldId() { 423 int declaringClassIndex = readUnsignedShort(); 424 int typeIndex = readUnsignedShort(); 425 int nameIndex = readInt(); 426 return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex); 427 } 428 readMethodId()429 public MethodId readMethodId() { 430 int declaringClassIndex = readUnsignedShort(); 431 int protoIndex = readUnsignedShort(); 432 int nameIndex = readInt(); 433 return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex); 434 } 435 readProtoId()436 public ProtoId readProtoId() { 437 int shortyIndex = readInt(); 438 int returnTypeIndex = readInt(); 439 int parametersOffset = readInt(); 440 return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset); 441 } 442 readCallSiteId()443 public CallSiteId readCallSiteId() { 444 int offset = readInt(); 445 return new CallSiteId(Dex.this, offset); 446 } 447 readMethodHandle()448 public MethodHandle readMethodHandle() { 449 MethodHandleType methodHandleType = MethodHandleType.fromValue(readUnsignedShort()); 450 int unused1 = readUnsignedShort(); 451 int fieldOrMethodId = readUnsignedShort(); 452 int unused2 = readUnsignedShort(); 453 return new MethodHandle(Dex.this, methodHandleType, unused1, fieldOrMethodId, unused2); 454 } 455 readClassDef()456 public ClassDef readClassDef() { 457 int offset = getPosition(); 458 int type = readInt(); 459 int accessFlags = readInt(); 460 int supertype = readInt(); 461 int interfacesOffset = readInt(); 462 int sourceFileIndex = readInt(); 463 int annotationsOffset = readInt(); 464 int classDataOffset = readInt(); 465 int staticValuesOffset = readInt(); 466 return new ClassDef(Dex.this, offset, type, accessFlags, supertype, 467 interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset, 468 staticValuesOffset); 469 } 470 readCode()471 private Code readCode() { 472 int registersSize = readUnsignedShort(); 473 int insSize = readUnsignedShort(); 474 int outsSize = readUnsignedShort(); 475 int triesSize = readUnsignedShort(); 476 int debugInfoOffset = readInt(); 477 int instructionsSize = readInt(); 478 short[] instructions = readShortArray(instructionsSize); 479 Try[] tries; 480 CatchHandler[] catchHandlers; 481 if (triesSize > 0) { 482 if (instructions.length % 2 == 1) { 483 readShort(); // padding 484 } 485 486 /* 487 * We can't read the tries until we've read the catch handlers. 488 * Unfortunately they're in the opposite order in the dex file 489 * so we need to read them out-of-order. 490 */ 491 Section triesSection = open(data.position()); 492 skip(triesSize * SizeOf.TRY_ITEM); 493 catchHandlers = readCatchHandlers(); 494 tries = triesSection.readTries(triesSize, catchHandlers); 495 } else { 496 tries = new Try[0]; 497 catchHandlers = new CatchHandler[0]; 498 } 499 return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions, 500 tries, catchHandlers); 501 } 502 readCatchHandlers()503 private CatchHandler[] readCatchHandlers() { 504 int baseOffset = data.position(); 505 int catchHandlersSize = readUleb128(); 506 CatchHandler[] result = new CatchHandler[catchHandlersSize]; 507 for (int i = 0; i < catchHandlersSize; i++) { 508 int offset = data.position() - baseOffset; 509 result[i] = readCatchHandler(offset); 510 } 511 return result; 512 } 513 readTries(int triesSize, CatchHandler[] catchHandlers)514 private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) { 515 Try[] result = new Try[triesSize]; 516 for (int i = 0; i < triesSize; i++) { 517 int startAddress = readInt(); 518 int instructionCount = readUnsignedShort(); 519 int handlerOffset = readUnsignedShort(); 520 int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset); 521 result[i] = new Try(startAddress, instructionCount, catchHandlerIndex); 522 } 523 return result; 524 } 525 findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset)526 private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) { 527 for (int i = 0; i < catchHandlers.length; i++) { 528 CatchHandler catchHandler = catchHandlers[i]; 529 if (catchHandler.getOffset() == offset) { 530 return i; 531 } 532 } 533 throw new IllegalArgumentException(); 534 } 535 readCatchHandler(int offset)536 private CatchHandler readCatchHandler(int offset) { 537 int size = readSleb128(); 538 int handlersCount = Math.abs(size); 539 int[] typeIndexes = new int[handlersCount]; 540 int[] addresses = new int[handlersCount]; 541 for (int i = 0; i < handlersCount; i++) { 542 typeIndexes[i] = readUleb128(); 543 addresses[i] = readUleb128(); 544 } 545 int catchAllAddress = size <= 0 ? readUleb128() : -1; 546 return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset); 547 } 548 readClassData()549 private ClassData readClassData() { 550 int staticFieldsSize = readUleb128(); 551 int instanceFieldsSize = readUleb128(); 552 int directMethodsSize = readUleb128(); 553 int virtualMethodsSize = readUleb128(); 554 ClassData.Field[] staticFields = readFields(staticFieldsSize); 555 ClassData.Field[] instanceFields = readFields(instanceFieldsSize); 556 ClassData.Method[] directMethods = readMethods(directMethodsSize); 557 ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize); 558 return new ClassData(staticFields, instanceFields, directMethods, virtualMethods); 559 } 560 readFields(int count)561 private ClassData.Field[] readFields(int count) { 562 ClassData.Field[] result = new ClassData.Field[count]; 563 int fieldIndex = 0; 564 for (int i = 0; i < count; i++) { 565 fieldIndex += readUleb128(); // field index diff 566 int accessFlags = readUleb128(); 567 result[i] = new ClassData.Field(fieldIndex, accessFlags); 568 } 569 return result; 570 } 571 readMethods(int count)572 private ClassData.Method[] readMethods(int count) { 573 ClassData.Method[] result = new ClassData.Method[count]; 574 int methodIndex = 0; 575 for (int i = 0; i < count; i++) { 576 methodIndex += readUleb128(); // method index diff 577 int accessFlags = readUleb128(); 578 int codeOff = readUleb128(); 579 result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff); 580 } 581 return result; 582 } 583 584 /** 585 * Returns a byte array containing the bytes from {@code start} to this 586 * section's current position. 587 */ getBytesFrom(int start)588 private byte[] getBytesFrom(int start) { 589 int end = data.position(); 590 byte[] result = new byte[end - start]; 591 data.position(start); 592 data.get(result); 593 return result; 594 } 595 readAnnotation()596 public Annotation readAnnotation() { 597 byte visibility = readByte(); 598 int start = data.position(); 599 new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue(); 600 return new Annotation(Dex.this, visibility, new EncodedValue(getBytesFrom(start))); 601 } 602 readEncodedArray()603 public EncodedValue readEncodedArray() { 604 int start = data.position(); 605 new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue(); 606 return new EncodedValue(getBytesFrom(start)); 607 } 608 skip(int count)609 public void skip(int count) { 610 if (count < 0) { 611 throw new IllegalArgumentException(); 612 } 613 data.position(data.position() + count); 614 } 615 616 /** 617 * Skips bytes until the position is aligned to a multiple of 4. 618 */ alignToFourBytes()619 public void alignToFourBytes() { 620 data.position((data.position() + 3) & ~3); 621 } 622 623 /** 624 * Writes 0x00 until the position is aligned to a multiple of 4. 625 */ alignToFourBytesWithZeroFill()626 public void alignToFourBytesWithZeroFill() { 627 while ((data.position() & 3) != 0) { 628 data.put((byte) 0); 629 } 630 } 631 assertFourByteAligned()632 public void assertFourByteAligned() { 633 if ((data.position() & 3) != 0) { 634 throw new IllegalStateException("Not four byte aligned!"); 635 } 636 } 637 write(byte[] bytes)638 public void write(byte[] bytes) { 639 this.data.put(bytes); 640 } 641 642 @Override writeByte(int b)643 public void writeByte(int b) { 644 data.put((byte) b); 645 } 646 writeShort(short i)647 public void writeShort(short i) { 648 data.putShort(i); 649 } 650 writeUnsignedShort(int i)651 public void writeUnsignedShort(int i) { 652 short s = (short) i; 653 if (i != (s & 0xffff)) { 654 throw new IllegalArgumentException("Expected an unsigned short: " + i); 655 } 656 writeShort(s); 657 } 658 write(short[] shorts)659 public void write(short[] shorts) { 660 for (short s : shorts) { 661 writeShort(s); 662 } 663 } 664 writeInt(int i)665 public void writeInt(int i) { 666 data.putInt(i); 667 } 668 writeUleb128(int i)669 public void writeUleb128(int i) { 670 try { 671 Leb128.writeUnsignedLeb128(this, i); 672 } catch (ArrayIndexOutOfBoundsException e) { 673 throw new DexException("Section limit " + data.limit() + " exceeded by " + name); 674 } 675 } 676 writeSleb128(int i)677 public void writeSleb128(int i) { 678 try { 679 Leb128.writeSignedLeb128(this, i); 680 } catch (ArrayIndexOutOfBoundsException e) { 681 throw new DexException("Section limit " + data.limit() + " exceeded by " + name); 682 } 683 } 684 writeStringData(String value)685 public void writeStringData(String value) { 686 try { 687 int length = value.length(); 688 writeUleb128(length); 689 write(Mutf8.encode(value)); 690 writeByte(0); 691 } catch (UTFDataFormatException e) { 692 throw new AssertionError(); 693 } 694 } 695 writeTypeList(TypeList typeList)696 public void writeTypeList(TypeList typeList) { 697 short[] types = typeList.getTypes(); 698 writeInt(types.length); 699 for (short type : types) { 700 writeShort(type); 701 } 702 alignToFourBytesWithZeroFill(); 703 } 704 705 /** 706 * Returns the number of bytes used by this section. 707 */ used()708 public int used() { 709 return data.position() - initialPosition; 710 } 711 } 712 713 private final class StringTable extends AbstractList<String> implements RandomAccess { 714 @Override get(int index)715 public String get(int index) { 716 checkBounds(index, tableOfContents.stringIds.size); 717 return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM)) 718 .readString(); 719 } 720 @Override size()721 public int size() { 722 return tableOfContents.stringIds.size; 723 } 724 } 725 726 private final class TypeIndexToDescriptorIndexTable extends AbstractList<Integer> 727 implements RandomAccess { 728 @Override get(int index)729 public Integer get(int index) { 730 return descriptorIndexFromTypeIndex(index); 731 } 732 @Override size()733 public int size() { 734 return tableOfContents.typeIds.size; 735 } 736 } 737 738 private final class TypeIndexToDescriptorTable extends AbstractList<String> 739 implements RandomAccess { 740 @Override get(int index)741 public String get(int index) { 742 return strings.get(descriptorIndexFromTypeIndex(index)); 743 } 744 @Override size()745 public int size() { 746 return tableOfContents.typeIds.size; 747 } 748 } 749 750 private final class ProtoIdTable extends AbstractList<ProtoId> implements RandomAccess { 751 @Override get(int index)752 public ProtoId get(int index) { 753 checkBounds(index, tableOfContents.protoIds.size); 754 return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index)) 755 .readProtoId(); 756 } 757 @Override size()758 public int size() { 759 return tableOfContents.protoIds.size; 760 } 761 } 762 763 private final class FieldIdTable extends AbstractList<FieldId> implements RandomAccess { 764 @Override get(int index)765 public FieldId get(int index) { 766 checkBounds(index, tableOfContents.fieldIds.size); 767 return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index)) 768 .readFieldId(); 769 } 770 @Override size()771 public int size() { 772 return tableOfContents.fieldIds.size; 773 } 774 } 775 776 private final class MethodIdTable extends AbstractList<MethodId> implements RandomAccess { 777 @Override get(int index)778 public MethodId get(int index) { 779 checkBounds(index, tableOfContents.methodIds.size); 780 return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index)) 781 .readMethodId(); 782 } 783 @Override size()784 public int size() { 785 return tableOfContents.methodIds.size; 786 } 787 } 788 789 private final class ClassDefIterator implements Iterator<ClassDef> { 790 private final Dex.Section in = open(tableOfContents.classDefs.off); 791 private int count = 0; 792 793 @Override hasNext()794 public boolean hasNext() { 795 return count < tableOfContents.classDefs.size; 796 } 797 @Override next()798 public ClassDef next() { 799 if (!hasNext()) { 800 throw new NoSuchElementException(); 801 } 802 count++; 803 return in.readClassDef(); 804 } 805 @Override remove()806 public void remove() { 807 throw new UnsupportedOperationException(); 808 } 809 } 810 811 private final class ClassDefIterable implements Iterable<ClassDef> { 812 @Override iterator()813 public Iterator<ClassDef> iterator() { 814 return !tableOfContents.classDefs.exists() 815 ? Collections.<ClassDef>emptySet().iterator() 816 : new ClassDefIterator(); 817 } 818 } 819 } 820