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