All Downloads are FREE. Search and download functionalities are using the official Maven repository.

framework.src.org.checkerframework.common.reflection.ClassValAnnotatedTypeFactory Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java’s type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.42.0
Show newest version
package org.checkerframework.common.reflection;

import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.reflection.qual.ClassBound;
import org.checkerframework.common.reflection.qual.ClassVal;
import org.checkerframework.common.reflection.qual.ClassValBottom;
import org.checkerframework.common.reflection.qual.ForName;
import org.checkerframework.common.reflection.qual.GetClass;
import org.checkerframework.common.reflection.qual.UnknownClass;
import org.checkerframework.common.value.ValueAnnotatedTypeFactory;
import org.checkerframework.common.value.ValueChecker;
import org.checkerframework.common.value.qual.StringVal;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.TreeAnnotator;
import org.checkerframework.framework.util.AnnotationBuilder;
import org.checkerframework.framework.util.MultiGraphQualifierHierarchy;
import org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.InternalUtils;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;

import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.ArrayType;
import com.sun.tools.javac.code.Type.UnionClassType;

public class ClassValAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {


    protected final AnnotationMirror CLASSVAL_TOP = AnnotationUtils
            .fromClass(elements, UnknownClass.class);


    public ClassValAnnotatedTypeFactory(BaseTypeChecker checker) {
        super(checker);
        if (this.getClass().equals(ClassValAnnotatedTypeFactory.class)) {
            this.postInit();
        }
    }

    @Override
    protected Set> createSupportedTypeQualifiers() {
        return Collections.unmodifiableSet(
                new HashSet>(
                        Arrays.asList(UnknownClass.class, ClassVal.class, ClassBound.class, ClassValBottom.class)));
    }

    private AnnotationMirror createClassVal(List values) {
        AnnotationBuilder builder = new AnnotationBuilder(processingEnv,
                ClassVal.class.getCanonicalName());
        builder.setValue("value", values);
        return builder.build();
    }

    private AnnotationMirror createClassBound(List values) {
        AnnotationBuilder builder = new AnnotationBuilder(processingEnv,
                ClassBound.class.getCanonicalName());
        builder.setValue("value", values);
        return builder.build();
    }

    /**
     * Returns the list of classnames from @ClassBound or @ClassVal if anno is
     * @ClassBound or @ClassVal, otherwise returns an empty list
     *
     * @param anno any AnnotationMirror
     * @return list of classnames in anno
     */
    public static List getClassNamesFromAnnotation(AnnotationMirror anno) {
        if (AnnotationUtils.areSameByClass(anno, ClassBound.class)
                || AnnotationUtils.areSameByClass(anno, ClassVal.class)) {
            return AnnotationUtils.getElementValueArray(anno, "value",
                    String.class, true);
        }
        return new ArrayList<>();
    }

    @Override
    public QualifierHierarchy createQualifierHierarchy(MultiGraphFactory factory) {
        return new ClassValQualifierHierarchy(factory);
    }

