/* * [The "BSD licence"] * Copyright (c) 2009 Ben Gruver * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jf.dexlib; import org.jf.dexlib.Util.*; import java.util.List; import java.util.Collections; public class ClassDataItem extends Item { private EncodedField[] staticFields; private EncodedField[] instanceFields; private EncodedMethod[] directMethods; private EncodedMethod[] virtualMethods; private ClassDefItem parent = null; /** * Creates a new uninitialized ClassDataItem * @param dexFile The DexFile that this item belongs to */ public ClassDataItem(final DexFile dexFile) { super(dexFile); } /** * Creates a new ClassDataItem with the given values * @param dexFile The DexFile that this item belongs to * @param staticFields The static fields for this class * @param instanceFields The instance fields for this class * @param directMethods The direct methods for this class * @param virtualMethods The virtual methods for this class */ private ClassDataItem(DexFile dexFile, EncodedField[] staticFields, EncodedField[] instanceFields, EncodedMethod[] directMethods, EncodedMethod[] virtualMethods) { super(dexFile); this.staticFields = staticFields==null?new EncodedField[0]:staticFields; this.instanceFields = instanceFields==null?new EncodedField[0]:instanceFields; this.directMethods = directMethods==null?new EncodedMethod[0]:directMethods; this.virtualMethods = virtualMethods==null?new EncodedMethod[0]:virtualMethods; } /** * Creates a new ClassDataItem with the given values * @param dexFile The DexFile that this item belongs to * @param staticFields The static fields for this class * @param instanceFields The instance fields for this class * @param directMethods The direct methods for this class * @param virtualMethods The virtual methods for this class * @return a new ClassDataItem with the given values */ public static ClassDataItem getInternedClassDataItem(DexFile dexFile, List staticFields, List instanceFields, List directMethods, List virtualMethods) { EncodedField[] staticFieldsArray = null; EncodedField[] instanceFieldsArray = null; EncodedMethod[] directMethodsArray = null; EncodedMethod[] virtualMethodsArray = null; if (staticFields != null && staticFields.size() > 0) { Collections.sort(staticFields); staticFieldsArray = new EncodedField[staticFields.size()]; staticFields.toArray(staticFieldsArray); } if (instanceFields != null && instanceFields.size() > 0) { Collections.sort(instanceFields); instanceFieldsArray = new EncodedField[instanceFields.size()]; instanceFields.toArray(instanceFieldsArray); } if (directMethods != null && directMethods.size() > 0) { Collections.sort(directMethods); directMethodsArray = new EncodedMethod[directMethods.size()]; directMethods.toArray(directMethodsArray); } if (virtualMethods != null && virtualMethods.size() > 0) { Collections.sort(virtualMethods); virtualMethodsArray = new EncodedMethod[virtualMethods.size()]; virtualMethods.toArray(virtualMethodsArray); } ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFieldsArray, instanceFieldsArray, directMethodsArray, virtualMethodsArray); return dexFile.ClassDataSection.intern(classDataItem); } /** {@inheritDoc} */ protected void readItem(Input in, ReadContext readContext) { staticFields = new EncodedField[in.readUnsignedLeb128()]; instanceFields = new EncodedField[in.readUnsignedLeb128()]; directMethods = new EncodedMethod[in.readUnsignedLeb128()]; virtualMethods = new EncodedMethod[in.readUnsignedLeb128()]; EncodedField previousEncodedField = null; for (int i=0; iClassDefItem that this ClassDataItem is associated with * @param classDefItem the ClassDefItem that this ClassDataItem is associated with */ protected void setParent(ClassDefItem classDefItem) { this.parent = classDefItem; } /** * @return the static fields for this class */ public EncodedField[] getStaticFields() { return staticFields; } /** * @return the instance fields for this class */ public EncodedField[] getInstanceFields() { return instanceFields; } /** * @return the direct methods for this class */ public EncodedMethod[] getDirectMethods() { return directMethods; } /** * @return the virtual methods for this class */ public EncodedMethod[] getVirtualMethods() { return virtualMethods; } public static class EncodedField implements Comparable { /** * The FieldIdItem that this EncodedField is associated with */ public final FieldIdItem field; /** * The access flags for this field */ public final int accessFlags; /** * Constructs a new EncodedField with the given values * @param field The FieldIdItem that this EncodedField is associated with * @param accessFlags The access flags for this field */ public EncodedField(FieldIdItem field, int accessFlags) { this.field = field; this.accessFlags = accessFlags; } /** * This is used internally to construct a new EncodedField while reading in a DexFile * @param dexFile The DexFile that is being read in * @param in the Input object to read the EncodedField from * @param previousEncodedField The previous EncodedField in the list containing this * EncodedField. */ private EncodedField(DexFile dexFile, Input in, EncodedField previousEncodedField) { int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); field = dexFile.FieldIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex); accessFlags = in.readUnsignedLeb128(); } /** * Writes the EncodedField to the given AnnotatedOutput object * @param out the AnnotatedOutput object to write to * @param previousEncodedField The previous EncodedField in the list containing this * EncodedField. */ private void writeTo(AnnotatedOutput out, EncodedField previousEncodedField) { int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); if (out.annotates()) { out.annotate("field: " + field.getFieldString()); out.writeUnsignedLeb128(field.getIndex() - previousIndex); out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForField(accessFlags)); out.writeUnsignedLeb128(accessFlags); }else { out.writeUnsignedLeb128(field.getIndex() - previousIndex); out.writeUnsignedLeb128(accessFlags); } } /** * Calculates the size of this EncodedField and returns the offset * immediately following it * @param offset the offset of this EncodedField in the DexFile * @param previousEncodedField The previous EncodedField in the list containing this * EncodedField. * @return the offset immediately following this EncodedField */ private int place(int offset, EncodedField previousEncodedField) { int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); offset += Leb128Utils.unsignedLeb128Size(field.getIndex() - previousIndex); offset += Leb128Utils.unsignedLeb128Size(accessFlags); return offset; } /** * Compares this EncodedField to another, based on the comparison of the associated * FieldIdItem * @param other The EncodedField to compare against * @return a standard integer comparison value indicating the relationship */ public int compareTo(EncodedField other) { return field.compareTo(other.field); } /** * @return true if this is a static field */ public boolean isStatic() { return (accessFlags & AccessFlags.STATIC.getValue()) != 0; } } public static class EncodedMethod implements Comparable { /** * The MethodIdItem that this EncodedMethod is associated with */ public final MethodIdItem method; /** * The access flags for this method */ public final int accessFlags; /** * The CodeItem containing the code for this method, or null if there is no code for this method * (i.e. an abstract method) */ public final CodeItem codeItem; /** * Constructs a new EncodedMethod with the given values * @param method The MethodIdItem that this EncodedMethod is associated with * @param accessFlags The access flags for this method * @param codeItem The CodeItem containing the code for this method, or null if there is no code * for this method (i.e. an abstract method) */ public EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem) { this.method = method; this.accessFlags = accessFlags; this.codeItem = codeItem; if (codeItem != null) { codeItem.setParent(this); } } /** * This is used internally to construct a new EncodedMethod while reading in a DexFile * @param dexFile The DexFile that is being read in * @param readContext a ReadContext object to hold information that is only needed while reading * in a file * @param in the Input object to read the EncodedMethod from * @param previousEncodedMethod The previous EncodedMethod in the list containing this * EncodedMethod. */ public EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod) { int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); method = dexFile.MethodIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex); accessFlags = in.readUnsignedLeb128(); codeItem = (CodeItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_CODE_ITEM, in.readUnsignedLeb128()); if (codeItem != null) { codeItem.setParent(this); } } /** * Writes the EncodedMethod to the given AnnotatedOutput object * @param out the AnnotatedOutput object to write to * @param previousEncodedMethod The previous EncodedMethod in the list containing this * EncodedMethod. */ private void writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod) { int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); if (out.annotates()) { out.annotate("method: " + method.getMethodString()); out.writeUnsignedLeb128(method.getIndex() - previousIndex); out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForMethod(accessFlags)); out.writeUnsignedLeb128(accessFlags); if (codeItem != null) { out.annotate("code_off: 0x" + codeItem.getOffset()); out.writeUnsignedLeb128(codeItem.getOffset()); } else { out.annotate("code_off: 0x0"); out.writeUnsignedLeb128(0); } }else { out.writeUnsignedLeb128(method.getIndex() - previousIndex); out.writeUnsignedLeb128(accessFlags); out.writeUnsignedLeb128(codeItem==null?0:codeItem.getOffset()); } } /** * Calculates the size of this EncodedMethod and returns the offset * immediately following it * @param offset the offset of this EncodedMethod in the DexFile * @param previousEncodedMethod The previous EncodedMethod in the list containing this * EncodedMethod. * @return the offset immediately following this EncodedField */ private int place(int offset, EncodedMethod previousEncodedMethod) { int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); offset += Leb128Utils.unsignedLeb128Size(method.getIndex() - previousIndex); offset += Leb128Utils.unsignedLeb128Size(accessFlags); offset += codeItem==null?1:Leb128Utils.unsignedLeb128Size(codeItem.getOffset()); return offset; } /** * Compares this EncodedMethod to another, based on the comparison of the associated * MethodIdItem * @param other The EncodedMethod to compare against * @return a standard integer comparison value indicating the relationship */ public int compareTo(EncodedMethod other) { return method.compareTo(other.method); } /** * @return true if this is a direct method */ public boolean isDirect() { return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() | AccessFlags.CONSTRUCTOR.getValue())) != 0); } } }