1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2010 Ben Gruver (JesusFreke) 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29package org.jf.dexlib; 30 31import com.google.common.base.Preconditions; 32import org.jf.dexlib.Util.*; 33 34import javax.annotation.Nonnull; 35import javax.annotation.Nullable; 36import java.util.*; 37 38public class ClassDataItem extends Item<ClassDataItem> { 39 @Nullable 40 private EncodedField[] staticFields = null; 41 @Nullable 42 private EncodedField[] instanceFields = null; 43 @Nullable 44 private EncodedMethod[] directMethods = null; 45 @Nullable 46 private EncodedMethod[] virtualMethods = null; 47 48 /** 49 * Creates a new uninitialized <code>ClassDataItem</code> 50 * @param dexFile The <code>DexFile</code> that this item belongs to 51 */ 52 public ClassDataItem(final DexFile dexFile) { 53 super(dexFile); 54 } 55 56 /** 57 * Creates a new <code>ClassDataItem</code> with the given values 58 * @param dexFile The <code>DexFile</code> that this item belongs to 59 * @param staticFields The static fields for this class 60 * @param instanceFields The instance fields for this class 61 * @param directMethods The direct methods for this class 62 * @param virtualMethods The virtual methods for this class 63 */ 64 private ClassDataItem(DexFile dexFile, @Nullable EncodedField[] staticFields, 65 @Nullable EncodedField[] instanceFields, @Nullable EncodedMethod[] directMethods, 66 @Nullable EncodedMethod[] virtualMethods) { 67 super(dexFile); 68 this.staticFields = staticFields; 69 this.instanceFields = instanceFields; 70 this.directMethods = directMethods; 71 this.virtualMethods = virtualMethods; 72 } 73 74 /** 75 * Creates a new <code>ClassDataItem</code> with the given values 76 * @param dexFile The <code>DexFile</code> that this item belongs to 77 * @param staticFields The static fields for this class 78 * @param instanceFields The instance fields for this class 79 * @param directMethods The direct methods for this class 80 * @param virtualMethods The virtual methods for this class 81 * @return a new <code>ClassDataItem</code> with the given values 82 */ 83 public static ClassDataItem internClassDataItem(DexFile dexFile, @Nullable List<EncodedField> staticFields, 84 @Nullable List<EncodedField> instanceFields, 85 @Nullable List<EncodedMethod> directMethods, 86 @Nullable List<EncodedMethod> virtualMethods) { 87 EncodedField[] staticFieldsArray = null; 88 EncodedField[] instanceFieldsArray = null; 89 EncodedMethod[] directMethodsArray = null; 90 EncodedMethod[] virtualMethodsArray = null; 91 92 if (staticFields != null && staticFields.size() > 0) { 93 SortedSet<EncodedField> staticFieldsSet = new TreeSet<EncodedField>(); 94 for (EncodedField staticField: staticFields) { 95 if (staticFieldsSet.contains(staticField)) { 96 System.err.println(String.format("Ignoring duplicate static field definition: %s", 97 staticField.field.getFieldString())); 98 continue; 99 } 100 staticFieldsSet.add(staticField); 101 } 102 103 staticFieldsArray = new EncodedField[staticFieldsSet.size()]; 104 staticFieldsArray = staticFieldsSet.toArray(staticFieldsArray); 105 } 106 107 if (instanceFields != null && instanceFields.size() > 0) { 108 SortedSet<EncodedField> instanceFieldsSet = new TreeSet<EncodedField>(); 109 for (EncodedField instanceField: instanceFields) { 110 if (instanceFieldsSet.contains(instanceField)) { 111 System.err.println(String.format("Ignoring duplicate instance field definition: %s", 112 instanceField.field.getFieldString())); 113 continue; 114 } 115 instanceFieldsSet.add(instanceField); 116 } 117 118 instanceFieldsArray = new EncodedField[instanceFieldsSet.size()]; 119 instanceFieldsArray = instanceFieldsSet.toArray(instanceFieldsArray); 120 } 121 122 TreeSet<EncodedMethod> directMethodSet = new TreeSet<EncodedMethod>(); 123 124 if (directMethods != null && directMethods.size() > 0) { 125 for (EncodedMethod directMethod: directMethods) { 126 if (directMethodSet.contains(directMethod)) { 127 System.err.println(String.format("Ignoring duplicate direct method definition: %s", 128 directMethod.method.getMethodString())); 129 continue; 130 } 131 directMethodSet.add(directMethod); 132 } 133 134 directMethodsArray = new EncodedMethod[directMethodSet.size()]; 135 directMethodsArray = directMethodSet.toArray(directMethodsArray); 136 } 137 138 if (virtualMethods != null && virtualMethods.size() > 0) { 139 TreeSet<EncodedMethod> virtualMethodSet = new TreeSet<EncodedMethod>(); 140 for (EncodedMethod virtualMethod: virtualMethods) { 141 if (directMethodSet.contains(virtualMethod)) { 142 // If both a direct and virtual definition is present, dalvik's behavior seems to be undefined, 143 // so we can't gracefully handle this case, like we can if the duplicates are all direct or all 144 // virtual -- in which case, we ignore all but the first definition 145 throw new RuntimeException(String.format("Duplicate direct+virtual method definition: %s", 146 virtualMethod.method.getMethodString())); 147 } 148 if (virtualMethodSet.contains(virtualMethod)) { 149 System.err.println(String.format("Ignoring duplicate virtual method definition: %s", 150 virtualMethod.method.getMethodString())); 151 continue; 152 } 153 virtualMethodSet.add(virtualMethod); 154 } 155 156 virtualMethodsArray = new EncodedMethod[virtualMethodSet.size()]; 157 virtualMethodsArray = virtualMethodSet.toArray(virtualMethodsArray); 158 } 159 160 ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFieldsArray, instanceFieldsArray, 161 directMethodsArray, virtualMethodsArray); 162 return dexFile.ClassDataSection.intern(classDataItem); 163 } 164 165 /** {@inheritDoc} */ 166 protected void readItem(Input in, ReadContext readContext) { 167 int staticFieldsCount = in.readUnsignedLeb128(); 168 int instanceFieldsCount = in.readUnsignedLeb128(); 169 int directMethodsCount = in.readUnsignedLeb128(); 170 int virtualMethodsCount = in.readUnsignedLeb128(); 171 172 if (staticFieldsCount > 0) { 173 staticFields = new EncodedField[staticFieldsCount]; 174 EncodedField previousEncodedField = null; 175 for (int i=0; i<staticFieldsCount; i++) { 176 try { 177 staticFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField); 178 } catch (Exception ex) { 179 throw ExceptionWithContext.withContext(ex, "Error while reading static field at index " + i); 180 } 181 } 182 } 183 184 if (instanceFieldsCount > 0) { 185 instanceFields = new EncodedField[instanceFieldsCount]; 186 EncodedField previousEncodedField = null; 187 for (int i=0; i<instanceFieldsCount; i++) { 188 try { 189 instanceFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField); 190 } catch (Exception ex) { 191 throw ExceptionWithContext.withContext(ex, "Error while reading instance field at index " + i); 192 } 193 } 194 } 195 196 if (directMethodsCount > 0) { 197 directMethods = new EncodedMethod[directMethodsCount]; 198 EncodedMethod previousEncodedMethod = null; 199 for (int i=0; i<directMethodsCount; i++) { 200 try { 201 directMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in, 202 previousEncodedMethod); 203 } catch (Exception ex) { 204 throw ExceptionWithContext.withContext(ex, "Error while reading direct method at index " + i); 205 } 206 } 207 } 208 209 if (virtualMethodsCount > 0) { 210 virtualMethods = new EncodedMethod[virtualMethodsCount]; 211 EncodedMethod previousEncodedMethod = null; 212 for (int i=0; i<virtualMethodsCount; i++) { 213 try { 214 virtualMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in, 215 previousEncodedMethod); 216 } catch (Exception ex) { 217 throw ExceptionWithContext.withContext(ex, "Error while reading virtual method at index " + i); 218 } 219 } 220 } 221 } 222 223 /** {@inheritDoc} */ 224 protected int placeItem(int offset) { 225 offset += Leb128Utils.unsignedLeb128Size(getStaticFieldCount()); 226 offset += Leb128Utils.unsignedLeb128Size(getInstanceFieldCount()); 227 offset += Leb128Utils.unsignedLeb128Size(getDirectMethodCount()); 228 offset += Leb128Utils.unsignedLeb128Size(getVirtualMethodCount()); 229 230 if (staticFields != null) { 231 EncodedField previousEncodedField = null; 232 for (EncodedField encodedField: staticFields) { 233 offset = encodedField.place(offset, previousEncodedField); 234 previousEncodedField = encodedField; 235 } 236 } 237 238 if (instanceFields != null) { 239 EncodedField previousEncodedField = null; 240 for (EncodedField encodedField: instanceFields) { 241 offset = encodedField.place(offset, previousEncodedField); 242 previousEncodedField = encodedField; 243 } 244 } 245 246 if (directMethods != null) { 247 EncodedMethod previousEncodedMethod = null; 248 for (EncodedMethod encodedMethod: directMethods) { 249 offset = encodedMethod.place(offset, previousEncodedMethod); 250 previousEncodedMethod = encodedMethod; 251 } 252 } 253 254 if (virtualMethods != null) { 255 EncodedMethod previousEncodedMethod = null; 256 for (EncodedMethod encodedMethod: virtualMethods) { 257 offset = encodedMethod.place(offset, previousEncodedMethod); 258 previousEncodedMethod = encodedMethod; 259 } 260 } 261 262 return offset; 263 } 264 265 /** {@inheritDoc} */ 266 protected void writeItem(AnnotatedOutput out) { 267 if (out.annotates()) { 268 int staticFieldCount = getStaticFieldCount(); 269 out.annotate("static_fields_size: 0x" + Integer.toHexString(staticFieldCount) + " (" + 270 staticFieldCount + ")"); 271 out.writeUnsignedLeb128(staticFieldCount); 272 273 int instanceFieldCount = getInstanceFieldCount(); 274 out.annotate("instance_fields_size: 0x" + Integer.toHexString(instanceFieldCount) + " (" + 275 instanceFieldCount + ")"); 276 out.writeUnsignedLeb128(instanceFieldCount); 277 278 int directMethodCount = getDirectMethodCount(); 279 out.annotate("direct_methods_size: 0x" + Integer.toHexString(directMethodCount) + " (" + 280 directMethodCount + ")"); 281 out.writeUnsignedLeb128(directMethodCount); 282 283 int virtualMethodCount = getVirtualMethodCount(); 284 out.annotate("virtual_methods_size: 0x" + Integer.toHexString(virtualMethodCount) + " (" + 285 virtualMethodCount + ")"); 286 out.writeUnsignedLeb128(virtualMethodCount); 287 288 289 if (staticFields != null) { 290 int index = 0; 291 EncodedField previousEncodedField = null; 292 for (EncodedField encodedField: staticFields) { 293 out.annotate("[" + index++ + "] static_field"); 294 out.indent(); 295 encodedField.writeTo(out, previousEncodedField); 296 out.deindent(); 297 previousEncodedField = encodedField; 298 } 299 } 300 301 if (instanceFields != null) { 302 int index = 0; 303 EncodedField previousEncodedField = null; 304 for (EncodedField encodedField: instanceFields) { 305 out.annotate("[" + index++ + "] instance_field"); 306 out.indent(); 307 encodedField.writeTo(out, previousEncodedField); 308 out.deindent(); 309 previousEncodedField = encodedField; 310 } 311 } 312 313 if (directMethods != null) { 314 int index = 0; 315 EncodedMethod previousEncodedMethod = null; 316 for (EncodedMethod encodedMethod: directMethods) { 317 out.annotate("[" + index++ + "] direct_method"); 318 out.indent(); 319 encodedMethod.writeTo(out, previousEncodedMethod); 320 out.deindent(); 321 previousEncodedMethod = encodedMethod; 322 } 323 } 324 325 if (virtualMethods != null) { 326 int index = 0; 327 EncodedMethod previousEncodedMethod = null; 328 for (EncodedMethod encodedMethod: virtualMethods) { 329 out.annotate("[" + index++ + "] virtual_method"); 330 out.indent(); 331 encodedMethod.writeTo(out, previousEncodedMethod); 332 out.deindent(); 333 previousEncodedMethod = encodedMethod; 334 } 335 } 336 } else { 337 out.writeUnsignedLeb128(getStaticFieldCount()); 338 out.writeUnsignedLeb128(getInstanceFieldCount()); 339 out.writeUnsignedLeb128(getDirectMethodCount()); 340 out.writeUnsignedLeb128(getVirtualMethodCount()); 341 342 if (staticFields != null) { 343 EncodedField previousEncodedField = null; 344 for (EncodedField encodedField: staticFields) { 345 encodedField.writeTo(out, previousEncodedField); 346 previousEncodedField = encodedField; 347 } 348 } 349 350 351 if (instanceFields != null) { 352 EncodedField previousEncodedField = null; 353 for (EncodedField encodedField: instanceFields) { 354 encodedField.writeTo(out, previousEncodedField); 355 previousEncodedField = encodedField; 356 } 357 } 358 359 if (directMethods != null) { 360 EncodedMethod previousEncodedMethod = null; 361 for (EncodedMethod encodedMethod: directMethods) { 362 encodedMethod.writeTo(out, previousEncodedMethod); 363 previousEncodedMethod = encodedMethod; 364 } 365 } 366 367 if (virtualMethods != null) { 368 EncodedMethod previousEncodedMethod = null; 369 for (EncodedMethod encodedMethod: virtualMethods) { 370 encodedMethod.writeTo(out, previousEncodedMethod); 371 previousEncodedMethod = encodedMethod; 372 } 373 } 374 } 375 } 376 377 /** {@inheritDoc} */ 378 public ItemType getItemType() { 379 return ItemType.TYPE_CLASS_DATA_ITEM; 380 } 381 382 /** {@inheritDoc} */ 383 public String getConciseIdentity() { 384 TypeIdItem parentType = getParentType(); 385 if (parentType == null) { 386 return "class_data_item @0x" + Integer.toHexString(getOffset()); 387 } 388 return "class_data_item @0x" + Integer.toHexString(getOffset()) + " (" + parentType.getTypeDescriptor() +")"; 389 } 390 391 /** {@inheritDoc} */ 392 public int compareTo(ClassDataItem other) { 393 Preconditions.checkNotNull(other); 394 395 // An empty CodeDataItem may be shared by multiple ClassDefItems, so we can't use parent in this case 396 if (isEmpty()) { 397 if (other.isEmpty()) { 398 return 0; 399 } 400 return -1; 401 } 402 if (other.isEmpty()) { 403 return 1; 404 } 405 406 TypeIdItem parentType = getParentType(); 407 TypeIdItem otherParentType= other.getParentType(); 408 if (parentType == null) { 409 if (otherParentType == null) { 410 return 0; 411 } 412 return -1; 413 } 414 if (otherParentType == null) { 415 return 1; 416 } 417 return parentType.compareTo(otherParentType); 418 } 419 420 @Override 421 public int hashCode() { 422 // If the item has a single parent, we can use the re-use the identity (hash) of that parent 423 TypeIdItem parentType = getParentType(); 424 if (parentType != null) { 425 return parentType.hashCode(); 426 } 427 return 0; 428 } 429 430 /** 431 * Returns the parent type for a non-empty ClassDataItem, or null for an empty one (which could be referenced by 432 * multiple ClassDefItem parents) 433 * 434 * Only an empty ClassDataItem may have multiple parents. 435 * 436 * @return The parent type for this ClassDefItem, or null if it may have multiple parents 437 */ 438 @Nullable 439 public TypeIdItem getParentType() { 440 if (staticFields != null && staticFields.length > 0) { 441 return staticFields[0].field.getContainingClass(); 442 } 443 if (instanceFields != null && instanceFields.length > 0) { 444 return instanceFields[0].field.getContainingClass(); 445 } 446 if (directMethods != null && directMethods.length > 0) { 447 return directMethods[0].method.getContainingClass(); 448 } 449 if (virtualMethods != null && virtualMethods.length > 0) { 450 return virtualMethods[0].method.getContainingClass(); 451 } 452 return null; 453 } 454 455 /** 456 * @return the static fields for this class 457 */ 458 @Nonnull 459 public List<EncodedField> getStaticFields() { 460 if (staticFields == null) { 461 return Collections.emptyList(); 462 } 463 return ReadOnlyArrayList.of(staticFields); 464 } 465 466 /** 467 * @return the instance fields for this class 468 */ 469 @Nonnull 470 public List<EncodedField> getInstanceFields() { 471 if (instanceFields == null) { 472 return Collections.emptyList(); 473 } 474 return ReadOnlyArrayList.of(instanceFields); 475 } 476 477 /** 478 * @return the direct methods for this class 479 */ 480 @Nonnull 481 public List<EncodedMethod> getDirectMethods() { 482 if (directMethods == null) { 483 return Collections.emptyList(); 484 } 485 return ReadOnlyArrayList.of(directMethods); 486 } 487 488 /** 489 * @return the virtual methods for this class 490 */ 491 @Nonnull 492 public List<EncodedMethod> getVirtualMethods() { 493 if (virtualMethods == null) { 494 return Collections.emptyList(); 495 } 496 return ReadOnlyArrayList.of(virtualMethods); 497 } 498 499 /** 500 * @return The number of static fields in this <code>ClassDataItem</code> 501 */ 502 public int getStaticFieldCount() { 503 if (staticFields == null) { 504 return 0; 505 } 506 return staticFields.length; 507 } 508 509 /** 510 * @return The number of instance fields in this <code>ClassDataItem</code> 511 */ 512 public int getInstanceFieldCount() { 513 if (instanceFields == null) { 514 return 0; 515 } 516 return instanceFields.length; 517 } 518 519 /** 520 * @return The number of direct methods in this <code>ClassDataItem</code> 521 */ 522 public int getDirectMethodCount() { 523 if (directMethods == null) { 524 return 0; 525 } 526 return directMethods.length; 527 } 528 529 /** 530 * @return The number of virtual methods in this <code>ClassDataItem</code> 531 */ 532 public int getVirtualMethodCount() { 533 if (virtualMethods == null) { 534 return 0; 535 } 536 return virtualMethods.length; 537 } 538 539 /** 540 * @return true if this is an empty ClassDataItem 541 */ 542 public boolean isEmpty() { 543 return (getStaticFieldCount() + getInstanceFieldCount() + 544 getDirectMethodCount() + getVirtualMethodCount()) == 0; 545 } 546 547 /** 548 * Performs a binary search for the definition of the specified direct method 549 * @param methodIdItem The MethodIdItem of the direct method to search for 550 * @return The EncodedMethod for the specified direct method, or null if not found 551 */ 552 public EncodedMethod findDirectMethodByMethodId(MethodIdItem methodIdItem) { 553 return findMethodByMethodIdInternal(methodIdItem.index, directMethods); 554 } 555 556 /** 557 * Performs a binary search for the definition of the specified virtual method 558 * @param methodIdItem The MethodIdItem of the virtual method to search for 559 * @return The EncodedMethod for the specified virtual method, or null if not found 560 */ 561 public EncodedMethod findVirtualMethodByMethodId(MethodIdItem methodIdItem) { 562 return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods); 563 } 564 565 /** 566 * Performs a binary search for the definition of the specified method. It can be either direct or virtual 567 * @param methodIdItem The MethodIdItem of the virtual method to search for 568 * @return The EncodedMethod for the specified virtual method, or null if not found 569 */ 570 public EncodedMethod findMethodByMethodId(MethodIdItem methodIdItem) { 571 EncodedMethod encodedMethod = findMethodByMethodIdInternal(methodIdItem.index, directMethods); 572 if (encodedMethod != null) { 573 return encodedMethod; 574 } 575 576 return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods); 577 } 578 579 private static EncodedMethod findMethodByMethodIdInternal(int methodIdItemIndex, EncodedMethod[] encodedMethods) { 580 int min = 0; 581 int max = encodedMethods.length; 582 583 while (min<max) { 584 int index = (min+max)>>1; 585 586 EncodedMethod encodedMethod = encodedMethods[index]; 587 588 int encodedMethodIndex = encodedMethod.method.getIndex(); 589 if (encodedMethodIndex == methodIdItemIndex) { 590 return encodedMethod; 591 } else if (encodedMethodIndex < methodIdItemIndex) { 592 if (min == index) { 593 break; 594 } 595 min = index; 596 } else { 597 if (max == index) { 598 break; 599 } 600 max = index; 601 } 602 } 603 604 return null; 605 } 606 607 public static class EncodedField implements Comparable<EncodedField> { 608 /** 609 * The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with 610 */ 611 public final FieldIdItem field; 612 613 /** 614 * The access flags for this field 615 */ 616 public final int accessFlags; 617 618 /** 619 * Constructs a new <code>EncodedField</code> with the given values 620 * @param field The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with 621 * @param accessFlags The access flags for this field 622 */ 623 public EncodedField(FieldIdItem field, int accessFlags) { 624 this.field = field; 625 this.accessFlags = accessFlags; 626 } 627 628 /** 629 * This is used internally to construct a new <code>EncodedField</code> while reading in a <code>DexFile</code> 630 * @param dexFile The <code>DexFile</code> that is being read in 631 * @param in the Input object to read the <code>EncodedField</code> from 632 * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this 633 * <code>EncodedField</code>. 634 */ 635 private EncodedField(DexFile dexFile, Input in, @Nullable EncodedField previousEncodedField) { 636 int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); 637 field = dexFile.FieldIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex); 638 accessFlags = in.readUnsignedLeb128(); 639 } 640 641 /** 642 * Writes the <code>EncodedField</code> to the given <code>AnnotatedOutput</code> object 643 * @param out the <code>AnnotatedOutput</code> object to write to 644 * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this 645 * <code>EncodedField</code>. 646 */ 647 private void writeTo(AnnotatedOutput out, EncodedField previousEncodedField) { 648 int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); 649 650 if (out.annotates()) { 651 out.annotate("field: " + field.getFieldString()); 652 out.writeUnsignedLeb128(field.getIndex() - previousIndex); 653 out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForField(accessFlags)); 654 out.writeUnsignedLeb128(accessFlags); 655 }else { 656 out.writeUnsignedLeb128(field.getIndex() - previousIndex); 657 out.writeUnsignedLeb128(accessFlags); 658 } 659 } 660 661 /** 662 * Calculates the size of this <code>EncodedField</code> and returns the offset 663 * immediately following it 664 * @param offset the offset of this <code>EncodedField</code> in the <code>DexFile</code> 665 * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this 666 * <code>EncodedField</code>. 667 * @return the offset immediately following this <code>EncodedField</code> 668 */ 669 private int place(int offset, EncodedField previousEncodedField) { 670 int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); 671 672 offset += Leb128Utils.unsignedLeb128Size(field.getIndex() - previousIndex); 673 offset += Leb128Utils.unsignedLeb128Size(accessFlags); 674 return offset; 675 } 676 677 /** 678 * Compares this <code>EncodedField</code> to another, based on the comparison of the associated 679 * <code>FieldIdItem</code> 680 * @param other The <code>EncodedField</code> to compare against 681 * @return a standard integer comparison value indicating the relationship 682 */ 683 public int compareTo(EncodedField other) 684 { 685 return field.compareTo(other.field); 686 } 687 688 /** 689 * Determines if this <code>EncodedField</code> is equal to other, based on the equality of the associated 690 * <code>FieldIdItem</code> 691 * @param other The <code>EncodedField</code> to test for equality 692 * @return true if other is equal to this instance, otherwise false 693 */ 694 public boolean equals(Object other) { 695 if (other instanceof EncodedField) { 696 return compareTo((EncodedField)other) == 0; 697 } 698 return false; 699 } 700 701 /** 702 * @return true if this is a static field 703 */ 704 public boolean isStatic() { 705 return (accessFlags & AccessFlags.STATIC.getValue()) != 0; 706 } 707 } 708 709 public static class EncodedMethod implements Comparable<EncodedMethod> { 710 /** 711 * The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with 712 */ 713 public final MethodIdItem method; 714 715 /** 716 * The access flags for this method 717 */ 718 public final int accessFlags; 719 720 /** 721 * The <code>CodeItem</code> containing the code for this method, or null if there is no code for this method 722 * (i.e. an abstract method) 723 */ 724 public final CodeItem codeItem; 725 726 /** 727 * Constructs a new <code>EncodedMethod</code> with the given values 728 * @param method The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with 729 * @param accessFlags The access flags for this method 730 * @param codeItem The <code>CodeItem</code> containing the code for this method, or null if there is no code 731 * for this method (i.e. an abstract method) 732 */ 733 public EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem) { 734 this.method = method; 735 this.accessFlags = accessFlags; 736 this.codeItem = codeItem; 737 if (codeItem != null) { 738 codeItem.setParent(this); 739 } 740 } 741 742 /** 743 * This is used internally to construct a new <code>EncodedMethod</code> while reading in a <code>DexFile</code> 744 * @param dexFile The <code>DexFile</code> that is being read in 745 * @param readContext a <code>ReadContext</code> object to hold information that is only needed while reading 746 * in a file 747 * @param in the Input object to read the <code>EncodedMethod</code> from 748 * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this 749 * <code>EncodedMethod</code>. 750 */ 751 public EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod) { 752 int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); 753 method = dexFile.MethodIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex); 754 accessFlags = in.readUnsignedLeb128(); 755 if (dexFile.skipInstructions()) { 756 in.readUnsignedLeb128(); 757 codeItem = null; 758 } else { 759 codeItem = (CodeItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CODE_ITEM, 760 in.readUnsignedLeb128()); 761 } 762 if (codeItem != null) { 763 codeItem.setParent(this); 764 } 765 } 766 767 /** 768 * Writes the <code>EncodedMethod</code> to the given <code>AnnotatedOutput</code> object 769 * @param out the <code>AnnotatedOutput</code> object to write to 770 * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this 771 * <code>EncodedMethod</code>. 772 */ 773 private void writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod) { 774 int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); 775 776 if (out.annotates()) { 777 out.annotate("method: " + method.getMethodString()); 778 out.writeUnsignedLeb128(method.getIndex() - previousIndex); 779 out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForMethod(accessFlags)); 780 out.writeUnsignedLeb128(accessFlags); 781 if (codeItem != null) { 782 out.annotate("code_off: 0x" + Integer.toHexString(codeItem.getOffset())); 783 out.writeUnsignedLeb128(codeItem.getOffset()); 784 } else { 785 out.annotate("code_off: 0x0"); 786 out.writeUnsignedLeb128(0); 787 } 788 }else { 789 out.writeUnsignedLeb128(method.getIndex() - previousIndex); 790 out.writeUnsignedLeb128(accessFlags); 791 out.writeUnsignedLeb128(codeItem==null?0:codeItem.getOffset()); 792 } 793 } 794 795 /** 796 * Calculates the size of this <code>EncodedMethod</code> and returns the offset 797 * immediately following it 798 * @param offset the offset of this <code>EncodedMethod</code> in the <code>DexFile</code> 799 * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this 800 * <code>EncodedMethod</code>. 801 * @return the offset immediately following this <code>EncodedField</code> 802 */ 803 private int place(int offset, EncodedMethod previousEncodedMethod) { 804 int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); 805 806 offset += Leb128Utils.unsignedLeb128Size(method.getIndex() - previousIndex); 807 offset += Leb128Utils.unsignedLeb128Size(accessFlags); 808 offset += codeItem==null?1:Leb128Utils.unsignedLeb128Size(codeItem.getOffset()); 809 return offset; 810 } 811 812 /** 813 * Compares this <code>EncodedMethod</code> to another, based on the comparison of the associated 814 * <code>MethodIdItem</code> 815 * @param other The <code>EncodedMethod</code> to compare against 816 * @return a standard integer comparison value indicating the relationship 817 */ 818 public int compareTo(EncodedMethod other) { 819 return method.compareTo(other.method); 820 } 821 822 /** 823 * Determines if this <code>EncodedMethod</code> is equal to other, based on the equality of the associated 824 * <code>MethodIdItem</code> 825 * @param other The <code>EncodedMethod</code> to test for equality 826 * @return true if other is equal to this instance, otherwise false 827 */ 828 public boolean equals(Object other) { 829 if (other instanceof EncodedMethod) { 830 return compareTo((EncodedMethod)other) == 0; 831 } 832 return false; 833 } 834 835 /** 836 * @return true if this is a direct method 837 */ 838 public boolean isDirect() { 839 return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() | 840 AccessFlags.CONSTRUCTOR.getValue())) != 0); 841 } 842 } 843} 844