    /**
     * The qualifier hierarchy for the ClassVal type system
     */
    protected class ClassValQualifierHierarchy extends
            MultiGraphQualifierHierarchy {

        public ClassValQualifierHierarchy(MultiGraphFactory f) {
            super(f);
        }

        /*
         * Determines the least upper bound of a1 and a2. If both are ClassVal
         * annotations, then the least upper bound is the set of elements
         * obtained by combining the values of both annotations.
         */
        @Override
        public AnnotationMirror leastUpperBound(AnnotationMirror a1,
                AnnotationMirror a2) {
            if (!AnnotationUtils.areSameIgnoringValues(getTopAnnotation(a1),
                    getTopAnnotation(a2))) {
                return null;
            } else if (isSubtype(a1, a2)) {
                return a2;
            } else if (isSubtype(a2, a1)) {
                return a1;
            } else {
                List a1ClassNames = getClassNamesFromAnnotation(a1);
                List a2ClassNames = getClassNamesFromAnnotation(a2);
                Set lubClassNames = new TreeSet();
                lubClassNames.addAll(a1ClassNames);
                lubClassNames.addAll(a2ClassNames);

                // If either annotation is a ClassBound, the lub must also be a class bound.
                if (AnnotationUtils.areSameByClass(a1, ClassBound.class) ||
                        AnnotationUtils.areSameByClass(a2, ClassBound.class)) {
                    return createClassBound(new ArrayList<>(lubClassNames));
                } else {
                    return createClassVal(new ArrayList<>(lubClassNames));
                }
            }
        }

        @Override
        public AnnotationMirror greatestLowerBound(AnnotationMirror a1, AnnotationMirror a2) {
            if (!AnnotationUtils.areSameIgnoringValues(getTopAnnotation(a1), getTopAnnotation(a2))) {
                return null;
            } else if (isSubtype(a1, a2)) {
                return a1;
            } else if (isSubtype(a2, a1)) {
                return a2;
            } else {
                List a1ClassNames = getClassNamesFromAnnotation(a1);
                List a2ClassNames = getClassNamesFromAnnotation(a2);
                Set glbClassNames = new TreeSet();
                glbClassNames.addAll(a1ClassNames);
                glbClassNames.retainAll(a2ClassNames);

                // If either annotation is a ClassVal, the glb must also be a ClassVal.
                // For example:
                // GLB( @ClassVal(a,b), @ClassBound(a,c)) is @ClassVal(a)
                // because @ClassBound(a) is not a subtype of @ClassVal(a,b)
                if (AnnotationUtils.areSameByClass(a1, ClassVal.class) ||
                            AnnotationUtils.areSameByClass(a2, ClassVal.class)) {
                    return createClassVal(new ArrayList<>(glbClassNames));
                } else {
                    return createClassBound(new ArrayList<>(glbClassNames));
                }
            }
        }

        /*
         * Computes subtyping as per the subtyping in the qualifier hierarchy
         * structure unless both annotations are ClassVal. In this case, rhs is
         * a subtype of lhs iff lhs contains at least every element of rhs
         */
        @Override
        public boolean isSubtype(AnnotationMirror sub, AnnotationMirror sup) {
            if (AnnotationUtils.areSame(sub, sup)
                    || AnnotationUtils.areSameByClass(sup, UnknownClass.class)
                    || AnnotationUtils
                            .areSameByClass(sub, ClassValBottom.class)) {
                return true;
            }
            if (AnnotationUtils.areSameByClass(sub, UnknownClass.class)
                    || AnnotationUtils
                            .areSameByClass(sup, ClassValBottom.class)) {
                return false;
            }
            if (AnnotationUtils.areSameByClass(sup, ClassVal.class)
                    && AnnotationUtils.areSameByClass(sub, ClassBound.class)) {
                return false;
            }

            // if super: ClassVal && sub is ClassVal
            // if super: ClassBound && (sub is ClassBound or ClassVal)

            List supValues = getClassNamesFromAnnotation(sup);
            List subValues = getClassNamesFromAnnotation(sub);

            return supValues.containsAll(subValues);
        }
    }

    @Override
    protected TreeAnnotator createTreeAnnotator() {
        return new ListTreeAnnotator(new ClassValTreeAnnotator(this),
                super.createTreeAnnotator());
    }

    /**
     * Implements these type inference rules:
     * C.class:             @ClassVal(fully qualified name of C)
     * Class.forName(name): @ClassVal("name")
     * exp.getClass():      @ClassBound(fully qualified classname of exp)
     */
    protected class ClassValTreeAnnotator extends TreeAnnotator {

        protected ClassValTreeAnnotator(ClassValAnnotatedTypeFactory factory) {
            super(factory);
        }

        @Override
        public Void visitMemberSelect(MemberSelectTree tree,
                AnnotatedTypeMirror type) {
            if (TreeUtils.isClassLiteral(tree)) {
                // Create annotations for Class literals
                // C.class: @ClassVal(fully qualified name of C)
                ExpressionTree etree = tree.getExpression();
                Type classType = (Type) InternalUtils.typeOf(etree);
                String name = getClassNameFromType(classType);
                if (name != null) {
                    AnnotationMirror newQual = createClassVal(Arrays
                            .asList(name));
                    type.replaceAnnotation(newQual);
                }
            }
            return null;
        }

