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