DexBuffer.java revision d43341a24abe339d474b0b0d92669917ae2eb9bf
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 17package com.android.dx.io; 18 19import com.android.dx.dex.DexFormat; 20import com.android.dx.dex.SizeOf; 21import com.android.dx.dex.TableOfContents; 22import com.android.dx.io.Code.CatchHandler; 23import com.android.dx.io.Code.Try; 24import com.android.dx.merge.TypeList; 25import com.android.dx.util.ByteInput; 26import com.android.dx.util.ByteOutput; 27import com.android.dx.util.DexException; 28import com.android.dx.util.FileUtils; 29import com.android.dx.util.Leb128Utils; 30import com.android.dx.util.Mutf8; 31import java.io.ByteArrayOutputStream; 32import java.io.File; 33import java.io.FileInputStream; 34import java.io.FileOutputStream; 35import java.io.IOException; 36import java.io.InputStream; 37import java.io.OutputStream; 38import java.io.UTFDataFormatException; 39import java.util.AbstractList; 40import java.util.Arrays; 41import java.util.Collections; 42import java.util.Iterator; 43import java.util.List; 44import java.util.NoSuchElementException; 45import java.util.zip.ZipEntry; 46import java.util.zip.ZipFile; 47 48/** 49 * The bytes of a dex file in memory for reading and writing. All int offsets 50 * are unsigned. 51 */ 52public final class DexBuffer { 53 private byte[] data; 54 private final TableOfContents tableOfContents = new TableOfContents(); 55 private int length = 0; 56 57 private final List<String> strings = new AbstractList<String>() { 58 @Override public String get(int index) { 59 checkBounds(index, tableOfContents.stringIds.size); 60 return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM)) 61 .readString(); 62 } 63 @Override public int size() { 64 return tableOfContents.stringIds.size; 65 } 66 }; 67 68 private final List<Integer> typeIds = new AbstractList<Integer>() { 69 @Override public Integer get(int index) { 70 checkBounds(index, tableOfContents.typeIds.size); 71 return open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt(); 72 } 73 @Override public int size() { 74 return tableOfContents.typeIds.size; 75 } 76 }; 77 78 private final List<String> typeNames = new AbstractList<String>() { 79 @Override public String get(int index) { 80 checkBounds(index, tableOfContents.typeIds.size); 81 return strings.get(typeIds.get(index)); 82 } 83 @Override public int size() { 84 return tableOfContents.typeIds.size; 85 } 86 }; 87 88 private final List<ProtoId> protoIds = new AbstractList<ProtoId>() { 89 @Override public ProtoId get(int index) { 90 checkBounds(index, tableOfContents.protoIds.size); 91 return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index)) 92 .readProtoId(); 93 } 94 @Override public int size() { 95 return tableOfContents.protoIds.size; 96 } 97 }; 98 99 private final List<FieldId> fieldIds = new AbstractList<FieldId>() { 100 @Override public FieldId get(int index) { 101 checkBounds(index, tableOfContents.fieldIds.size); 102 return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index)) 103 .readFieldId(); 104 } 105 @Override public int size() { 106 return tableOfContents.fieldIds.size; 107 } 108 }; 109 110 private final List<MethodId> methodIds = new AbstractList<MethodId>() { 111 @Override public MethodId get(int index) { 112 checkBounds(index, tableOfContents.methodIds.size); 113 return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index)) 114 .readMethodId(); 115 } 116 @Override public int size() { 117 return tableOfContents.methodIds.size; 118 } 119 }; 120 121 /** 122 * Creates a new dex buffer defining no classes. 123 */ 124 public DexBuffer() { 125 this.data = new byte[0]; 126 } 127 128 /** 129 * Creates a new dex buffer that reads from {@code data}. It is an error to 130 * modify {@code data} after using it to create a dex buffer. 131 */ 132 public DexBuffer(byte[] data) throws IOException { 133 this.data = data; 134 this.length = data.length; 135 this.tableOfContents.readFrom(this); 136 } 137 138 /** 139 * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}. 140 */ 141 public DexBuffer(InputStream in) throws IOException { 142 loadFrom(in); 143 } 144 145 /** 146 * Creates a new dex buffer from the dex file {@code file}. 147 */ 148 public DexBuffer(File file) throws IOException { 149 if (FileUtils.hasArchiveSuffix(file.getName())) { 150 ZipFile zipFile = new ZipFile(file); 151 ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME); 152 if (entry != null) { 153 loadFrom(zipFile.getInputStream(entry)); 154 zipFile.close(); 155 } else { 156 throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file); 157 } 158 } else if (file.getName().endsWith(".dex")) { 159 loadFrom(new FileInputStream(file)); 160 } else { 161 throw new DexException("unknown output extension: " + file); 162 } 163 } 164 165 private void loadFrom(InputStream in) throws IOException { 166 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 167 byte[] buffer = new byte[8192]; 168 169 int count; 170 while ((count = in.read(buffer)) != -1) { 171 bytesOut.write(buffer, 0, count); 172 } 173 in.close(); 174 175 this.data = bytesOut.toByteArray(); 176 this.length = data.length; 177 this.tableOfContents.readFrom(this); 178 } 179 180 private static void checkBounds(int index, int length) { 181 if (index < 0 || index >= length) { 182 throw new IndexOutOfBoundsException("index:" + index + ", length=" + length); 183 } 184 } 185 186 public void writeTo(OutputStream out) throws IOException { 187 out.write(data); 188 } 189 190 public void writeTo(File dexOut) throws IOException { 191 OutputStream out = new FileOutputStream(dexOut); 192 writeTo(out); 193 out.close(); 194 } 195 196 public TableOfContents getTableOfContents() { 197 return tableOfContents; 198 } 199 200 public Section open(int position) { 201 if (position < 0 || position > length) { 202 throw new IllegalArgumentException("position=" + position + " length=" + length); 203 } 204 return new Section(position); 205 } 206 207 public Section appendSection(int maxByteCount, String name) { 208 int limit = fourByteAlign(length + maxByteCount); 209 Section result = new Section(name, length, limit); 210 length = limit; 211 return result; 212 } 213 214 public void noMoreSections() { 215 data = new byte[length]; 216 } 217 218 public int getLength() { 219 return length; 220 } 221 222 public static int fourByteAlign(int position) { 223 return (position + 3) & ~3; 224 } 225 226 public byte[] getBytes() { 227 return data; 228 } 229 230 public List<String> strings() { 231 return strings; 232 } 233 234 public List<Integer> typeIds() { 235 return typeIds; 236 } 237 238 public List<String> typeNames() { 239 return typeNames; 240 } 241 242 public List<ProtoId> protoIds() { 243 return protoIds; 244 } 245 246 public List<FieldId> fieldIds() { 247 return fieldIds; 248 } 249 250 public List<MethodId> methodIds() { 251 return methodIds; 252 } 253 254 public Iterable<ClassDef> classDefs() { 255 return new Iterable<ClassDef>() { 256 public Iterator<ClassDef> iterator() { 257 if (!tableOfContents.classDefs.exists()) { 258 return Collections.<ClassDef>emptySet().iterator(); 259 } 260 return new Iterator<ClassDef>() { 261 private DexBuffer.Section in = open(tableOfContents.classDefs.off); 262 private int count = 0; 263 264 public boolean hasNext() { 265 return count < tableOfContents.classDefs.size; 266 } 267 public ClassDef next() { 268 if (!hasNext()) { 269 throw new NoSuchElementException(); 270 } 271 count++; 272 return in.readClassDef(); 273 } 274 public void remove() { 275 throw new UnsupportedOperationException(); 276 } 277 }; 278 } 279 }; 280 } 281 282 public TypeList readTypeList(int offset) { 283 if (offset == 0) { 284 return TypeList.EMPTY; 285 } 286 return open(offset).readTypeList(); 287 } 288 289 public ClassData readClassData(ClassDef classDef) { 290 int offset = classDef.getClassDataOffset(); 291 if (offset == 0) { 292 throw new IllegalArgumentException("offset == 0"); 293 } 294 return open(offset).readClassData(); 295 } 296 297 public Code readCode(ClassData.Method method) { 298 int offset = method.getCodeOffset(); 299 if (offset == 0) { 300 throw new IllegalArgumentException("offset == 0"); 301 } 302 return open(offset).readCode(); 303 } 304 305 public final class Section implements ByteInput, ByteOutput { 306 private final String name; 307 private int position; 308 private final int limit; 309 private final int initialPosition; 310 311 private Section(String name, int position, int limit) { 312 this.name = name; 313 this.position = this.initialPosition = position; 314 this.limit = limit; 315 } 316 317 private Section(int position) { 318 this("section", position, data.length); 319 } 320 321 public int getPosition() { 322 return position; 323 } 324 325 public int readInt() { 326 int result = (data[position] & 0xff) 327 | (data[position + 1] & 0xff) << 8 328 | (data[position + 2] & 0xff) << 16 329 | (data[position + 3] & 0xff) << 24; 330 position += 4; 331 return result; 332 } 333 334 public short readShort() { 335 int result = (data[position] & 0xff) 336 | (data[position + 1] & 0xff) << 8; 337 position += 2; 338 return (short) result; 339 } 340 341 public int readUnsignedShort() { 342 return readShort() & 0xffff; 343 } 344 345 public byte readByte() { 346 return (byte) (data[position++] & 0xff); 347 } 348 349 public byte[] readByteArray(int length) { 350 byte[] result = Arrays.copyOfRange(data, position, position + length); 351 position += length; 352 return result; 353 } 354 355 public short[] readShortArray(int length) { 356 short[] result = new short[length]; 357 for (int i = 0; i < length; i++) { 358 result[i] = readShort(); 359 } 360 return result; 361 } 362 363 public int readUleb128() { 364 return Leb128Utils.readUnsignedLeb128(this); 365 } 366 367 public int readUleb128p1() { 368 return Leb128Utils.readUnsignedLeb128(this) - 1; 369 } 370 371 public int readSleb128() { 372 return Leb128Utils.readSignedLeb128(this); 373 } 374 375 public TypeList readTypeList() { 376 int size = readInt(); 377 short[] types = new short[size]; 378 for (int i = 0; i < size; i++) { 379 types[i] = readShort(); 380 } 381 alignToFourBytes(); 382 return new TypeList(DexBuffer.this, types); 383 } 384 385 public String readString() { 386 int offset = readInt(); 387 int savedPosition = position; 388 position = offset; 389 try { 390 int expectedLength = readUleb128(); 391 String result = Mutf8.decode(this, new char[expectedLength]); 392 if (result.length() != expectedLength) { 393 throw new DexException("Declared length " + expectedLength 394 + " doesn't match decoded length of " + result.length()); 395 } 396 return result; 397 } catch (UTFDataFormatException e) { 398 throw new DexException(e); 399 } finally { 400 position = savedPosition; 401 } 402 } 403 404 public FieldId readFieldId() { 405 int declaringClassIndex = readUnsignedShort(); 406 int typeIndex = readUnsignedShort(); 407 int nameIndex = readInt(); 408 return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex); 409 } 410 411 public MethodId readMethodId() { 412 int declaringClassIndex = readUnsignedShort(); 413 int protoIndex = readUnsignedShort(); 414 int nameIndex = readInt(); 415 return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex); 416 } 417 418 public ProtoId readProtoId() { 419 int shortyIndex = readInt(); 420 int returnTypeIndex = readInt(); 421 int parametersOffset = readInt(); 422 return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parametersOffset); 423 } 424 425 public ClassDef readClassDef() { 426 int offset = getPosition(); 427 int type = readInt(); 428 int accessFlags = readInt(); 429 int supertype = readInt(); 430 int interfacesOffset = readInt(); 431 int sourceFileIndex = readInt(); 432 int annotationsOffset = readInt(); 433 int classDataOffset = readInt(); 434 int staticValuesOffset = readInt(); 435 return new ClassDef(DexBuffer.this, offset, type, accessFlags, supertype, 436 interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset, 437 staticValuesOffset); 438 } 439 440 private Code readCode() { 441 int registersSize = readUnsignedShort(); 442 int insSize = readUnsignedShort(); 443 int outsSize = readUnsignedShort(); 444 int triesSize = readUnsignedShort(); 445 int debugInfoOffset = readInt(); 446 int instructionsSize = readInt(); 447 short[] instructions = readShortArray(instructionsSize); 448 Try[] tries; 449 CatchHandler[] catchHandlers; 450 if (triesSize > 0) { 451 if (instructions.length % 2 == 1) { 452 readShort(); // padding 453 } 454 455 /* 456 * We can't read the tries until we've read the catch handlers. 457 * Unfortunately they're in the opposite order in the dex file 458 * so we need to read them out-of-order. 459 */ 460 Section triesSection = open(position); 461 skip(triesSize * SizeOf.TRY_ITEM); 462 catchHandlers = readCatchHandlers(); 463 tries = triesSection.readTries(triesSize, catchHandlers); 464 } else { 465 tries = new Try[0]; 466 catchHandlers = new CatchHandler[0]; 467 } 468 return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions, 469 tries, catchHandlers); 470 } 471 472 private CatchHandler[] readCatchHandlers() { 473 int baseOffset = position; 474 int catchHandlersSize = readUleb128(); 475 CatchHandler[] result = new CatchHandler[catchHandlersSize]; 476 for (int i = 0; i < catchHandlersSize; i++) { 477 int offset = position - baseOffset; 478 result[i] = readCatchHandler(offset); 479 } 480 return result; 481 } 482 483 private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) { 484 Try[] result = new Try[triesSize]; 485 for (int i = 0; i < triesSize; i++) { 486 int startAddress = readInt(); 487 int instructionCount = readUnsignedShort(); 488 int handlerOffset = readUnsignedShort(); 489 int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset); 490 result[i] = new Try(startAddress, instructionCount, catchHandlerIndex); 491 } 492 return result; 493 } 494 495 private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) { 496 for (int i = 0; i < catchHandlers.length; i++) { 497 CatchHandler catchHandler = catchHandlers[i]; 498 if (catchHandler.getOffset() == offset) { 499 return i; 500 } 501 } 502 throw new IllegalArgumentException(); 503 } 504 505 private CatchHandler readCatchHandler(int offset) { 506 int size = readSleb128(); 507 int handlersCount = Math.abs(size); 508 int[] typeIndexes = new int[handlersCount]; 509 int[] addresses = new int[handlersCount]; 510 for (int i = 0; i < handlersCount; i++) { 511 typeIndexes[i] = readUleb128(); 512 addresses[i] = readUleb128(); 513 } 514 int catchAllAddress = size <= 0 ? readUleb128() : -1; 515 return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset); 516 } 517 518 private ClassData readClassData() { 519 int staticFieldsSize = readUleb128(); 520 int instanceFieldsSize = readUleb128(); 521 int directMethodsSize = readUleb128(); 522 int virtualMethodsSize = readUleb128(); 523 ClassData.Field[] staticFields = readFields(staticFieldsSize); 524 ClassData.Field[] instanceFields = readFields(instanceFieldsSize); 525 ClassData.Method[] directMethods = readMethods(directMethodsSize); 526 ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize); 527 return new ClassData(staticFields, instanceFields, directMethods, virtualMethods); 528 } 529 530 private ClassData.Field[] readFields(int count) { 531 ClassData.Field[] result = new ClassData.Field[count]; 532 int fieldIndex = 0; 533 for (int i = 0; i < count; i++) { 534 fieldIndex += readUleb128(); // field index diff 535 int accessFlags = readUleb128(); 536 result[i] = new ClassData.Field(fieldIndex, accessFlags); 537 } 538 return result; 539 } 540 541 private ClassData.Method[] readMethods(int count) { 542 ClassData.Method[] result = new ClassData.Method[count]; 543 int methodIndex = 0; 544 for (int i = 0; i < count; i++) { 545 methodIndex += readUleb128(); // method index diff 546 int accessFlags = readUleb128(); 547 int codeOff = readUleb128(); 548 result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff); 549 } 550 return result; 551 } 552 553 public Annotation readAnnotation() { 554 byte visibility = readByte(); 555 int typeIndex = readUleb128(); 556 int size = readUleb128(); 557 int[] names = new int[size]; 558 EncodedValue[] values = new EncodedValue[size]; 559 for (int i = 0; i < size; i++) { 560 names[i] = readUleb128(); 561 values[i] = readEncodedValue(); 562 } 563 return new Annotation(DexBuffer.this, visibility, typeIndex, names, values); 564 } 565 566 public EncodedValue readEncodedValue() { 567 int start = position; 568 new EncodedValueReader(this).readValue(); 569 int end = position; 570 return new EncodedValue(Arrays.copyOfRange(data, start, end)); 571 } 572 573 public EncodedValue readEncodedArray() { 574 int start = position; 575 new EncodedValueReader(this).readArray(); 576 int end = position; 577 return new EncodedValue(Arrays.copyOfRange(data, start, end)); 578 } 579 580 private void ensureCapacity(int size) { 581 if (position + size > limit) { 582 throw new DexException("Section limit " + limit + " exceeded by " + name); 583 } 584 } 585 586 public void skip(int count) { 587 if (count < 0) { 588 throw new IllegalArgumentException(); 589 } 590 ensureCapacity(count); 591 position += count; 592 } 593 594 /** 595 * Writes 0x00 until the position is aligned to a multiple of 4. 596 */ 597 public void alignToFourBytes() { 598 int unalignedCount = position; 599 position = DexBuffer.fourByteAlign(position); 600 for (int i = unalignedCount; i < position; i++) { 601 data[i] = 0; 602 } 603 } 604 605 public void assertFourByteAligned() { 606 if ((position & 3) != 0) { 607 throw new IllegalStateException("Not four byte aligned!"); 608 } 609 } 610 611 public void write(byte[] bytes) { 612 ensureCapacity(bytes.length); 613 System.arraycopy(bytes, 0, data, position, bytes.length); 614 position += bytes.length; 615 } 616 617 public void writeByte(int b) { 618 ensureCapacity(1); 619 data[position++] = (byte) b; 620 } 621 622 public void writeShort(short i) { 623 ensureCapacity(2); 624 data[position ] = (byte) i; 625 data[position + 1] = (byte) (i >>> 8); 626 position += 2; 627 } 628 629 public void writeUnsignedShort(int i) { 630 short s = (short) i; 631 if (i != (s & 0xffff)) { 632 throw new IllegalArgumentException("Expected an unsigned short: " + i); 633 } 634 writeShort(s); 635 } 636 637 public void write(short[] shorts) { 638 for (short s : shorts) { 639 writeShort(s); 640 } 641 } 642 643 public void writeInt(int i) { 644 ensureCapacity(4); 645 data[position ] = (byte) i; 646 data[position + 1] = (byte) (i >>> 8); 647 data[position + 2] = (byte) (i >>> 16); 648 data[position + 3] = (byte) (i >>> 24); 649 position += 4; 650 } 651 652 public void writeUleb128(int i) { 653 try { 654 Leb128Utils.writeUnsignedLeb128(this, i); 655 ensureCapacity(0); 656 } catch (ArrayIndexOutOfBoundsException e) { 657 throw new DexException("Section limit " + limit + " exceeded by " + name); 658 } 659 } 660 661 public void writeUleb128p1(int i) { 662 writeUleb128(i + 1); 663 } 664 665 public void writeSleb128(int i) { 666 try { 667 Leb128Utils.writeSignedLeb128(this, i); 668 ensureCapacity(0); 669 } catch (ArrayIndexOutOfBoundsException e) { 670 throw new DexException("Section limit " + limit + " exceeded by " + name); 671 } 672 } 673 674 public void writeStringData(String value) { 675 try { 676 int length = value.length(); 677 writeUleb128(length); 678 write(Mutf8.encode(value)); 679 writeByte(0); 680 } catch (UTFDataFormatException e) { 681 throw new AssertionError(); 682 } 683 } 684 685 public void writeTypeList(TypeList typeList) { 686 short[] types = typeList.getTypes(); 687 writeInt(types.length); 688 for (short type : types) { 689 writeShort(type); 690 } 691 alignToFourBytes(); 692 } 693 694 /** 695 * Returns the number of bytes remaining in this section. 696 */ 697 public int remaining() { 698 return limit - position; 699 } 700 701 /** 702 * Returns the number of bytes used by this section. 703 */ 704 public int used () { 705 return position - initialPosition; 706 } 707 } 708} 709