DexBuffer.java revision fa3b293a068fd521a6ba6019a051ad502dfaca55
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 public 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 308 private Section(String name, int position, int limit) { 309 this.name = name; 310 this.position = position; 311 this.limit = limit; 312 } 313 314 private Section(int position) { 315 this("section", position, data.length); 316 } 317 318 public int getPosition() { 319 return position; 320 } 321 322 public int readInt() { 323 int result = (data[position] & 0xff) 324 | (data[position + 1] & 0xff) << 8 325 | (data[position + 2] & 0xff) << 16 326 | (data[position + 3] & 0xff) << 24; 327 position += 4; 328 return result; 329 } 330 331 public short readShort() { 332 int result = (data[position] & 0xff) 333 | (data[position + 1] & 0xff) << 8; 334 position += 2; 335 return (short) result; 336 } 337 338 public int readUnsignedShort() { 339 return readShort() & 0xffff; 340 } 341 342 public byte readByte() { 343 return (byte) (data[position++] & 0xff); 344 } 345 346 public byte[] readByteArray(int length) { 347 byte[] result = Arrays.copyOfRange(data, position, position + length); 348 position += length; 349 return result; 350 } 351 352 public short[] readShortArray(int length) { 353 short[] result = new short[length]; 354 for (int i = 0; i < length; i++) { 355 result[i] = readShort(); 356 } 357 return result; 358 } 359 360 public int readUleb128() { 361 return Leb128Utils.readUnsignedLeb128(this); 362 } 363 364 public int readSleb128() { 365 return Leb128Utils.readSignedLeb128(this); 366 } 367 368 public TypeList readTypeList() { 369 int size = readInt(); 370 short[] types = new short[size]; 371 for (int i = 0; i < size; i++) { 372 types[i] = readShort(); 373 } 374 alignToFourBytes(); 375 return new TypeList(DexBuffer.this, types); 376 } 377 378 public String readString() { 379 int offset = readInt(); 380 int savedPosition = position; 381 position = offset; 382 try { 383 int expectedLength = readUleb128(); 384 String result = Mutf8.decode(this, new char[expectedLength]); 385 if (result.length() != expectedLength) { 386 throw new DexException("Declared length " + expectedLength 387 + " doesn't match decoded length of " + result.length()); 388 } 389 return result; 390 } catch (UTFDataFormatException e) { 391 throw new DexException(e); 392 } finally { 393 position = savedPosition; 394 } 395 } 396 397 public FieldId readFieldId() { 398 int declaringClassIndex = readUnsignedShort(); 399 int typeIndex = readUnsignedShort(); 400 int nameIndex = readInt(); 401 return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex); 402 } 403 404 public MethodId readMethodId() { 405 int declaringClassIndex = readUnsignedShort(); 406 int protoIndex = readUnsignedShort(); 407 int nameIndex = readInt(); 408 return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex); 409 } 410 411 public ProtoId readProtoId() { 412 int shortyIndex = readInt(); 413 int returnTypeIndex = readInt(); 414 int parametersOffset = readInt(); 415 return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parametersOffset); 416 } 417 418 public ClassDef readClassDef() { 419 int offset = getPosition(); 420 int type = readInt(); 421 int accessFlags = readInt(); 422 int supertype = readInt(); 423 int interfacesOffset = readInt(); 424 int sourceFileIndex = readInt(); 425 int annotationsOffset = readInt(); 426 int classDataOffset = readInt(); 427 int staticValuesOffset = readInt(); 428 return new ClassDef(DexBuffer.this, offset, type, accessFlags, supertype, 429 interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset, 430 staticValuesOffset); 431 } 432 433 private Code readCode() { 434 int registersSize = readUnsignedShort(); 435 int insSize = readUnsignedShort(); 436 int outsSize = readUnsignedShort(); 437 int triesSize = readUnsignedShort(); 438 int debugInfoOffset = readInt(); 439 int instructionsSize = readInt(); 440 short[] instructions = readShortArray(instructionsSize); 441 Code.Try[] tries = new Code.Try[triesSize]; 442 Code.CatchHandler[] catchHandlers = new Code.CatchHandler[0]; 443 if (triesSize > 0) { 444 if (instructions.length % 2 == 1) { 445 readShort(); // padding 446 } 447 448 for (int i = 0; i < triesSize; i++) { 449 int startAddress = readInt(); 450 int instructionCount = readUnsignedShort(); 451 int handlerOffset = readUnsignedShort(); 452 tries[i] = new Code.Try(startAddress, instructionCount, handlerOffset); 453 } 454 455 int catchHandlersSize = readUleb128(); 456 catchHandlers = new Code.CatchHandler[catchHandlersSize]; 457 for (int i = 0; i < catchHandlersSize; i++) { 458 catchHandlers[i] = readCatchHandler(); 459 } 460 } 461 return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions, 462 tries, catchHandlers); 463 } 464 465 private Code.CatchHandler readCatchHandler() { 466 int size = readSleb128(); 467 int handlersCount = Math.abs(size); 468 int[] typeIndexes = new int[handlersCount]; 469 int[] addresses = new int[handlersCount]; 470 for (int i = 0; i < handlersCount; i++) { 471 typeIndexes[i] = readUleb128(); 472 addresses[i] = readUleb128(); 473 } 474 int catchAllAddress = size <= 0 ? readUleb128() : -1; 475 return new Code.CatchHandler(typeIndexes, addresses, catchAllAddress); 476 } 477 478 private ClassData readClassData() { 479 int staticFieldsSize = readUleb128(); 480 int instanceFieldsSize = readUleb128(); 481 int directMethodsSize = readUleb128(); 482 int virtualMethodsSize = readUleb128(); 483 ClassData.Field[] staticFields = readFields(staticFieldsSize); 484 ClassData.Field[] instanceFields = readFields(instanceFieldsSize); 485 ClassData.Method[] directMethods = readMethods(directMethodsSize); 486 ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize); 487 return new ClassData(staticFields, instanceFields, directMethods, virtualMethods); 488 } 489 490 private ClassData.Field[] readFields(int count) { 491 ClassData.Field[] result = new ClassData.Field[count]; 492 int fieldIndex = 0; 493 for (int i = 0; i < count; i++) { 494 fieldIndex += readUleb128(); // field index diff 495 int accessFlags = readUleb128(); 496 result[i] = new ClassData.Field(fieldIndex, accessFlags); 497 } 498 return result; 499 } 500 501 private ClassData.Method[] readMethods(int count) { 502 ClassData.Method[] result = new ClassData.Method[count]; 503 int methodIndex = 0; 504 for (int i = 0; i < count; i++) { 505 methodIndex += readUleb128(); // method index diff 506 int accessFlags = readUleb128(); 507 int codeOff = readUleb128(); 508 result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff); 509 } 510 return result; 511 } 512 513 public Annotation readAnnotation() { 514 byte visibility = readByte(); 515 int typeIndex = readUleb128(); 516 int size = readUleb128(); 517 int[] names = new int[size]; 518 EncodedValue[] values = new EncodedValue[size]; 519 for (int i = 0; i < size; i++) { 520 names[i] = readUleb128(); 521 values[i] = readEncodedValue(); 522 } 523 return new Annotation(DexBuffer.this, visibility, typeIndex, names, values); 524 } 525 526 public EncodedValue readEncodedValue() { 527 int start = position; 528 new EncodedValueReader(this).readValue(); 529 int end = position; 530 return new EncodedValue(Arrays.copyOfRange(data, start, end)); 531 } 532 533 public EncodedValue readEncodedArray() { 534 int start = position; 535 new EncodedValueReader(this).readArray(); 536 int end = position; 537 return new EncodedValue(Arrays.copyOfRange(data, start, end)); 538 } 539 540 private void ensureCapacity(int size) { 541 if (position + size > limit) { 542 throw new DexException("Section limit " + limit + " exceeded by " + name); 543 } 544 } 545 546 /** 547 * Writes 0x00 until the position is aligned to a multiple of 4. 548 */ 549 public void alignToFourBytes() { 550 int unalignedCount = position; 551 position = DexBuffer.fourByteAlign(position); 552 for (int i = unalignedCount; i < position; i++) { 553 data[i] = 0; 554 } 555 } 556 557 public void assertFourByteAligned() { 558 if ((position & 3) != 0) { 559 throw new IllegalStateException("Not four byte aligned!"); 560 } 561 } 562 563 public void write(byte[] bytes) { 564 ensureCapacity(bytes.length); 565 System.arraycopy(bytes, 0, data, position, bytes.length); 566 position += bytes.length; 567 } 568 569 public void writeByte(int b) { 570 ensureCapacity(1); 571 data[position++] = (byte) b; 572 } 573 574 public void writeShort(short i) { 575 ensureCapacity(2); 576 data[position ] = (byte) i; 577 data[position + 1] = (byte) (i >>> 8); 578 position += 2; 579 } 580 581 public void writeUnsignedShort(int i) { 582 short s = (short) i; 583 if (i != (s & 0xffff)) { 584 throw new IllegalArgumentException("Expected an unsigned short: " + i); 585 } 586 writeShort(s); 587 } 588 589 public void write(short[] shorts) { 590 for (short s : shorts) { 591 writeShort(s); 592 } 593 } 594 595 public void writeInt(int i) { 596 ensureCapacity(4); 597 data[position ] = (byte) i; 598 data[position + 1] = (byte) (i >>> 8); 599 data[position + 2] = (byte) (i >>> 16); 600 data[position + 3] = (byte) (i >>> 24); 601 position += 4; 602 } 603 604 public void writeUleb128(int i) { 605 try { 606 Leb128Utils.writeUnsignedLeb128(this, i); 607 ensureCapacity(0); 608 } catch (ArrayIndexOutOfBoundsException e) { 609 throw new DexException("Section limit " + limit + " exceeded by " + name); 610 } 611 } 612 613 public void writeSleb128(int i) { 614 try { 615 Leb128Utils.writeSignedLeb128(this, i); 616 ensureCapacity(0); 617 } catch (ArrayIndexOutOfBoundsException e) { 618 throw new DexException("Section limit " + limit + " exceeded by " + name); 619 } 620 } 621 622 public void writeStringData(String value) { 623 try { 624 int length = value.length(); 625 writeUleb128(length); 626 write(Mutf8.encode(value)); 627 writeByte(0); 628 } catch (UTFDataFormatException e) { 629 throw new AssertionError(); 630 } 631 } 632 633 public void writeTypeList(TypeList typeList) { 634 short[] types = typeList.getTypes(); 635 writeInt(types.length); 636 for (short type : types) { 637 writeShort(type); 638 } 639 alignToFourBytes(); 640 } 641 642 /** 643 * Returns the number of bytes remaining in this section. 644 */ 645 public int remaining() { 646 return limit - position; 647 } 648 } 649} 650