Search in sources :

Example 16 with FieldAccess

use of org.checkerframework.dataflow.expression.FieldAccess in project checker-framework by typetools.

the class CFAbstractStore method updateForMethodCall.

/* --------------------------------------------------------- */
/* Handling of fields */
/* --------------------------------------------------------- */
/**
 * Remove any information that might not be valid any more after a method call, and add
 * information guaranteed by the method.
 *
 * <ol>
 *   <li>If the method is side-effect-free (as indicated by {@link
 *       org.checkerframework.dataflow.qual.SideEffectFree} or {@link
 *       org.checkerframework.dataflow.qual.Pure}), then no information needs to be removed.
 *   <li>Otherwise, all information about field accesses {@code a.f} needs to be removed, except
 *       if the method {@code n} cannot modify {@code a.f} (e.g., if {@code a} is a local variable
 *       or {@code this}, and {@code f} is final).
 *   <li>Furthermore, if the field has a monotonic annotation, then its information can also be
 *       kept.
 * </ol>
 *
 * Furthermore, if the method is deterministic, we store its result {@code val} in the store.
 */
public void updateForMethodCall(MethodInvocationNode n, AnnotatedTypeFactory atypeFactory, V val) {
    ExecutableElement method = n.getTarget().getMethod();
    // case 1: remove information if necessary
    if (!(analysis.checker.hasOption("assumeSideEffectFree") || analysis.checker.hasOption("assumePure") || isSideEffectFree(atypeFactory, method))) {
        boolean sideEffectsUnrefineAliases = ((GenericAnnotatedTypeFactory) atypeFactory).sideEffectsUnrefineAliases;
        // isUnmodifiableByOtherCode.  Example: @KeyFor("valueThatCanBeMutated").
        if (sideEffectsUnrefineAliases) {
            localVariableValues.entrySet().removeIf(e -> !e.getKey().isUnmodifiableByOtherCode());
        }
        // update this value
        if (sideEffectsUnrefineAliases) {
            thisValue = null;
        }
        // update field values
        if (sideEffectsUnrefineAliases) {
            fieldValues.entrySet().removeIf(e -> !e.getKey().isUnmodifiableByOtherCode());
        } else {
            Map<FieldAccess, V> newFieldValues = new HashMap<>(CollectionsPlume.mapCapacity(fieldValues));
            for (Map.Entry<FieldAccess, V> e : fieldValues.entrySet()) {
                FieldAccess fieldAccess = e.getKey();
                V otherVal = e.getValue();
                // case 3: the field has a monotonic annotation
                if (!((GenericAnnotatedTypeFactory<?, ?, ?, ?>) atypeFactory).getSupportedMonotonicTypeQualifiers().isEmpty()) {
                    List<Pair<AnnotationMirror, AnnotationMirror>> fieldAnnotations = atypeFactory.getAnnotationWithMetaAnnotation(fieldAccess.getField(), MonotonicQualifier.class);
                    V newOtherVal = null;
                    for (Pair<AnnotationMirror, AnnotationMirror> fieldAnnotation : fieldAnnotations) {
                        AnnotationMirror monotonicAnnotation = fieldAnnotation.second;
                        // permitted for use in the framework
                        @SuppressWarnings("deprecation") Name annotation = AnnotationUtils.getElementValueClassName(monotonicAnnotation, "value", false);
                        AnnotationMirror target = AnnotationBuilder.fromName(atypeFactory.getElementUtils(), annotation);
                        // Make sure the 'target' annotation is present.
                        if (AnnotationUtils.containsSame(otherVal.getAnnotations(), target)) {
                            newOtherVal = analysis.createSingleAnnotationValue(target, otherVal.getUnderlyingType()).mostSpecific(newOtherVal, null);
                        }
                    }
                    if (newOtherVal != null) {
                        // keep information for all hierarchies where we had a
                        // monotone annotation.
                        newFieldValues.put(fieldAccess, newOtherVal);
                        continue;
                    }
                }
                // case 2:
                if (!fieldAccess.isUnassignableByOtherCode()) {
                    // remove information completely
                    continue;
                }
                // keep information
                newFieldValues.put(fieldAccess, otherVal);
            }
            fieldValues = newFieldValues;
        }
        // update array values
        arrayValues.clear();
        // update method values
        methodValues.keySet().removeIf(e -> !e.isUnmodifiableByOtherCode());
    }
    // store information about method call if possible
    JavaExpression methodCall = JavaExpression.fromNode(n);
    replaceValue(methodCall, val);
}
Also used : GenericAnnotatedTypeFactory(org.checkerframework.framework.type.GenericAnnotatedTypeFactory) JavaExpression(org.checkerframework.dataflow.expression.JavaExpression) HashMap(java.util.HashMap) ExecutableElement(javax.lang.model.element.ExecutableElement) Name(javax.lang.model.element.Name) ClassName(org.checkerframework.dataflow.expression.ClassName) AnnotationMirror(javax.lang.model.element.AnnotationMirror) FieldAccess(org.checkerframework.dataflow.expression.FieldAccess) HashMap(java.util.HashMap) Map(java.util.Map) Pair(org.checkerframework.javacutil.Pair)

Example 17 with FieldAccess

use of org.checkerframework.dataflow.expression.FieldAccess in project checker-framework by typetools.

the class MustCallConsistencyAnalyzer method isValidCreatesMustCallForExpression.

/**
 * Checks the validity of the given expression from an invoked method's {@link
 * org.checkerframework.checker.mustcall.qual.CreatesMustCallFor} annotation. Helper method for
 * {@link #checkCreatesMustCallForInvocation(Set, MethodInvocationNode)}.
 *
 * <p>An expression is valid if one of the following conditions is true: 1) the expression is an
 * owning pointer, 2) the expression already has a tracked Obligation (i.e. there is already a
 * resource alias in some Obligation's resource alias set that refers to the expression), or 3)
 * the method in which the invocation occurs also has an @CreatesMustCallFor annotation, with the
 * same expression.
 *
 * @param obligations the currently-tracked Obligations; this value is side-effected if there is
 *     an Obligation in it which tracks {@code expression} as one of its resource aliases
 * @param expression an element of a method's @CreatesMustCallFor annotation
 * @param path the path to the invocation of the method from whose @CreateMustCallFor annotation
 *     {@code expression} came
 * @return true iff the expression is valid, as defined above
 */
private boolean isValidCreatesMustCallForExpression(Set<Obligation> obligations, JavaExpression expression, TreePath path) {
    if (expression instanceof FieldAccess) {
        Element elt = ((FieldAccess) expression).getField();
        if (!checker.hasOption(MustCallChecker.NO_LIGHTWEIGHT_OWNERSHIP) && typeFactory.getDeclAnnotation(elt, Owning.class) != null) {
            // The expression is an Owning field.  This satisfies case 1.
            return true;
        }
    } else if (expression instanceof LocalVariable) {
        Element elt = ((LocalVariable) expression).getElement();
        if (!checker.hasOption(MustCallChecker.NO_LIGHTWEIGHT_OWNERSHIP) && typeFactory.getDeclAnnotation(elt, Owning.class) != null) {
            // This satisfies case 1.
            return true;
        } else {
            Obligation toRemove = null;
            Obligation toAdd = null;
            for (Obligation obligation : obligations) {
                ResourceAlias alias = obligation.getResourceAlias(expression);
                if (alias != null) {
                    // This satisfies case 2 above. Remove all its aliases, then return below.
                    if (toRemove != null) {
                        throw new TypeSystemError("tried to remove multiple sets containing a reset expression at once");
                    }
                    toRemove = obligation;
                    toAdd = new Obligation(ImmutableSet.of(alias));
                }
            }
            if (toRemove != null) {
                obligations.remove(toRemove);
                obligations.add(toAdd);
                // This satisfies case 2.
                return true;
            }
        }
    }
    // TODO: Getting this every time is inefficient if a method has many @CreatesMustCallFor
    // annotations, but that should be rare.
    MethodTree enclosingMethodTree = TreePathUtil.enclosingMethod(path);
    if (enclosingMethodTree == null) {
        return false;
    }
    ExecutableElement enclosingMethodElt = TreeUtils.elementFromDeclaration(enclosingMethodTree);
    MustCallAnnotatedTypeFactory mcAtf = typeFactory.getTypeFactoryOfSubchecker(MustCallChecker.class);
    List<String> enclosingCmcfValues = ResourceLeakVisitor.getCreatesMustCallForValues(enclosingMethodElt, mcAtf, typeFactory);
    if (enclosingCmcfValues.isEmpty()) {
        return false;
    }
    for (String enclosingCmcfValue : enclosingCmcfValues) {
        JavaExpression enclosingTarget;
        try {
            enclosingTarget = StringToJavaExpression.atMethodBody(enclosingCmcfValue, enclosingMethodTree, checker);
        } catch (JavaExpressionParseException e) {
            // Do not issue an error here, because it would be a duplicate.
            // The error will be issued by the Transfer class of the checker,
            // via the CreatesMustCallForElementSupplier interface.
            enclosingTarget = null;
        }
        if (areSame(expression, enclosingTarget)) {
            // This satisfies case 3.
            return true;
        }
    }
    return false;
}
Also used : JavaExpression(org.checkerframework.dataflow.expression.JavaExpression) StringToJavaExpression(org.checkerframework.framework.util.StringToJavaExpression) MethodTree(com.sun.source.tree.MethodTree) TypeElement(javax.lang.model.element.TypeElement) Element(javax.lang.model.element.Element) VariableElement(javax.lang.model.element.VariableElement) ExecutableElement(javax.lang.model.element.ExecutableElement) ExecutableElement(javax.lang.model.element.ExecutableElement) LocalVariable(org.checkerframework.dataflow.expression.LocalVariable) TypeSystemError(org.checkerframework.javacutil.TypeSystemError) MustCallAnnotatedTypeFactory(org.checkerframework.checker.mustcall.MustCallAnnotatedTypeFactory) NotOwning(org.checkerframework.checker.mustcall.qual.NotOwning) Owning(org.checkerframework.checker.mustcall.qual.Owning) JavaExpressionParseException(org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException) FieldAccess(org.checkerframework.dataflow.expression.FieldAccess)

Example 18 with FieldAccess

use of org.checkerframework.dataflow.expression.FieldAccess in project checker-framework by typetools.

the class UpperBoundVisitor method checkEffectivelyFinalAndParsable.

/**
 * Reports an error if the Java expression named by s is not effectively final when parsed at the
 * declaration of the given class.
 *
 * @param s a Java expression
 * @param classTree the expression is parsed with respect to this class
 * @param whereToReportError the tree at which to possibly report an error
 */
private void checkEffectivelyFinalAndParsable(String s, ClassTree classTree, Tree whereToReportError) {
    JavaExpression je;
    try {
        je = StringToJavaExpression.atTypeDecl(s, TreeUtils.elementFromDeclaration(classTree), checker);
    } catch (JavaExpressionParseException e) {
        checker.report(whereToReportError, e.getDiagMessage());
        return;
    }
    Element element = null;
    if (je instanceof LocalVariable) {
        element = ((LocalVariable) je).getElement();
    } else if (je instanceof FieldAccess) {
        element = ((FieldAccess) je).getField();
    } else if (je instanceof ThisReference || je instanceof ValueLiteral) {
        return;
    }
    if (element == null || !ElementUtils.isEffectivelyFinal(element)) {
        checker.reportError(whereToReportError, NOT_FINAL, je);
    }
}
Also used : JavaExpression(org.checkerframework.dataflow.expression.JavaExpression) StringToJavaExpression(org.checkerframework.framework.util.StringToJavaExpression) Element(javax.lang.model.element.Element) JavaExpressionParseException(org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException) LocalVariable(org.checkerframework.dataflow.expression.LocalVariable) ValueLiteral(org.checkerframework.dataflow.expression.ValueLiteral) FieldAccess(org.checkerframework.dataflow.expression.FieldAccess) ThisReference(org.checkerframework.dataflow.expression.ThisReference)

Example 19 with FieldAccess

use of org.checkerframework.dataflow.expression.FieldAccess in project checker-framework by typetools.

the class LockAnnotatedTypeFactory method isExpressionEffectivelyFinal.

/**
 * Returns whether or not the expression is effectively final.
 *
 * <p>This method returns true in the following cases when expr is:
 *
 * <p>1. a field access and the field is final and the field access expression is effectively
 * final as specified by this method.
 *
 * <p>2. an effectively final local variable.
 *
 * <p>3. a deterministic method call whose arguments and receiver expression are effectively final
 * as specified by this method.
 *
 * <p>4. a this reference or a class literal
 *
 * @param expr expression
 * @return whether or not the expression is effectively final
 */
boolean isExpressionEffectivelyFinal(JavaExpression expr) {
    if (expr instanceof FieldAccess) {
        FieldAccess fieldAccess = (FieldAccess) expr;
        JavaExpression receiver = fieldAccess.getReceiver();
        // Don't call fieldAccess
        return fieldAccess.isFinal() && isExpressionEffectivelyFinal(receiver);
    } else if (expr instanceof LocalVariable) {
        return ElementUtils.isEffectivelyFinal(((LocalVariable) expr).getElement());
    } else if (expr instanceof MethodCall) {
        MethodCall methodCall = (MethodCall) expr;
        for (JavaExpression arg : methodCall.getArguments()) {
            if (!isExpressionEffectivelyFinal(arg)) {
                return false;
            }
        }
        return PurityUtils.isDeterministic(this, methodCall.getElement()) && isExpressionEffectivelyFinal(methodCall.getReceiver());
    } else if (expr instanceof ThisReference || expr instanceof ClassName) {
        // too.
        return true;
    } else {
        // type of 'expr' is not supported in @GuardedBy(...) lock expressions
        return false;
    }
}
Also used : JavaExpression(org.checkerframework.dataflow.expression.JavaExpression) LocalVariable(org.checkerframework.dataflow.expression.LocalVariable) ClassName(org.checkerframework.dataflow.expression.ClassName) FieldAccess(org.checkerframework.dataflow.expression.FieldAccess) ThisReference(org.checkerframework.dataflow.expression.ThisReference) MethodCall(org.checkerframework.dataflow.expression.MethodCall)

Example 20 with FieldAccess

use of org.checkerframework.dataflow.expression.FieldAccess in project checker-framework by typetools.

the class LockStore method updateForMethodCall.

@Override
public void updateForMethodCall(MethodInvocationNode n, AnnotatedTypeFactory atypeFactory, CFValue val) {
    super.updateForMethodCall(n, atypeFactory, val);
    ExecutableElement method = n.getTarget().getMethod();
    // cannot use that logic.
    if (!isSideEffectFree(atypeFactory, method)) {
        // to @LockPossiblyHeld, but the annotation in the GuardedBy hierarchy should not be changed.
        for (FieldAccess field : new ArrayList<>(fieldValues.keySet())) {
            CFValue newValue = changeLockAnnoToTop(field, fieldValues.get(field));
            if (newValue != null) {
                fieldValues.put(field, newValue);
            } else {
                fieldValues.remove(field);
            }
        }
        // Local variables could also be unlocked via an alias
        for (LocalVariable var : new ArrayList<>(localVariableValues.keySet())) {
            CFValue newValue = changeLockAnnoToTop(var, localVariableValues.get(var));
            if (newValue != null) {
                localVariableValues.put(var, newValue);
            }
        }
        if (thisValue != null) {
            thisValue = changeLockAnnoToTop(null, thisValue);
        }
    }
}
Also used : CFValue(org.checkerframework.framework.flow.CFValue) ExecutableElement(javax.lang.model.element.ExecutableElement) ArrayList(java.util.ArrayList) LocalVariable(org.checkerframework.dataflow.expression.LocalVariable) FieldAccess(org.checkerframework.dataflow.expression.FieldAccess)

Aggregations

FieldAccess (org.checkerframework.dataflow.expression.FieldAccess)26 LocalVariable (org.checkerframework.dataflow.expression.LocalVariable)11 ClassName (org.checkerframework.dataflow.expression.ClassName)10 JavaExpression (org.checkerframework.dataflow.expression.JavaExpression)9 MethodCall (org.checkerframework.dataflow.expression.MethodCall)9 HashMap (java.util.HashMap)8 ArrayAccess (org.checkerframework.dataflow.expression.ArrayAccess)8 Map (java.util.Map)7 ThisReference (org.checkerframework.dataflow.expression.ThisReference)7 AnnotationMirror (javax.lang.model.element.AnnotationMirror)5 CFValue (org.checkerframework.framework.flow.CFValue)5 Element (javax.lang.model.element.Element)4 ExecutableElement (javax.lang.model.element.ExecutableElement)4 VariableElement (javax.lang.model.element.VariableElement)4 JavaExpressionParseException (org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException)3 MethodTree (com.sun.source.tree.MethodTree)2 Tree (com.sun.source.tree.Tree)2 ArrayList (java.util.ArrayList)2 TypeElement (javax.lang.model.element.TypeElement)2 CFStore (org.checkerframework.framework.flow.CFStore)2