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