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);
}
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;
}
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);
}
}
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;
}
}
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);
}
}
}
Aggregations