diff options
Diffstat (limited to 'dexcomparator/src/com/android/jack/DexAnnotationsComparator.java')
| -rw-r--r-- | dexcomparator/src/com/android/jack/DexAnnotationsComparator.java | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/dexcomparator/src/com/android/jack/DexAnnotationsComparator.java b/dexcomparator/src/com/android/jack/DexAnnotationsComparator.java new file mode 100644 index 00000000..df7b2a7b --- /dev/null +++ b/dexcomparator/src/com/android/jack/DexAnnotationsComparator.java @@ -0,0 +1,697 @@ +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; + } +}
\ No newline at end of file |
