ClassDataItem.java revision 7eca83ddb6b9958c04cb19f71b0124338f9fc631
1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2009 Ben Gruver 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 org.jf.dexlib.Util.*; 32 33import java.util.List; 34import java.util.Collections; 35 36public class ClassDataItem extends Item<ClassDataItem> { 37 private EncodedField[] staticFields; 38 private EncodedField[] instanceFields; 39 private EncodedMethod[] directMethods; 40 private EncodedMethod[] virtualMethods; 41 42 private ClassDefItem parent = null; 43 44 /** 45 * Creates a new uninitialized <code>ClassDataItem</code> 46 * @param dexFile The <code>DexFile</code> that this item belongs to 47 */ 48 public ClassDataItem(final DexFile dexFile) { 49 super(dexFile); 50 } 51 52 /** 53 * Creates a new <code>ClassDataItem</code> with the given values 54 * @param dexFile The <code>DexFile</code> that this item belongs to 55 * @param staticFields The static fields for this class 56 * @param instanceFields The instance fields for this class 57 * @param directMethods The direct methods for this class 58 * @param virtualMethods The virtual methods for this class 59 */ 60 private ClassDataItem(DexFile dexFile, EncodedField[] staticFields, EncodedField[] instanceFields, 61 EncodedMethod[] directMethods, EncodedMethod[] virtualMethods) { 62 super(dexFile); 63 this.staticFields = staticFields==null?new EncodedField[0]:staticFields; 64 this.instanceFields = instanceFields==null?new EncodedField[0]:instanceFields; 65 this.directMethods = directMethods==null?new EncodedMethod[0]:directMethods; 66 this.virtualMethods = virtualMethods==null?new EncodedMethod[0]:virtualMethods; 67 } 68 69 /** 70 * Creates a new <code>ClassDataItem</code> with the given values 71 * @param dexFile The <code>DexFile</code> that this item belongs to 72 * @param staticFields The static fields for this class 73 * @param instanceFields The instance fields for this class 74 * @param directMethods The direct methods for this class 75 * @param virtualMethods The virtual methods for this class 76 * @return a new <code>ClassDataItem</code> with the given values 77 */ 78 public static ClassDataItem getInternedClassDataItem(DexFile dexFile, List<EncodedField> staticFields, 79 List<EncodedField> instanceFields, 80 List<EncodedMethod> directMethods, 81 List<EncodedMethod> virtualMethods) { 82 EncodedField[] staticFieldsArray = null; 83 EncodedField[] instanceFieldsArray = null; 84 EncodedMethod[] directMethodsArray = null; 85 EncodedMethod[] virtualMethodsArray = null; 86 87 if (staticFields != null && staticFields.size() > 0) { 88 Collections.sort(staticFields); 89 staticFieldsArray = new EncodedField[staticFields.size()]; 90 staticFields.toArray(staticFieldsArray); 91 } 92 93 if (instanceFields != null && instanceFields.size() > 0) { 94 Collections.sort(instanceFields); 95 instanceFieldsArray = new EncodedField[instanceFields.size()]; 96 instanceFields.toArray(instanceFieldsArray); 97 } 98 99 if (directMethods != null && directMethods.size() > 0) { 100 Collections.sort(directMethods); 101 directMethodsArray = new EncodedMethod[directMethods.size()]; 102 directMethods.toArray(directMethodsArray); 103 } 104 105 if (virtualMethods != null && virtualMethods.size() > 0) { 106 Collections.sort(virtualMethods); 107 virtualMethodsArray = new EncodedMethod[virtualMethods.size()]; 108 virtualMethods.toArray(virtualMethodsArray); 109 } 110 111 ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFieldsArray, instanceFieldsArray, 112 directMethodsArray, virtualMethodsArray); 113 return dexFile.ClassDataSection.intern(classDataItem); 114 } 115 116 /** {@inheritDoc} */ 117 protected void readItem(Input in, ReadContext readContext) { 118 staticFields = new EncodedField[in.readUnsignedLeb128()]; 119 instanceFields = new EncodedField[in.readUnsignedLeb128()]; 120 directMethods = new EncodedMethod[in.readUnsignedLeb128()]; 121 virtualMethods = new EncodedMethod[in.readUnsignedLeb128()]; 122 123 EncodedField previousEncodedField = null; 124 for (int i=0; i<staticFields.length; i++) { 125 staticFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField); 126 } 127 128 previousEncodedField = null; 129 for (int i=0; i<instanceFields.length; i++) { 130 instanceFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField); 131 } 132 133 EncodedMethod previousEncodedMethod = null; 134 for (int i=0; i<directMethods.length; i++) { 135 directMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in, 136 previousEncodedMethod); 137 } 138 139 previousEncodedMethod = null; 140 for (int i=0; i<virtualMethods.length; i++) { 141 virtualMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in, 142 previousEncodedMethod); 143 } 144 } 145 146 /** {@inheritDoc} */ 147 protected int placeItem(int offset) { 148 offset += Leb128Utils.unsignedLeb128Size(staticFields.length); 149 offset += Leb128Utils.unsignedLeb128Size(instanceFields.length); 150 offset += Leb128Utils.unsignedLeb128Size(directMethods.length); 151 offset += Leb128Utils.unsignedLeb128Size(virtualMethods.length); 152 153 EncodedField previousEncodedField = null; 154 for (EncodedField encodedField: staticFields) { 155 offset = encodedField.place(offset, previousEncodedField); 156 previousEncodedField = encodedField; 157 } 158 159 previousEncodedField = null; 160 for (EncodedField encodedField: instanceFields) { 161 offset = encodedField.place(offset, previousEncodedField); 162 previousEncodedField = encodedField; 163 } 164 165 EncodedMethod previousEncodedMethod = null; 166 for (EncodedMethod encodedMethod: directMethods) { 167 offset = encodedMethod.place(offset, previousEncodedMethod); 168 previousEncodedMethod = encodedMethod; 169 } 170 171 previousEncodedMethod = null; 172 for (EncodedMethod encodedMethod: virtualMethods) { 173 offset = encodedMethod.place(offset, previousEncodedMethod); 174 previousEncodedMethod = encodedMethod; 175 } 176 177 return offset; 178 } 179 180 /** {@inheritDoc} */ 181 protected void writeItem(AnnotatedOutput out) { 182 if (out.annotates()) { 183 out.annotate("static_fields_size: 0x" + Integer.toHexString(staticFields.length) + " (" + 184 staticFields.length + ")"); 185 out.writeUnsignedLeb128(staticFields.length); 186 out.annotate("instance_fields_size: 0x" + Integer.toHexString(instanceFields.length) + " (" + 187 instanceFields.length + ")"); 188 out.writeUnsignedLeb128(instanceFields.length); 189 out.annotate("direct_methods_size: 0x" + Integer.toHexString(directMethods.length) + " (" + 190 directMethods.length + ")"); 191 out.writeUnsignedLeb128(directMethods.length); 192 out.annotate("virtual_methods_size: 0x" + Integer.toHexString(virtualMethods.length) + " (" + 193 virtualMethods.length + ")"); 194 out.writeUnsignedLeb128(virtualMethods.length); 195 196 int index = 0; 197 EncodedField previousEncodedField = null; 198 for (EncodedField encodedField: staticFields) { 199 out.annotate("[" + index++ + "] static_field"); 200 out.indent(); 201 encodedField.writeTo(out, previousEncodedField); 202 out.deindent(); 203 previousEncodedField = encodedField; 204 } 205 206 index = 0; 207 previousEncodedField = null; 208 for (EncodedField encodedField: instanceFields) { 209 out.annotate("[" + index++ + "] instance_field"); 210 out.indent(); 211 encodedField.writeTo(out, previousEncodedField); 212 out.deindent(); 213 previousEncodedField = encodedField; 214 } 215 216 index = 0; 217 EncodedMethod previousEncodedMethod = null; 218 for (EncodedMethod encodedMethod: directMethods) { 219 out.annotate("[" + index++ + "] direct_method"); 220 out.indent(); 221 encodedMethod.writeTo(out, previousEncodedMethod); 222 out.deindent(); 223 previousEncodedMethod = encodedMethod; 224 } 225 226 index = 0; 227 previousEncodedMethod = null; 228 for (EncodedMethod encodedMethod: virtualMethods) { 229 out.annotate("[" + index++ + "] virtual_method"); 230 out.indent(); 231 encodedMethod.writeTo(out, previousEncodedMethod); 232 out.deindent(); 233 previousEncodedMethod = encodedMethod; 234 } 235 } else { 236 out.writeUnsignedLeb128(staticFields.length); 237 out.writeUnsignedLeb128(instanceFields.length); 238 out.writeUnsignedLeb128(directMethods.length); 239 out.writeUnsignedLeb128(virtualMethods.length); 240 241 EncodedField previousEncodedField = null; 242 for (EncodedField encodedField: staticFields) { 243 encodedField.writeTo(out, previousEncodedField); 244 previousEncodedField = encodedField; 245 } 246 247 previousEncodedField = null; 248 for (EncodedField encodedField: instanceFields) { 249 encodedField.writeTo(out, previousEncodedField); 250 previousEncodedField = encodedField; 251 } 252 253 EncodedMethod previousEncodedMethod = null; 254 for (EncodedMethod encodedMethod: directMethods) { 255 encodedMethod.writeTo(out, previousEncodedMethod); 256 previousEncodedMethod = encodedMethod; 257 } 258 259 previousEncodedMethod = null; 260 for (EncodedMethod encodedMethod: virtualMethods) { 261 encodedMethod.writeTo(out, previousEncodedMethod); 262 previousEncodedMethod = encodedMethod; 263 } 264 } 265 } 266 267 /** {@inheritDoc} */ 268 public ItemType getItemType() { 269 return ItemType.TYPE_CLASS_DATA_ITEM; 270 } 271 272 /** {@inheritDoc} */ 273 public String getConciseIdentity() { 274 return "class_data_item @0x" + Integer.toHexString(getOffset()); 275 } 276 277 /** {@inheritDoc} */ 278 public int compareTo(ClassDataItem other) { 279 if (parent == null) { 280 if (other.parent == null) { 281 return 0; 282 } 283 return -1; 284 } 285 if (other.parent == null) { 286 return 1; 287 } 288 return parent.compareTo(other.parent); 289 } 290 291 /** 292 * Sets the <code>ClassDefItem</code> that this <code>ClassDataItem</code> is associated with 293 * @param classDefItem the <code>ClassDefItem</code> that this <code>ClassDataItem</code> is associated with 294 */ 295 protected void setParent(ClassDefItem classDefItem) { 296 this.parent = classDefItem; 297 } 298 299 /** 300 * @return the static fields for this class 301 */ 302 public EncodedField[] getStaticFields() { 303 return staticFields; 304 } 305 306 /** 307 * @return the instance fields for this class 308 */ 309 public EncodedField[] getInstanceFields() { 310 return instanceFields; 311 } 312 313 /** 314 * @return the direct methods for this class 315 */ 316 public EncodedMethod[] getDirectMethods() { 317 return directMethods; 318 } 319 320 /** 321 * @return the virtual methods for this class 322 */ 323 public EncodedMethod[] getVirtualMethods() { 324 return virtualMethods; 325 } 326 327 public static class EncodedField implements Comparable<EncodedField> { 328 /** 329 * The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with 330 */ 331 public final FieldIdItem field; 332 333 /** 334 * The access flags for this field 335 */ 336 public final int accessFlags; 337 338 /** 339 * Constructs a new <code>EncodedField</code> with the given values 340 * @param field The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with 341 * @param accessFlags The access flags for this field 342 */ 343 public EncodedField(FieldIdItem field, int accessFlags) { 344 this.field = field; 345 this.accessFlags = accessFlags; 346 } 347 348 /** 349 * This is used internally to construct a new <code>EncodedField</code> while reading in a <code>DexFile</code> 350 * @param dexFile The <code>DexFile</code> that is being read in 351 * @param in the Input object to read the <code>EncodedField</code> from 352 * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this 353 * <code>EncodedField</code>. 354 */ 355 private EncodedField(DexFile dexFile, Input in, EncodedField previousEncodedField) { 356 int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); 357 field = dexFile.FieldIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex); 358 accessFlags = in.readUnsignedLeb128(); 359 } 360 361 /** 362 * Writes the <code>EncodedField</code> to the given <code>AnnotatedOutput</code> object 363 * @param out the <code>AnnotatedOutput</code> object to write to 364 * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this 365 * <code>EncodedField</code>. 366 */ 367 private void writeTo(AnnotatedOutput out, EncodedField previousEncodedField) { 368 int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); 369 370 if (out.annotates()) { 371 out.annotate("field: " + field.getFieldString()); 372 out.writeUnsignedLeb128(field.getIndex() - previousIndex); 373 out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForField(accessFlags)); 374 out.writeUnsignedLeb128(accessFlags); 375 }else { 376 out.writeUnsignedLeb128(field.getIndex() - previousIndex); 377 out.writeUnsignedLeb128(accessFlags); 378 } 379 } 380 381 /** 382 * Calculates the size of this <code>EncodedField</code> and returns the offset 383 * immediately following it 384 * @param offset the offset of this <code>EncodedField</code> in the <code>DexFile</code> 385 * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this 386 * <code>EncodedField</code>. 387 * @return the offset immediately following this <code>EncodedField</code> 388 */ 389 private int place(int offset, EncodedField previousEncodedField) { 390 int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); 391 392 offset += Leb128Utils.unsignedLeb128Size(field.getIndex() - previousIndex); 393 offset += Leb128Utils.unsignedLeb128Size(accessFlags); 394 return offset; 395 } 396 397 /** 398 * Compares this <code>EncodedField</code> to another, based on the comparison of the associated 399 * <code>FieldIdItem</code> 400 * @param other The <code>EncodedField</code> to compare against 401 * @return a standard integer comparison value indicating the relationship 402 */ 403 public int compareTo(EncodedField other) 404 { 405 return field.compareTo(other.field); 406 } 407 408 /** 409 * @return true if this is a static field 410 */ 411 public boolean isStatic() { 412 return (accessFlags & AccessFlags.STATIC.getValue()) != 0; 413 } 414 } 415 416 public static class EncodedMethod implements Comparable<EncodedMethod> { 417 /** 418 * The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with 419 */ 420 public final MethodIdItem method; 421 422 /** 423 * The access flags for this method 424 */ 425 public final int accessFlags; 426 427 /** 428 * The <code>CodeItem</code> containing the code for this method, or null if there is no code for this method 429 * (i.e. an abstract method) 430 */ 431 public final CodeItem codeItem; 432 433 /** 434 * Constructs a new <code>EncodedMethod</code> with the given values 435 * @param method The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with 436 * @param accessFlags The access flags for this method 437 * @param codeItem The <code>CodeItem</code> containing the code for this method, or null if there is no code 438 * for this method (i.e. an abstract method) 439 */ 440 public EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem) { 441 this.method = method; 442 this.accessFlags = accessFlags; 443 this.codeItem = codeItem; 444 if (codeItem != null) { 445 codeItem.setParent(this); 446 } 447 } 448 449 /** 450 * This is used internally to construct a new <code>EncodedMethod</code> while reading in a <code>DexFile</code> 451 * @param dexFile The <code>DexFile</code> that is being read in 452 * @param readContext a <code>ReadContext</code> object to hold information that is only needed while reading 453 * in a file 454 * @param in the Input object to read the <code>EncodedMethod</code> from 455 * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this 456 * <code>EncodedMethod</code>. 457 */ 458 public EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod) { 459 int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); 460 method = dexFile.MethodIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex); 461 accessFlags = in.readUnsignedLeb128(); 462 codeItem = (CodeItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_CODE_ITEM, in.readUnsignedLeb128()); 463 if (codeItem != null) { 464 codeItem.setParent(this); 465 } 466 } 467 468 /** 469 * Writes the <code>EncodedMethod</code> to the given <code>AnnotatedOutput</code> object 470 * @param out the <code>AnnotatedOutput</code> object to write to 471 * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this 472 * <code>EncodedMethod</code>. 473 */ 474 private void writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod) { 475 int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); 476 477 if (out.annotates()) { 478 out.annotate("method: " + method.getMethodString()); 479 out.writeUnsignedLeb128(method.getIndex() - previousIndex); 480 out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForMethod(accessFlags)); 481 out.writeUnsignedLeb128(accessFlags); 482 if (codeItem != null) { 483 out.annotate("code_off: 0x" + codeItem.getOffset()); 484 out.writeUnsignedLeb128(codeItem.getOffset()); 485 } else { 486 out.annotate("code_off: 0x0"); 487 out.writeUnsignedLeb128(0); 488 } 489 }else { 490 out.writeUnsignedLeb128(method.getIndex() - previousIndex); 491 out.writeUnsignedLeb128(accessFlags); 492 out.writeUnsignedLeb128(codeItem==null?0:codeItem.getOffset()); 493 } 494 } 495 496 /** 497 * Calculates the size of this <code>EncodedMethod</code> and returns the offset 498 * immediately following it 499 * @param offset the offset of this <code>EncodedMethod</code> in the <code>DexFile</code> 500 * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this 501 * <code>EncodedMethod</code>. 502 * @return the offset immediately following this <code>EncodedField</code> 503 */ 504 private int place(int offset, EncodedMethod previousEncodedMethod) { 505 int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); 506 507 offset += Leb128Utils.unsignedLeb128Size(method.getIndex() - previousIndex); 508 offset += Leb128Utils.unsignedLeb128Size(accessFlags); 509 offset += codeItem==null?1:Leb128Utils.unsignedLeb128Size(codeItem.getOffset()); 510 return offset; 511 } 512 513 /** 514 * Compares this <code>EncodedMethod</code> to another, based on the comparison of the associated 515 * <code>MethodIdItem</code> 516 * @param other The <code>EncodedMethod</code> to compare against 517 * @return a standard integer comparison value indicating the relationship 518 */ 519 public int compareTo(EncodedMethod other) { 520 return method.compareTo(other.method); 521 } 522 523 /** 524 * @return true if this is a direct method 525 */ 526 public boolean isDirect() { 527 return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() | 528 AccessFlags.CONSTRUCTOR.getValue())) != 0); 529 } 530 } 531} 532