diff --git a/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java b/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java index 708e66e3..a6750611 100644 --- a/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java +++ b/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java @@ -33,9 +33,7 @@ import org.jf.dexlib.Util.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; public class ClassDataItem extends Item { @Nullable @@ -95,27 +93,71 @@ public class ClassDataItem extends Item { EncodedMethod[] virtualMethodsArray = null; if (staticFields != null && staticFields.size() > 0) { - staticFieldsArray = new EncodedField[staticFields.size()]; - staticFieldsArray = staticFields.toArray(staticFieldsArray); - Arrays.sort(staticFieldsArray); + SortedSet staticFieldsSet = new TreeSet(); + for (EncodedField staticField: staticFields) { + if (staticFieldsSet.contains(staticField)) { + System.err.println(String.format("Ignoring duplicate static field definition: %s", + staticField.field.getFieldString())); + continue; + } + staticFieldsSet.add(staticField); + } + + staticFieldsArray = new EncodedField[staticFieldsSet.size()]; + staticFieldsArray = staticFieldsSet.toArray(staticFieldsArray); } if (instanceFields != null && instanceFields.size() > 0) { - instanceFieldsArray = new EncodedField[instanceFields.size()]; - instanceFieldsArray = instanceFields.toArray(instanceFieldsArray); - Arrays.sort(instanceFieldsArray); + SortedSet instanceFieldsSet = new TreeSet(); + for (EncodedField instanceField: instanceFields) { + if (instanceFieldsSet.contains(instanceField)) { + System.err.println(String.format("Ignoring duplicate instance field definition: %s", + instanceField.field.getFieldString())); + continue; + } + instanceFieldsSet.add(instanceField); + } + + instanceFieldsArray = new EncodedField[instanceFieldsSet.size()]; + instanceFieldsArray = instanceFieldsSet.toArray(instanceFieldsArray); } + TreeSet directMethodSet = new TreeSet(); + if (directMethods != null && directMethods.size() > 0) { - directMethodsArray = new EncodedMethod[directMethods.size()]; - directMethodsArray = directMethods.toArray(directMethodsArray); - Arrays.sort(directMethodsArray); + for (EncodedMethod directMethod: directMethods) { + if (directMethodSet.contains(directMethod)) { + System.err.println(String.format("Ignoring duplicate direct method definition: %s", + directMethod.method.getMethodString())); + continue; + } + directMethodSet.add(directMethod); + } + + directMethodsArray = new EncodedMethod[directMethodSet.size()]; + directMethodsArray = directMethodSet.toArray(directMethodsArray); } if (virtualMethods != null && virtualMethods.size() > 0) { - virtualMethodsArray = new EncodedMethod[virtualMethods.size()]; - virtualMethodsArray = virtualMethods.toArray(virtualMethodsArray); - Arrays.sort(virtualMethodsArray); + TreeSet virtualMethodSet = new TreeSet(); + for (EncodedMethod virtualMethod: virtualMethods) { + if (directMethodSet.contains(virtualMethod)) { + // If both a direct and virtual definition is present, dalvik's behavior seems to be undefined, + // so we can't gracefully handle this case, like we can if the duplicates are all direct or all + // virtual -- in which case, we ignore all but the first definition + throw new RuntimeException(String.format("Duplicate direct+virtual method definition: %s", + virtualMethod.method.getMethodString())); + } + if (virtualMethodSet.contains(virtualMethod)) { + System.err.println(String.format("Ignoring duplicate virtual method definition: %s", + virtualMethod.method.getMethodString())); + continue; + } + virtualMethodSet.add(virtualMethod); + } + + virtualMethodsArray = new EncodedMethod[virtualMethodSet.size()]; + virtualMethodsArray = virtualMethodSet.toArray(virtualMethodsArray); } ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFieldsArray, instanceFieldsArray, @@ -596,6 +638,19 @@ public class ClassDataItem extends Item { return field.compareTo(other.field); } + /** + * Determines if this EncodedField is equal to other, based on the equality of the associated + * FieldIdItem + * @param other The EncodedField to test for equality + * @return true if other is equal to this instance, otherwise false + */ + public boolean equals(Object other) { + if (other instanceof EncodedField) { + return compareTo((EncodedField)other) == 0; + } + return false; + } + /** * @return true if this is a static field */ @@ -717,6 +772,19 @@ public class ClassDataItem extends Item { return method.compareTo(other.method); } + /** + * Determines if this EncodedMethod is equal to other, based on the equality of the associated + * MethodIdItem + * @param other The EncodedMethod to test for equality + * @return true if other is equal to this instance, otherwise false + */ + public boolean equals(Object other) { + if (other instanceof EncodedMethod) { + return compareTo((EncodedMethod)other) == 0; + } + return false; + } + /** * @return true if this is a direct method */