Change how the parent is determined for AnnotationDirectoryItem and ClassDataItem

This commit is contained in:
Ben Gruver 2012-09-25 19:52:04 -07:00
parent 097b40531b
commit a59fe7e523
3 changed files with 106 additions and 82 deletions

View File

@ -48,16 +48,6 @@ public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
@Nullable
private ParameterAnnotation[] parameterAnnotations;
/**
* typically each AnnotationDirectoryItem will have a distinct parent. The only case that isn't true is when
* the AnnotationDirectoryItem *only* contains class annotations, with no other type of annotation. In that
* case, the same AnnotationDirectoryItem could be referenced from multiple classes.
* This isn't a problem though, because this field is only used in compareTo to determine the sort order, which
* which knows it should handle a null value as a special case
*/
@Nullable
private ClassDefItem parent = null;
/**
* Creates a new uninitialized <code>AnnotationDirectoryItem</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
@ -211,8 +201,9 @@ public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
/** {@inheritDoc} */
protected void writeItem(AnnotatedOutput out) {
if (out.annotates()) {
if (!isInternable() && parent != null) {
out.annotate(0, parent.getClassType().getTypeDescriptor());
TypeIdItem parentType = getParentType();
if (parentType != null) {
out.annotate(0, parentType.getTypeDescriptor());
}
if (classAnnotations != null) {
out.annotate(4, "class_annotations_off: 0x" + Integer.toHexString(classAnnotations.getOffset()));
@ -303,29 +294,60 @@ public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
/** {@inheritDoc} */
public String getConciseIdentity() {
if (parent == null) {
TypeIdItem parentType = getParentType();
if (parentType == null) {
return "annotation_directory_item @0x" + Integer.toHexString(getOffset());
}
return "annotation_directory_item @0x" + Integer.toHexString(getOffset()) + " (" + parent.getClassType() + ")";
return "annotation_directory_item @0x" + Integer.toHexString(getOffset()) +
" (" + parentType.getTypeDescriptor() + ")";
}
/** {@inheritDoc} */
public int compareTo(AnnotationDirectoryItem o) {
Preconditions.checkNotNull(o);
if (!isInternable()) {
if (!o.isInternable()) {
Preconditions.checkState(parent != null && o.parent != null,
"Must call setParent before comparing AnnotationDirectoryItem instances");
return parent.compareTo(o.parent);
TypeIdItem parentType = getParentType();
TypeIdItem otherParentType = o.getParentType();
if (parentType != null) {
if (otherParentType != null) {
return parentType.compareTo(otherParentType);
}
return 1;
}
if (otherParentType != null) {
return -1;
}
if (classAnnotations != null) {
if (o.classAnnotations != null) {
return classAnnotations.compareTo(o.classAnnotations);
}
return 1;
}
return -1;
}
if (!o.isInternable()) {
return 1;
/**
* Returns the parent type for an AnnotationDirectoryItem that is guaranteed to have a single parent, or null
* for one that may be referenced by multiple classes.
*
* Specifically, the AnnotationDirectoryItem may be referenced by multiple classes if it has only class annotations,
* but not field/method/parameter annotations.
*
* @return The parent type for this AnnotationDirectoryItem, or null if it may have multiple parents
*/
@Nullable
public TypeIdItem getParentType() {
if (fieldAnnotations != null && fieldAnnotations.length > 0) {
return fieldAnnotations[0].field.getContainingClass();
}
return classAnnotations.compareTo(o.classAnnotations);
if (methodAnnotations != null && methodAnnotations.length > 0) {
return methodAnnotations[0].method.getContainingClass();
}
if (parameterAnnotations != null && parameterAnnotations.length > 0) {
return parameterAnnotations[0].method.getContainingClass();
}
return null;
}
/**
@ -425,6 +447,17 @@ public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
return parameterAnnotations[index].annotationSet;
}
/**
*
*/
public int getClassAnnotationCount() {
if (classAnnotations == null) {
return 0;
}
AnnotationItem[] annotations = classAnnotations.getAnnotations();
return annotations.length;
}
/**
* @return The number of field annotations in this <code>AnnotationDirectoryItem</code>
*/
@ -455,41 +488,18 @@ public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
return parameterAnnotations.length;
}
/**
* @return true if this <code>AnnotationDirectoryItem</code> is internable. It is only internable if it has
* only class annotations, but no field, method or parameter annotations
*/
private boolean isInternable() {
return classAnnotations != null &&
(fieldAnnotations == null || fieldAnnotations.length == 0) &&
(methodAnnotations == null || methodAnnotations.length == 0) &&
(parameterAnnotations == null || parameterAnnotations.length == 0);
}
/**
* Sets the <code>ClassDefItem</code> that this <code>AnnotationDirectoryItem</code> is associated with.
*
* @param classDefItem the <code>ClassDefItem</code> that this <code>AnnotationDirectoryItem</code> is associated
* with.
*/
protected void setParent(ClassDefItem classDefItem) {
// If this AnnotationDirectoryItem is internable, then setParent may be called multiple times, because it is
// reused for multiple classes. In this case, the parent field isn't used, so it doesn't matter if we overwrite
// it.
// If, on the other hand, it is not internable, we want to make sure that only a single parent is set. parent
// should either be null, or be equal to the new parent
Preconditions.checkState(this.isInternable() || (parent == null || parent.equals(classDefItem)));
this.parent = classDefItem;
}
@Override
public int hashCode() {
// An instance is internable only if it has only class annotations, but no other type of annotation
if (!isInternable()) {
return super.hashCode();
// If the item has a single parent, we can use the re-use the identity (hash) of that parent
TypeIdItem parentType = getParentType();
if (parentType != null) {
return parentType.hashCode();
}
if (classAnnotations != null) {
return classAnnotations.hashCode();
}
return 0;
}
@Override
public boolean equals(Object o) {

View File

@ -45,9 +45,6 @@ public class ClassDataItem extends Item<ClassDataItem> {
@Nullable
private EncodedMethod[] virtualMethods = null;
@Nullable
private ClassDefItem parent = null;
/**
* Creates a new uninitialized <code>ClassDataItem</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
@ -384,14 +381,17 @@ public class ClassDataItem extends Item<ClassDataItem> {
/** {@inheritDoc} */
public String getConciseIdentity() {
if (parent == null) {
TypeIdItem parentType = getParentType();
if (parentType == null) {
return "class_data_item @0x" + Integer.toHexString(getOffset());
}
return "class_data_item @0x" + Integer.toHexString(getOffset()) + " (" + parent.getClassType() +")";
return "class_data_item @0x" + Integer.toHexString(getOffset()) + " (" + parentType.getTypeDescriptor() +")";
}
/** {@inheritDoc} */
public int compareTo(ClassDataItem other) {
Preconditions.checkNotNull(other);
// An empty CodeDataItem may be shared by multiple ClassDefItems, so we can't use parent in this case
if (isEmpty()) {
if (other.isEmpty()) {
@ -403,25 +403,54 @@ public class ClassDataItem extends Item<ClassDataItem> {
return 1;
}
if (parent == null) {
if (other.parent == null) {
TypeIdItem parentType = getParentType();
TypeIdItem otherParentType= other.getParentType();
if (parentType == null) {
if (otherParentType == null) {
return 0;
}
return -1;
}
if (other.parent == null) {
if (otherParentType == null) {
return 1;
}
return parent.compareTo(other.parent);
return parentType.compareTo(otherParentType);
}
@Override
public int hashCode() {
// If the item has a single parent, we can use the re-use the identity (hash) of that parent
TypeIdItem parentType = getParentType();
if (parentType != null) {
return parentType.hashCode();
}
return 0;
}
/**
* Sets the <code>ClassDefItem</code> that this <code>ClassDataItem</code> is associated with
* @param classDefItem the <code>ClassDefItem</code> that this <code>ClassDataItem</code> is associated with
* Returns the parent type for a non-empty ClassDataItem, or null for an empty one (which could be referenced by
* multiple ClassDefItem parents)
*
* Specifically, the AnnotationDirectoryItem may be referenced by multiple classes if it has only class annotations,
* but not field/method/parameter annotations.
*
* @return The parent type for this AnnotationDirectoryItem, or null if it may have multiple parents
*/
protected void setParent(ClassDefItem classDefItem) {
Preconditions.checkState(parent == null || parent.compareTo(classDefItem) == 0 || isEmpty());
parent = classDefItem;
@Nullable
public TypeIdItem getParentType() {
if (staticFields != null && staticFields.length > 0) {
return staticFields[0].field.getContainingClass();
}
if (instanceFields != null && instanceFields.length > 0) {
return instanceFields[0].field.getContainingClass();
}
if (directMethods != null && directMethods.length > 0) {
return directMethods[0].method.getContainingClass();
}
if (virtualMethods != null && virtualMethods.length > 0) {
return virtualMethods[0].method.getContainingClass();
}
return null;
}
/**

View File

@ -28,7 +28,6 @@
package org.jf.dexlib;
import com.google.common.base.Preconditions;
import org.jf.dexlib.EncodedValue.ArrayEncodedSubValue;
import org.jf.dexlib.EncodedValue.EncodedValue;
import org.jf.dexlib.Util.AccessFlags;
@ -88,13 +87,6 @@ public class ClassDefItem extends Item<ClassDefItem> {
this.annotations = annotations;
this.classData = classData;
this.staticFieldInitializers = staticFieldInitializers;
if (classData != null) {
classData.setParent(this);
}
if (annotations != null) {
annotations.setParent(this);
}
}
/**
@ -145,13 +137,6 @@ public class ClassDefItem extends Item<ClassDefItem> {
classData = (ClassDataItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CLASS_DATA_ITEM, in.readInt());
staticFieldInitializers = (EncodedArrayItem)readContext.getOptionalOffsettedItemByOffset(
ItemType.TYPE_ENCODED_ARRAY_ITEM, in.readInt());
if (classData != null) {
classData.setParent(this);
}
if (annotations != null) {
annotations.setParent(this);
}
}
/** {@inheritDoc} */
@ -344,7 +329,7 @@ public class ClassDefItem extends Item<ClassDefItem> {
*/
private static EncodedArrayItem makeStaticFieldInitializersItem(DexFile dexFile,
@Nonnull List<StaticFieldInitializer> staticFieldInitializers) {
if (staticFieldInitializers == null || staticFieldInitializers.size() == 0) {
if (staticFieldInitializers.size() == 0) {
return null;
}