ClassDefItem.java revision d320d6ce8c8b9ea0aa1c661fc4219e4679c1e196
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 org.jf.dexlib.EncodedValue.ArrayEncodedSubValue; 32import org.jf.dexlib.EncodedValue.EncodedValue; 33import org.jf.dexlib.Util.AccessFlags; 34import org.jf.dexlib.Util.AnnotatedOutput; 35import org.jf.dexlib.Util.Input; 36import org.jf.dexlib.Util.TypeUtils; 37 38import java.util.Collections; 39import java.util.Comparator; 40import java.util.HashMap; 41import java.util.List; 42 43public class ClassDefItem extends Item<ClassDefItem> { 44 private TypeIdItem classType; 45 private int accessFlags; 46 private TypeIdItem superType; 47 private TypeListItem implementedInterfaces; 48 private StringIdItem sourceFile; 49 private AnnotationDirectoryItem annotations; 50 private ClassDataItem classData; 51 private EncodedArrayItem staticFieldInitializers; 52 53 /** 54 * Creates a new uninitialized <code>ClassDefItem</code> 55 * @param dexFile The <code>DexFile</code> that this item belongs to 56 */ 57 protected ClassDefItem(DexFile dexFile) { 58 super(dexFile); 59 } 60 61 /** 62 * Creates a new <code>ClassDefItem</code> with the given values 63 * @param dexFile The <code>DexFile</code> that this item belongs to 64 * @param classType The type of this class 65 * @param accessFlags The access flags of this class 66 * @param superType The superclass of this class, or null if none (only valid for java.lang.Object) 67 * @param implementedInterfaces A list of the interfaces that this class implements, or null if none 68 * @param sourceFile The main source file that this class is defined in, or null if not available 69 * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none 70 * @param classData The <code>ClassDataItem</code> containing the method and field definitions for this class 71 * @param staticFieldInitializers The initial values for this class's static fields, or null if none. The initial 72 * values should be in the same order as the static fields in the <code>ClassDataItem</code>. It can contain 73 * fewer items than static fields, in which case the remaining static fields will be initialized with a default 74 * value of null/0. The initial value for any fields that don't specifically have a value can be either the 75 * type-appropriate null/0 encoded value, or null. 76 */ 77 private ClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, TypeIdItem superType, 78 TypeListItem implementedInterfaces, StringIdItem sourceFile, 79 AnnotationDirectoryItem annotations, ClassDataItem classData, 80 EncodedArrayItem staticFieldInitializers) { 81 super(dexFile); 82 assert classType != null; 83 this.classType = classType; 84 this.accessFlags = accessFlags; 85 this.superType = superType; 86 this.implementedInterfaces = implementedInterfaces; 87 this.sourceFile = sourceFile; 88 this.annotations = annotations; 89 this.classData = classData; 90 this.staticFieldInitializers = staticFieldInitializers; 91 92 if (classData != null) { 93 classData.setParent(this); 94 } 95 if (annotations != null) { 96 annotations.setParent(this); 97 } 98 } 99 100 /** 101 * Returns a <code>ClassDefItem</code> for the given values, and that has been interned into the given 102 * <code>DexFile</code> 103 * @param dexFile The <code>DexFile</code> that this item belongs to 104 * @param classType The type of this class 105 * @param accessFlags The access flags of this class 106 * @param superType The superclass of this class, or null if none (only valid for java.lang.Object) 107 * @param implementedInterfaces A list of the interfaces that this class implements, or null if none 108 * @param sourceFile The main source file that this class is defined in, or null if not available 109 * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none 110 * @param classData The <code>ClassDataItem</code> containing the method and field definitions for this class 111 * @param staticFieldInitializers The initial values for this class's static fields, or null if none. If it is not 112 * null, it must contain the same number of items as the number of static fields in this class. The value in the 113 * <code>StaticFieldInitializer</code> for any field that doesn't have an explicit initial value can either be null 114 * or be the type-appropriate null/0 value. 115 * @return a <code>ClassDefItem</code> for the given values, and that has been interned into the given 116 * <code>DexFile</code> 117 */ 118 public static ClassDefItem internClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, 119 TypeIdItem superType, TypeListItem implementedInterfaces, StringIdItem sourceFile, 120 AnnotationDirectoryItem annotations, ClassDataItem classData, 121 List<StaticFieldInitializer> staticFieldInitializers) { 122 EncodedArrayItem encodedArrayItem = null; 123 if(!dexFile.getInplace() && staticFieldInitializers != null && staticFieldInitializers.size() > 0) { 124 assert classData != null; 125 assert staticFieldInitializers.size() == classData.getStaticFields().length; 126 encodedArrayItem = makeStaticFieldInitializersItem(dexFile, staticFieldInitializers); 127 } 128 129 ClassDefItem classDefItem = new ClassDefItem(dexFile, classType, accessFlags, superType, implementedInterfaces, 130 sourceFile, annotations, classData, encodedArrayItem); 131 return dexFile.ClassDefsSection.intern(classDefItem); 132 } 133 134 /** 135 * Looks up a <code>ClassDefItem</code> from the given <code>DexFile</code> for the given 136 * values 137 * @param dexFile The <code>DexFile</code> that the <code>ClassDefItem</code> belongs to 138 * @param classType The type of the class 139 * @param accessFlags The access flags of the class 140 * @param superType The superclass of the class, or null if none (only valid for java.lang.Object) 141 * @param implementedInterfaces A list of the interfaces that the class implements, or null if none 142 * @param sourceFile The main source file that the class is defined in, or null if not available 143 * @param annotations The annotations for the class and its fields, methods and method parameters, or null if none 144 * @param classData The <code>ClassDataItem</code> containing the method and field definitions for the class 145 * @param staticFieldInitializers The initial values for the class's static fields, or null if none. If it is not 146 * null, it must contain the same number of items as the number of static fields in the class. The value in the 147 * <code>StaticFieldInitializer</code> for any field that doesn't have an explicit initial value can either be null 148 * or be the type-appropriate null/0 value. 149 * @return a <code>ClassDefItem</code> from the given <code>DexFile</code> for the given 150 * values, or null if it doesn't exist 151 */ 152 public static ClassDefItem lookupClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, 153 TypeIdItem superType, TypeListItem implementedInterfaces, StringIdItem sourceFile, 154 AnnotationDirectoryItem annotations, ClassDataItem classData, 155 List<StaticFieldInitializer> staticFieldInitializers) { 156 EncodedArrayItem encodedArrayItem = null; 157 if(!dexFile.getInplace() && staticFieldInitializers != null && staticFieldInitializers.size() > 0) { 158 assert classData != null; 159 assert staticFieldInitializers.size() == classData.getStaticFields().length; 160 encodedArrayItem = makeStaticFieldInitializersItem(dexFile, staticFieldInitializers); 161 } 162 163 ClassDefItem classDefItem = new ClassDefItem(dexFile, classType, accessFlags, superType, implementedInterfaces, 164 sourceFile, annotations, classData, encodedArrayItem); 165 return dexFile.ClassDefsSection.getInternedItem(classDefItem); 166 } 167 168 /** {@inheritDoc} */ 169 protected void readItem(Input in, ReadContext readContext) { 170 classType = dexFile.TypeIdsSection.getItemByIndex(in.readInt()); 171 accessFlags = in.readInt(); 172 superType = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readInt()); 173 implementedInterfaces = (TypeListItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST, 174 in.readInt()); 175 sourceFile = dexFile.StringIdsSection.getOptionalItemByIndex(in.readInt()); 176 annotations = (AnnotationDirectoryItem)readContext.getOptionalOffsettedItemByOffset( 177 ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, in.readInt()); 178 classData = (ClassDataItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CLASS_DATA_ITEM, in.readInt()); 179 staticFieldInitializers = (EncodedArrayItem)readContext.getOptionalOffsettedItemByOffset( 180 ItemType.TYPE_ENCODED_ARRAY_ITEM, in.readInt()); 181 182 if (classData != null) { 183 classData.setParent(this); 184 } 185 if (annotations != null) { 186 annotations.setParent(this); 187 } 188 } 189 190 /** {@inheritDoc} */ 191 protected int placeItem(int offset) { 192 return offset + 32; 193 } 194 195 /** {@inheritDoc} */ 196 protected void writeItem(AnnotatedOutput out) { 197 if (out.annotates()) { 198 out.annotate(4, "class_type: " + classType.getTypeDescriptor()); 199 out.annotate(4, "access_flags: " + AccessFlags.formatAccessFlagsForClass(accessFlags)); 200 out.annotate(4, "superclass_type: " + (superType==null?"":superType.getTypeDescriptor())); 201 out.annotate(4, "interfaces: " + 202 (implementedInterfaces==null?"":implementedInterfaces.getTypeListString(" "))); 203 out.annotate(4, "source_file: " + (sourceFile==null?"":sourceFile.getStringValue())); 204 out.annotate(4, "annotations_off: " + 205 (annotations==null?"":"0x"+Integer.toHexString(annotations.getOffset()))); 206 out.annotate(4, "class_data_off:" + 207 (classData==null?"":"0x"+Integer.toHexString(classData.getOffset()))); 208 out.annotate(4, "static_values_off: " + 209 (staticFieldInitializers==null?"":"0x"+Integer.toHexString(staticFieldInitializers.getOffset()))); 210 } 211 out.writeInt(classType.getIndex()); 212 out.writeInt(accessFlags); 213 out.writeInt(superType==null?-1:superType.getIndex()); 214 out.writeInt(implementedInterfaces==null?0:implementedInterfaces.getOffset()); 215 out.writeInt(sourceFile==null?-1:sourceFile.getIndex()); 216 out.writeInt(annotations==null?0:annotations.getOffset()); 217 out.writeInt(classData==null?0:classData.getOffset()); 218 out.writeInt(staticFieldInitializers==null?0:staticFieldInitializers.getOffset()); 219 } 220 221 /** {@inheritDoc} */ 222 public ItemType getItemType() { 223 return ItemType.TYPE_CLASS_DEF_ITEM; 224 } 225 226 /** {@inheritDoc} */ 227 public String getConciseIdentity() { 228 return "class_def_item: " + classType.getTypeDescriptor(); 229 } 230 231 /** {@inheritDoc} */ 232 public int compareTo(ClassDefItem o) { 233 //The actual sorting for this class is implemented in SortClassDefItemSection. 234 //This method is just used for sorting the associated ClassDataItem items, so 235 //we can just do the comparison based on the offsets of the items 236 return this.getOffset() - o.getOffset(); 237 } 238 239 public TypeIdItem getClassType() { 240 return classType; 241 } 242 243 public int getAccessFlags() { 244 return accessFlags; 245 } 246 247 public TypeIdItem getSuperclass() { 248 return superType; 249 } 250 251 public TypeListItem getInterfaces() { 252 return implementedInterfaces; 253 } 254 255 public StringIdItem getSourceFile() { 256 return sourceFile; 257 } 258 259 public AnnotationDirectoryItem getAnnotations() { 260 return annotations; 261 } 262 263 public ClassDataItem getClassData() { 264 return classData; 265 } 266 267 public EncodedArrayItem getStaticFieldInitializers() { 268 return staticFieldInitializers; 269 } 270 271 public static int placeClassDefItems(IndexedSection<ClassDefItem> section, int offset) { 272 ClassDefPlacer cdp = new ClassDefPlacer(section); 273 return cdp.placeSection(offset); 274 } 275 276 /** 277 * This class places the items within a ClassDefItem section, such that superclasses and interfaces are 278 * placed before sub/implementing classes 279 */ 280 private static class ClassDefPlacer { 281 private final IndexedSection<ClassDefItem> section; 282 private final HashMap<TypeIdItem, ClassDefItem> unplacedClassDefsByType = 283 new HashMap<TypeIdItem, ClassDefItem>(); 284 285 private int currentIndex = 0; 286 private int currentOffset; 287 288 public ClassDefPlacer(IndexedSection<ClassDefItem> section) { 289 this.section = section; 290 291 for (ClassDefItem classDefItem: section.items) { 292 TypeIdItem typeIdItem = classDefItem.classType; 293 unplacedClassDefsByType.put(typeIdItem, classDefItem); 294 } 295 } 296 297 public int placeSection(int offset) { 298 currentOffset = offset; 299 300 if (section.DexFile.getSortAllItems()) { 301 //presort the list, to guarantee a unique ordering 302 Collections.sort(section.items, new Comparator<ClassDefItem>() { 303 public int compare(ClassDefItem a, ClassDefItem b) { 304 return a.getClassType().compareTo(b.getClassType()); 305 } 306 }); 307 } 308 309 //we need to initialize the offset for all the classes to -1, so we can tell which ones 310 //have been placed 311 for (ClassDefItem classDefItem: section.items) { 312 classDefItem.offset = -1; 313 } 314 315 for (ClassDefItem classDefItem: section.items) { 316 placeClass(classDefItem); 317 } 318 319 for (ClassDefItem classDefItem: unplacedClassDefsByType.values()) { 320 section.items.set(classDefItem.getIndex(), classDefItem); 321 } 322 323 return currentOffset; 324 } 325 326 private void placeClass(ClassDefItem classDefItem) { 327 if (!classDefItem.isPlaced()) { 328 TypeIdItem superType = classDefItem.superType; 329 ClassDefItem superClassDefItem = unplacedClassDefsByType.get(superType); 330 331 if (superClassDefItem != null) { 332 placeClass(superClassDefItem); 333 } 334 335 TypeListItem interfaces = classDefItem.implementedInterfaces; 336 337 if (interfaces != null) { 338 for (TypeIdItem interfaceType: interfaces.getTypes()) { 339 ClassDefItem interfaceClass = unplacedClassDefsByType.get(interfaceType); 340 if (interfaceClass != null) { 341 placeClass(interfaceClass); 342 } 343 } 344 } 345 346 currentOffset = classDefItem.placeAt(currentOffset, currentIndex++); 347 unplacedClassDefsByType.remove(classDefItem.classType); 348 } 349 } 350 } 351 352 public static class StaticFieldInitializer implements Comparable<StaticFieldInitializer> { 353 public final EncodedValue value; 354 public final ClassDataItem.EncodedField field; 355 public StaticFieldInitializer(EncodedValue value, ClassDataItem.EncodedField field) { 356 this.value = value; 357 this.field = field; 358 } 359 360 public int compareTo(StaticFieldInitializer other) { 361 return field.compareTo(other.field); 362 } 363 } 364 365 366 /** 367 * A helper method to sort the static field initializers and populate the default values as needed 368 * @param dexFile the <code>DexFile</code> 369 * @param staticFieldInitializers the initial values 370 * @return an interned EncodedArrayItem containing the static field initializers 371 */ 372 private static EncodedArrayItem makeStaticFieldInitializersItem(DexFile dexFile, 373 List<StaticFieldInitializer> staticFieldInitializers) { 374 if (staticFieldInitializers == null || staticFieldInitializers.size() == 0) { 375 return null; 376 } 377 378 int len = staticFieldInitializers.size(); 379 380 Collections.sort(staticFieldInitializers); 381 382 int lastIndex = -1; 383 for (int i=len-1; i>=0; i--) { 384 StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i); 385 386 if (staticFieldInitializer.value != null && 387 (staticFieldInitializer.value.compareTo(TypeUtils.makeDefaultValueForType(dexFile, 388 staticFieldInitializer.field.field.getFieldType().getTypeDescriptor())) != 0)) { 389 lastIndex = i; 390 break; 391 } 392 } 393 394 //we don't have any non-null/non-default values, so we don't need to create an EncodedArrayItem 395 if (lastIndex == -1) { 396 return null; 397 } 398 399 EncodedValue[] values = new EncodedValue[lastIndex+1]; 400 401 for (int i=0; i<=lastIndex; i++) { 402 StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i); 403 EncodedValue encodedValue = staticFieldInitializer.value; 404 if (encodedValue == null) { 405 encodedValue = TypeUtils.makeDefaultValueForType(dexFile, 406 staticFieldInitializer.field.field.getFieldType().getTypeDescriptor()); 407 } 408 409 values[i] = encodedValue; 410 } 411 412 ArrayEncodedSubValue encodedArrayValue = new ArrayEncodedSubValue(values); 413 return EncodedArrayItem.internEncodedArrayItem(dexFile, encodedArrayValue); 414 } 415} 416