blob: df7b2a7b985ebf68169b3bc9a9cb04b19aa1385d [file] [log] [blame]
package com.android.jack;
import com.android.jack.dx.rop.code.AccessFlags;
import org.jf.dexlib.AnnotationDirectoryItem;
import org.jf.dexlib.AnnotationDirectoryItem.MethodAnnotation;
import org.jf.dexlib.AnnotationItem;
import org.jf.dexlib.AnnotationSetItem;
import org.jf.dexlib.AnnotationSetRefList;
import org.jf.dexlib.ClassDataItem;
import org.jf.dexlib.ClassDataItem.EncodedField;
import org.jf.dexlib.ClassDataItem.EncodedMethod;
import org.jf.dexlib.ClassDefItem;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.FieldIdItem;
import org.jf.dexlib.ItemType;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.ProtoIdItem;
import org.jf.dexlib.Section;
import org.jf.dexlib.StringIdItem;
import org.jf.dexlib.TypeIdItem;
import org.jf.dexlib.TypeListItem;
import org.jf.dexlib.EncodedValue.AnnotationEncodedSubValue;
import org.jf.dexlib.EncodedValue.ArrayEncodedSubValue;
import org.jf.dexlib.EncodedValue.ArrayEncodedValue;
import org.jf.dexlib.EncodedValue.EncodedValue;
import org.jf.dexlib.EncodedValue.EnumEncodedValue;
import org.jf.dexlib.EncodedValue.IntEncodedValue;
import org.jf.dexlib.EncodedValue.MethodEncodedValue;
import org.jf.dexlib.EncodedValue.StringEncodedValue;
import org.jf.dexlib.EncodedValue.TypeEncodedValue;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* This tool compares 2 dex files. The candidate is compared to the reference dex file, and MUST
* have at least all the features present in the reference. (Class, super class, fields, methods)
*/
public class DexAnnotationsComparator {
@Nonnull
private static final String MEMBER_CLASSES_DESCRIPTOR = "Ldalvik/annotation/MemberClasses;";
@Nonnull
private final Logger logger;
@Nonnull
private final DexFile referenceDexFile;
@Nonnull
private final DexFile candidateDexFile;
@Nonnull
private static final Level ERROR_LEVEL = Level.SEVERE;
@Nonnull
private static final Level DEBUG_LEVEL = Level.FINE;
private static final boolean IGNORE_ANONYMOUS_CLASSES = true;
private static final boolean TOLERATE_MISSING_SYNTHETICS = true;
private static final boolean TOLERATE_MISSING_INITS = true;
private static final boolean TOLERATE_MISSING_CLINITS = true;
public DexAnnotationsComparator(
@Nonnull File referenceFile, @Nonnull File candidateFile) throws IOException {
logger = Logger.getLogger(this.getClass().getName());
logger.setLevel(ERROR_LEVEL);
referenceDexFile = new DexFile(referenceFile);
candidateDexFile = new DexFile(candidateFile);
}
@SuppressWarnings("unchecked")
public void compare() throws DifferenceFoundException {
/* Reuse delegate instance for maximum memory saving */
CompareElementAnnotation compareElementAnnotation =
new CompareElementAnnotation(logger);
/* build a lookup table for candidate classes */
HashMap<String, ClassDefItem> candidateClassDefItemLookUpTable =
new HashMap<String, ClassDefItem>();
Section<ClassDefItem> candidateClassDefSection =
candidateDexFile.getSectionForType(ItemType.TYPE_CLASS_DEF_ITEM);
for (ClassDefItem classDefItem : candidateClassDefSection.getItems()) {
candidateClassDefItemLookUpTable.put(
classDefItem.getClassType().getTypeDescriptor(), classDefItem);
}
Section<ClassDefItem> classDefSection =
referenceDexFile.getSectionForType(ItemType.TYPE_CLASS_DEF_ITEM);
for (ClassDefItem classDefItem : classDefSection.getItems()) {
ClassDefItem candidateClassDefItem =
candidateClassDefItemLookUpTable.get(classDefItem.getClassType().getTypeDescriptor());
String className = classDefItem.getClassType().getTypeDescriptor();
if (!IGNORE_ANONYMOUS_CLASSES || !isAnonymousTypeName(className)) {
/* class */
if (candidateClassDefItem != null) {
logger.log(DEBUG_LEVEL, "Class {0} OK", className);
int accessFlags = classDefItem.getAccessFlags();
boolean synthetic = isSynthetic(accessFlags);
if (synthetic && TOLERATE_MISSING_SYNTHETICS) {
continue;
}
/* check ClassData field */
ClassDataItem classDataItem = classDefItem.getClassData();
ClassDataItem candidateClassDataItem = candidateClassDefItem.getClassData();
if (classDataItem == null && candidateClassDataItem == null) {
logger.log(DEBUG_LEVEL, "ClassData of {0} OK: both are null", className);
} else if (classDataItem == null || candidateClassDataItem == null) {
String errorString =
((classDataItem == null) ? ("reference's is null and not candidate's")
: ("candidate's is null and not reference's"));
throw new DifferenceFoundException(
"ClassDatas of '" + className + "' do not match: " + errorString);
} else {
/* Annotation pre check */
boolean doCheckAnnotations = false;
AnnotationDirectoryItem annotationDirectoryItem = classDefItem.getAnnotations();
AnnotationDirectoryItem candidateAnnotationDirectoryItem =
candidateClassDefItem.getAnnotations();
if (annotationDirectoryItem == null && candidateAnnotationDirectoryItem == null) {
logger.log(DEBUG_LEVEL, "AnnotationDirectoryItem of {0} OK: both are null", className);
} else if (annotationDirectoryItem == null || candidateAnnotationDirectoryItem == null) {
String errorString =
((annotationDirectoryItem == null) ? ("reference's is null and not candidate's")
: ("candidate's is null and not reference's"));
logger.log(ERROR_LEVEL, "AnnotationDirectoryItem of {0} NOK: {1}",
new Object[] {className, errorString});
if (annotationDirectoryItem != null) {
boolean containsOnlyAnnotationsOnSyntheticMethods = true;
for (MethodAnnotation anno : annotationDirectoryItem.getMethodAnnotations()) {
containsOnlyAnnotationsOnSyntheticMethods =
containsOnlyAnnotationsOnSyntheticMethods
&& isSynthetic(getMethodAccessFlags(classDataItem, anno.method));
}
if (containsOnlyAnnotationsOnSyntheticMethods) {
continue;
}
}
throw new DifferenceFoundException(
"AnnotationDirectoryItems of '" + className + "' do not match: " + errorString);
} else {
logger.log(DEBUG_LEVEL, "AnnotationDirectoryItem of {0}: not null", className);
doCheckAnnotations = true;
}
/* Class Annotations */
{
if (doCheckAnnotations) {
checkClassAnnotations(annotationDirectoryItem, candidateAnnotationDirectoryItem,
compareElementAnnotation, className);
}
} /* class annotation */
/* Field Annotations */
{
if (doCheckAnnotations) {
compareElementAnnotation.reset();
compareElementAnnotation.isCandidate = true;
assert candidateAnnotationDirectoryItem != null;
processFields(compareElementAnnotation, candidateAnnotationDirectoryItem,
candidateClassDataItem.getStaticFields());
processFields(compareElementAnnotation, candidateAnnotationDirectoryItem,
candidateClassDataItem.getInstanceFields());
compareElementAnnotation.isCandidate = false;
assert annotationDirectoryItem != null;
processFields(compareElementAnnotation, annotationDirectoryItem,
classDataItem.getStaticFields());
processFields(compareElementAnnotation, annotationDirectoryItem,
classDataItem.getInstanceFields());
}
}
/* Method and parameter Annotations */
{
if (doCheckAnnotations) {
compareElementAnnotation.reset();
compareElementAnnotation.isCandidate = true;
assert candidateAnnotationDirectoryItem != null;
processMethods(compareElementAnnotation, candidateAnnotationDirectoryItem,
candidateClassDataItem.getDirectMethods());
processMethods(compareElementAnnotation, candidateAnnotationDirectoryItem,
candidateClassDataItem.getVirtualMethods());
compareElementAnnotation.isCandidate = false;
assert annotationDirectoryItem != null;
compareElementAnnotation.classData = classDataItem;
processMethods(compareElementAnnotation, annotationDirectoryItem,
classDataItem.getDirectMethods());
processMethods(compareElementAnnotation, annotationDirectoryItem,
classDataItem.getVirtualMethods());
}
}
} /* check ClassData field */
classDataItem = null;
candidateClassDataItem = null;
} else /* Class */ {
logger.log(ERROR_LEVEL, "Class {0} NOK: missing", className);
if (!TOLERATE_MISSING_SYNTHETICS || !isSynthetic(classDefItem.getAccessFlags())) {
throw new DifferenceFoundException("Class " + className + " was not found in candidate.");
}
}
}
} /* for each class */
}
private void processMethods(@Nonnull CompareElementAnnotation compareElementAnnotation,
@Nonnull AnnotationDirectoryItem annotationDirectoryItem,
@Nonnull List<EncodedMethod> methods) {
for (EncodedMethod method : methods) {
AnnotationSetItem methodAnnotations =
annotationDirectoryItem.getMethodAnnotations(method.method);
if (methodAnnotations != null) {
compareElementAnnotation.processMethodAnnotations(method.method, methodAnnotations);
}
AnnotationSetRefList parameterAnnotations =
annotationDirectoryItem.getParameterAnnotations(method.method);
if (parameterAnnotations != null) {
compareElementAnnotation.processParameterAnnotations(method.method, parameterAnnotations);
}
}
}
private void processFields(@Nonnull CompareElementAnnotation compareElementAnnotation,
@Nonnull AnnotationDirectoryItem annotationDirectoryItem,
@Nonnull List<EncodedField> fields) {
for (EncodedField field : fields) {
AnnotationSetItem fieldAnnotations = annotationDirectoryItem.getFieldAnnotations(field.field);
if (fieldAnnotations != null) {
compareElementAnnotation.processFieldAnnotations(field.field, fieldAnnotations);
}
}
}
private void checkClassAnnotations(AnnotationDirectoryItem annotationDirectoryItem,
AnnotationDirectoryItem candidateAnnotationDirectoryItem,
CompareElementAnnotation compareElementAnnotationDelegate, String className)
throws DifferenceFoundException {
assert annotationDirectoryItem != null;
assert candidateAnnotationDirectoryItem != null;
AnnotationSetItem classAnnotations = annotationDirectoryItem.getClassAnnotations();
AnnotationSetItem candidateClassAnnotations =
candidateAnnotationDirectoryItem.getClassAnnotations();
if (classAnnotations == null && candidateClassAnnotations == null) {
logger.log(DEBUG_LEVEL, "ClassAnnotations of {0} OK: both are null", className);
} else if (classAnnotations == null || candidateClassAnnotations == null) {
String errorString =
((classAnnotations == null) ? ("reference's is null and not candidate's")
: ("candidate's is null and not reference's"));
logger.log(ERROR_LEVEL, "ClassAnnotations of {0} NOK: {1}",
new Object[] {className, errorString});
boolean onlyAnonymousMemberClassesAnnot = true;
if (classAnnotations != null) {
AnnotationItem[] annots = classAnnotations.getAnnotations();
for (AnnotationItem annot : annots) {
TypeIdItem type = annot.getEncodedAnnotation().annotationType;
boolean isMemberClassesAnnot =
type.getTypeDescriptor().equals(MEMBER_CLASSES_DESCRIPTOR);
if (isMemberClassesAnnot) {
onlyAnonymousMemberClassesAnnot &=
checkMemberClassesAnnotationOnlyHasAnonymousOrSynthetics(
annot.getEncodedAnnotation());
} else {
onlyAnonymousMemberClassesAnnot = false;
}
if (!onlyAnonymousMemberClassesAnnot) {
break;
}
}
}
if (!onlyAnonymousMemberClassesAnnot) {
throw new DifferenceFoundException(
"ClassAnnotations of '" + className + "' do not match: " + errorString);
} else {
return;
}
} else {
compareElementAnnotationDelegate.reset();
compareElementAnnotationDelegate.isCandidate = true;
compareElementAnnotationDelegate.processGeneric(
"class " + className,
candidateClassAnnotations);
compareElementAnnotationDelegate.isCandidate = false;
compareElementAnnotationDelegate.processGeneric(
"class " + className, classAnnotations);
}
}
private static boolean checkMemberClassesAnnotationOnlyHasAnonymousOrSynthetics(
AnnotationEncodedSubValue encodedAnnotation) {
boolean result = true;
for (EncodedValue value : encodedAnnotation.values) {
for (EncodedValue subvalue : ((ArrayEncodedValue) value).values) {
TypeIdItem typeId = ((TypeEncodedValue) subvalue).value;
result &= isAnonymousTypeName(typeId.getTypeDescriptor());
if (!result) {
break;
}
}
}
return result;
}
/**
* Compares annotations for each element (field method) in reference. Please re use instance for
* performance's sake (call reset() beforehand)
*
*/
private static class CompareElementAnnotation {
public boolean isCandidate = false;
@CheckForNull
public ClassDataItem classData = null;
@Nonnull
private final HashMap<String, AnnotationSetItem> candidateElementAnnotations =
new HashMap<String, AnnotationSetItem>();
@Nonnull
private final Logger logger;
public CompareElementAnnotation(Logger logger) {
this.logger = logger;
}
public void processGeneric(@Nonnull String elementString,
@Nonnull AnnotationSetItem annotations) throws DifferenceFoundException {
if (isCandidate) {
/* fill lookup table */
candidateElementAnnotations.put(elementString, annotations);
} else {
/* reference : compare against lookup table */
AnnotationSetItem candidateAnnotationsForField =
candidateElementAnnotations.get(elementString);
AnnotationItem[] candidatesAnnots =
(candidateAnnotationsForField != null) ? (candidateAnnotationsForField.getAnnotations())
: (new AnnotationItem[] {});
AnnotationItem[] refAnnots = annotations.getAnnotations();
for (int i = 0; i < refAnnots.length; i++) {
//for (AnnotationItem annot : refAnnots) {
AnnotationItem annot = refAnnots[i];
// if the annotation is a MemberClass annotation that only contains anonymous classes,
// it may not be in our candidate so we ignore it
boolean isMemberClassAnnotation = annot
.getEncodedAnnotation().annotationType.getTypeDescriptor()
.equals(MEMBER_CLASSES_DESCRIPTOR);
if (IGNORE_ANONYMOUS_CLASSES && isMemberClassAnnotation) {
if (checkMemberClassesAnnotationOnlyHasAnonymousOrSynthetics(annot
.getEncodedAnnotation())) {
continue;
}
}
boolean isAnnotFound = false;
for (AnnotationItem candidateAnnot : candidatesAnnots) {
/* found the same annotation for this element? (based on its type) */
if (compareTypeIds(annot.getEncodedAnnotation().annotationType,
candidateAnnot.getEncodedAnnotation().annotationType)) {
checkEncodedAnnotations(annot.getEncodedAnnotation(),
candidateAnnot.getEncodedAnnotation(), elementString);
isAnnotFound = true;
break;
}
}
if (!isAnnotFound) {
if ((!TOLERATE_MISSING_INITS || !elementString.contains("<init>")) &&
(!TOLERATE_MISSING_CLINITS || !elementString.contains("<clinit>"))) {
throw new DifferenceFoundException("NOK: for " + elementString
+ ": Could not find annotation "
+ annot.getEncodedAnnotation().annotationType.getTypeDescriptor()
+ " in candidate.");
}
}
}
}
}
private boolean compareValues(EncodedValue encodedValue, EncodedValue candEncodedValue,
String elementString, String type, String name) throws DifferenceFoundException {
boolean isEqual = false;
if (encodedValue.getValueType().value == candEncodedValue.getValueType().value) {
if (encodedValue instanceof StringEncodedValue) {
isEqual = ((StringEncodedValue) encodedValue).value.getStringValue()
.equals(((StringEncodedValue) candEncodedValue).value.getStringValue());
} else if (encodedValue instanceof TypeEncodedValue) {
isEqual = compareTypeIds(
((TypeEncodedValue) encodedValue).value, ((TypeEncodedValue) candEncodedValue).value);
} else if (encodedValue instanceof EnumEncodedValue) {
isEqual = compareFieldIds(
((EnumEncodedValue) encodedValue).value, ((EnumEncodedValue) candEncodedValue).value);
} else if (encodedValue instanceof MethodEncodedValue) {
isEqual = compareMethodIds(((MethodEncodedValue) encodedValue).value,
((MethodEncodedValue) candEncodedValue).value);
} else if (encodedValue instanceof AnnotationEncodedSubValue) {
AnnotationEncodedSubValue encodedAnnotation =
(AnnotationEncodedSubValue) encodedValue;
AnnotationEncodedSubValue candEncodedAnnotation =
(AnnotationEncodedSubValue) candEncodedValue;
boolean sameAnnotationType = compareTypeIds(
encodedAnnotation.annotationType, candEncodedAnnotation.annotationType);
if (sameAnnotationType) {
checkEncodedAnnotations(encodedAnnotation, candEncodedAnnotation, elementString);
isEqual = true;
} else {
throw new AssertionError("sub annotation types do not match");
}
} else if (encodedValue instanceof ArrayEncodedSubValue) {
ArrayEncodedSubValue arrayEncodedValue = (ArrayEncodedSubValue) encodedValue;
ArrayEncodedSubValue candArrayEncodedValue = (ArrayEncodedSubValue) candEncodedValue;
isEqual = true;
boolean isMemberClass = type.equals(MEMBER_CLASSES_DESCRIPTOR);
List<EncodedValue> refEncodedValues = Arrays.asList(arrayEncodedValue.values);
List<EncodedValue> candEncodedValues = Arrays.asList(candArrayEncodedValue.values);
Collections.sort(refEncodedValues);
Collections.sort(candEncodedValues);
Iterator<EncodedValue> refEncodedValuesIterator = refEncodedValues.iterator();
Iterator<EncodedValue> candEncodedValuesIterator = candEncodedValues.iterator();
while (refEncodedValuesIterator.hasNext()) {
if (!candEncodedValuesIterator.hasNext()) {
isEqual = false;
break;
}
EncodedValue refValue = refEncodedValuesIterator.next();
// our reference may have synthetic anonymous classes that we should ignore
if (isMemberClass) {
TypeEncodedValue typeRefValue = (TypeEncodedValue) refValue;
if (isAnonymousTypeName(typeRefValue.value.getTypeDescriptor())) {
continue;
}
}
EncodedValue candValue = candEncodedValuesIterator.next();
isEqual = compareValues(refValue, candValue, elementString, type, name);
if (!isEqual) {
break;
}
}
if (candEncodedValuesIterator.hasNext()) {
isEqual = false;
}
} else {
isEqual = encodedValue.compareTo(candEncodedValue) == 0;
}
if (!isEqual) {
// print access flags of InnerClass annotations
if (type.equals("Ldalvik/annotation/InnerClass;") && name.equals("accessFlags")) {
int flags = ((IntEncodedValue) encodedValue).value;
int candFlags = ((IntEncodedValue) candEncodedValue).value;
logger.log(DEBUG_LEVEL, "Difference in access flags of InnerClass annotations of "
+ elementString + ": ref=0x" + Integer.toHexString(flags) + " - cand=0x"
+ Integer.toHexString(candFlags));
}
throw new DifferenceFoundException("NOK: for " + elementString + ":annotation "
+ type
+ ", name = " + name
+ " is present but values differ");
}
} else {
throw new DifferenceFoundException("NOK: for " + elementString + ":annotation "
+ type
+ ", name = " + name
+ ", encoded values have different types");
}
return isEqual;
}
private boolean compareMethodIds(MethodIdItem value, MethodIdItem value2) {
boolean isEqual = compareTypeIds(value.getContainingClass(), value2.getContainingClass());
isEqual &= compareProtoIds(value.getPrototype(), value2.getPrototype());
isEqual &=
value.getMethodName().getStringValue().equals(value2.getMethodName().getStringValue());
return isEqual;
}
private boolean compareProtoIds(ProtoIdItem value, ProtoIdItem value2) {
boolean isEqual = false;
if (value == null && value2 == null) {
isEqual = true;
} else if (value != null && value2 != null) {
if (compareTypeIds(value.getReturnType(), value2.getReturnType())) {
TypeListItem tyleList = value.getParameters();
TypeListItem tyleList2 = value2.getParameters();
if (tyleList == null && tyleList2 == null) {
isEqual = true;
} else if (tyleList != null && tyleList2 != null) {
if (tyleList.getTypeCount() == tyleList2.getTypeCount()) {
Iterator<TypeIdItem> tyleListIter = tyleList.getTypes().iterator();
Iterator<TypeIdItem> tyleListIter2 = tyleList2.getTypes().iterator();
isEqual = true;
while (isEqual && tyleListIter.hasNext()) {
isEqual &= compareTypeIds(tyleListIter.next(), tyleListIter2.next());
}
}
}
}
}
return isEqual;
}
private boolean compareFieldIds(FieldIdItem value, FieldIdItem value2) {
boolean isEqual = compareTypeIds(value.getContainingClass(), value2.getContainingClass());
isEqual &= compareTypeIds(value.getFieldType(), value2.getFieldType());
isEqual &=
value.getFieldName().getStringValue().equals(value2.getFieldName().getStringValue());
return isEqual;
}
private boolean compareTypeIds(TypeIdItem annotationType, TypeIdItem annotationType2) {
return annotationType.getTypeDescriptor().equals(annotationType2.getTypeDescriptor());
}
private void checkEncodedAnnotations(AnnotationEncodedSubValue encodedAnnotation,
AnnotationEncodedSubValue encodedAnnotation2, String elementString)
throws DifferenceFoundException {
assert compareTypeIds(encodedAnnotation.annotationType, encodedAnnotation2.annotationType);
/* check every name and values for this annotation */
for (int i = 0; i < encodedAnnotation.names.length; i++) {
StringIdItem name = encodedAnnotation.names[i];
boolean isAnnotNameFound = false;
for (int j = 0; !isAnnotNameFound && j < encodedAnnotation2.names.length; j++) {
StringIdItem candidateName = encodedAnnotation2.names[j];
if (name.getStringValue().equals(candidateName.getStringValue())) {
EncodedValue subSubValue = encodedAnnotation.values[i];
EncodedValue candSubSubValue = encodedAnnotation2.values[j];
boolean isEqual = compareValues(subSubValue, candSubSubValue, elementString,
encodedAnnotation.annotationType.getTypeDescriptor(), name.getStringValue());
if (!isEqual) {
throw new DifferenceFoundException("NOK: for " + elementString + ":annotation "
+ encodedAnnotation.annotationType.getTypeDescriptor()
+ ", value type differs : reference = "
+ encodedAnnotation.getValueType().toString() + ", candidate = "
+ encodedAnnotation2.getValueType().toString());
}
isAnnotNameFound = true;
break; // We found the right name, stop searching
}
}
if (!isAnnotNameFound) {
throw new DifferenceFoundException("NOK: for " + elementString + ": Could not find name "
+ name.getStringValue() + " for annotation "
+ encodedAnnotation.annotationType.getTypeDescriptor() + " in candidate.");
}
}
}
public void processFieldAnnotations(FieldIdItem field, AnnotationSetItem fieldAnnotations) {
try {
processGeneric("field " + field.getFieldString(), fieldAnnotations);
} catch (DifferenceFoundException e) {
//TODO
throw new RuntimeException(e);
}
}
public void processMethodAnnotations(MethodIdItem method, AnnotationSetItem annotations) {
try {
String elementString = "method " + method.getMethodString();
if (isCandidate) {
/* fill lookup table */
candidateElementAnnotations.put(elementString, annotations);
} else {
assert classData != null;
boolean synthetic = isSynthetic(getMethodAccessFlags(classData, method));
/* reference : compare against lookup table */
AnnotationSetItem candidateAnnotationsForField =
candidateElementAnnotations.get(elementString);
AnnotationItem[] candidatesAnnots =
(candidateAnnotationsForField != null) ? (candidateAnnotationsForField
.getAnnotations())
: (new AnnotationItem[] {});
AnnotationItem[] refAnnots = annotations.getAnnotations();
for (AnnotationItem annot : refAnnots) {
boolean isAnnotFound = false;
for (AnnotationItem candidateAnnot : candidatesAnnots) {
/* found the same annotation for this element? (based on its type) */
if (compareTypeIds(annot.getEncodedAnnotation().annotationType,
candidateAnnot.getEncodedAnnotation().annotationType)) {
checkEncodedAnnotations(annot.getEncodedAnnotation(),
candidateAnnot.getEncodedAnnotation(), elementString);
isAnnotFound = true;
break;
}
}
//TODO
if (!isAnnotFound) {
if ((!TOLERATE_MISSING_SYNTHETICS || !synthetic) &&
(!TOLERATE_MISSING_INITS || !elementString.contains("<init>")) &&
(!TOLERATE_MISSING_CLINITS || !elementString.contains("<clinit>"))) {
throw new DifferenceFoundException("NOK: for " + elementString
+ ": Could not find annotation "
+ annot.getEncodedAnnotation().annotationType.getTypeDescriptor()
+ " in candidate.");
}
}
}
}
} catch (DifferenceFoundException e) {
//TODO
throw new RuntimeException(e);
}
}
public void processParameterAnnotations(
MethodIdItem method, AnnotationSetRefList parameterAnnotations) {
int cptParam = 0;
for (AnnotationSetItem asi : parameterAnnotations.getAnnotationSets()) {
try {
processGeneric("param " + cptParam + " " + method.getMethodString(), asi);
} catch (DifferenceFoundException e) {
// TODO
throw new RuntimeException(e);
}
}
}
public void reset() {
isCandidate = false;
candidateElementAnnotations.clear();
}
}
private static boolean isAnonymousTypeName(String typeName) {
//TODO(benoitlamarche): use Annotations to determine if the class is anonymous
int location = typeName.lastIndexOf('$');
if (location != -1) {
String num = typeName.substring(location + 1, typeName.length() - 1);
try {
Integer.parseInt(num);
return true;
} catch (NumberFormatException e) {
return false;
}
} else {
return false;
}
}
private static boolean isSynthetic(int modifier) {
return ((modifier & AccessFlags.ACC_SYNTHETIC) == AccessFlags.ACC_SYNTHETIC);
}
private static int getMethodAccessFlags(ClassDataItem classData, MethodIdItem methodId) {
for (EncodedMethod encodedMethod : classData.getDirectMethods()) {
if (encodedMethod.method == methodId) {
return encodedMethod.accessFlags;
}
}
for (EncodedMethod encodedMethod : classData.getVirtualMethods()) {
if (encodedMethod.method == methodId) {
return encodedMethod.accessFlags;
}
}
return -1;
}
}