        @Override
        public Void visitMethodInvocation(MethodInvocationTree tree,
                AnnotatedTypeMirror type) {

            if (isForNameMethodInovaction(tree)) {
                // Class.forName(name): @ClassVal("name")
                ExpressionTree arg = tree.getArguments().get(0);
                List classNames = getStringValues(arg);
                if (classNames != null) {
                    AnnotationMirror newQual = createClassVal(classNames);
                    type.replaceAnnotation(newQual);
                }
            } else if (isGetClassMethodInovaction(tree)) {
                // exp.getClass(): @ClassBound(fully qualified class name of exp)
                Type clType;
                if (TreeUtils.getReceiverTree(tree) != null) {
                    clType = (Type) InternalUtils.typeOf(TreeUtils
                            .getReceiverTree(tree));
                } else { // receiver is null, so it is implicitly "this"
                    ClassTree classTree = TreeUtils
                            .enclosingClass(getPath(tree));
                    clType = (Type) InternalUtils.typeOf(classTree);
                }
                String className = getClassNameFromType(clType);
                AnnotationMirror newQual = createClassBound(Arrays
                        .asList(className));
                type.replaceAnnotation(newQual);
            }
            return null;
        }

        private boolean isForNameMethodInovaction(MethodInvocationTree tree) {
            return getDeclAnnotation(InternalUtils.symbol(tree),
                    ForName.class) != null;
        }
        private boolean isGetClassMethodInovaction(MethodInvocationTree tree) {
            return getDeclAnnotation(InternalUtils.symbol(tree),
                    GetClass.class) != null;
        }

        private List getStringValues(ExpressionTree arg) {
            ValueAnnotatedTypeFactory valueATF = getTypeFactoryOfSubchecker(ValueChecker.class);
            AnnotationMirror annotation = valueATF.getAnnotationMirror(arg, StringVal.class);
            if (annotation == null) {
                return null;
            }
            return AnnotationUtils.getElementValueArray(annotation, "value",
                    String.class, true);
        }

        /**
         * Return String representation of class name. This will not return the
         * correct name for anonymous classes.
         */
        private String getClassNameFromType(Type classType) {
            switch (classType.getKind()) {
            case ARRAY:
                String array = "";
                while (classType.getKind() == TypeKind.ARRAY) {
                    classType = ((ArrayType) classType).getComponentType();
                    array+="[]";
                }
                return getClassNameFromType(classType)+array;
            case DECLARED:
                StringBuilder className = new StringBuilder(TypesUtils
                        .getQualifiedName((DeclaredType) classType).toString());
                if (classType.getEnclosingType() != null) {
                    while (classType.getEnclosingType().getKind() != TypeKind.NONE) {
                        classType = classType.getEnclosingType();
                        int last = className.lastIndexOf(".");
                        if (last > -1) {
                            className.replace(last, last + 1, "$");
                        }
                    }
                }
                return className.toString();
            case INTERSECTION:
                // This could be more precise
                return "java.lang.Object";
            case NULL:
                return "java.lang.Object";
            case UNION:
                classType = ((UnionClassType) classType).getLub();
                return getClassNameFromType(classType);
            case TYPEVAR:
            case WILDCARD:
                classType = classType.getUpperBound();
                return getClassNameFromType(classType);
            case INT:
                return int.class.getCanonicalName();
            case LONG:
                return long.class.getCanonicalName();
            case SHORT:
                return short.class.getCanonicalName();
            case BYTE:
                return byte.class.getCanonicalName();
            case CHAR:
                return char.class.getCanonicalName();
            case DOUBLE:
                return double.class.getCanonicalName();
            case FLOAT:
                return float.class.getCanonicalName();
            case BOOLEAN:
                return boolean.class.getCanonicalName();
            default:
                checker.errorAbort("ClassValAnnotatedTypeFactory.getClassname: did not expect " +classType.getKind());
                return "java.lang.Object";
            }

        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy