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