framework.src.org.checkerframework.common.aliasing.AliasingTransfer Maven / Gradle / Ivy
Show all versions of checker Show documentation
package org.checkerframework.common.aliasing;
import java.util.List;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import org.checkerframework.common.aliasing.qual.LeakedToResult;
import org.checkerframework.common.aliasing.qual.NonLeaked;
import org.checkerframework.common.aliasing.qual.Unique;
import org.checkerframework.dataflow.analysis.FlowExpressions;
import org.checkerframework.dataflow.analysis.FlowExpressions.Receiver;
import org.checkerframework.dataflow.analysis.RegularTransferResult;
import org.checkerframework.dataflow.analysis.TransferInput;
import org.checkerframework.dataflow.analysis.TransferResult;
import org.checkerframework.dataflow.cfg.node.AssignmentNode;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.dataflow.cfg.node.ObjectCreationNode;
import org.checkerframework.framework.flow.CFAbstractAnalysis;
import org.checkerframework.framework.flow.CFStore;
import org.checkerframework.framework.flow.CFTransfer;
import org.checkerframework.framework.flow.CFValue;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
import org.checkerframework.javacutil.TreeUtils;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
/**
* Type refinement is treated in the usual way, except that at
* (pseudo-)assignments the RHS may lose its type refinement, before the LHS is
* type-refined.
*
* The RHS always loses its type refinement (it is widened to
* {@literal @}MaybeAliased, and its declared type must have been
* {@literal @}MaybeAliased) except in the following cases:
*
* 1.The RHS is a fresh expression.
*
* 2.The LHS is a {@literal @}NonLeaked formal parameter and the RHS is an
* argument in a method call or constructor invocation.
*
* 3.The LHS is a {@literal @}LeakedToResult formal parameter, the RHS is an
* argument in a method call or constructor invocation, and the method's return
* value is discarded.
*/
public class AliasingTransfer extends CFTransfer {
private AnnotatedTypeFactory factory;
public AliasingTransfer(
CFAbstractAnalysis analysis) {
super(analysis);
factory = analysis.getTypeFactory();
}
/**
* Case 1: For every assignment, the LHS is refined if the RHS has type
* {@literal @}Unique and is a method invocation or a new class instance.
*/
@Override
public TransferResult visitAssignment(AssignmentNode n,
TransferInput in) {
Node rhs = n.getExpression();
Tree treeRhs = rhs.getTree();
AnnotatedTypeMirror rhsType = factory.getAnnotatedType(treeRhs);
if (rhsType.hasAnnotation(Unique.class)
&& (rhs instanceof MethodInvocationNode || rhs instanceof ObjectCreationNode)) {
return super.visitAssignment(n, in); // Do normal refinement.
}
// Widen the type of the rhs if the RHS's declared type wasn't @Unique.
Receiver r = FlowExpressions.internalReprOf(factory, rhs);
in.getRegularStore().clearValue(r);
return new RegularTransferResult<>(null, in.getRegularStore());
}
/**
* Handling pseudo-assignments. Called by
* CFAbstractTransfer.visitMethodInvocation()
.
*
* Case 2: Given a method call, traverses all formal parameters of the
* method declaration, and if it doesn't have the {@literal @}NonLeaked or
* {@literal @}LeakedToResult annotations, we remove the node of the
* respective argument in the method call from the store. If parameter has
* {@literal @}LeakedToResult, visitMethodInvocation()
handles it.
*/
@Override
protected void processPostconditions(MethodInvocationNode n, CFStore store,
ExecutableElement methodElement, Tree tree) {
super.processPostconditions(n, store, methodElement, tree);
if (TreeUtils.isEnumSuper(n.getTree())) {
// Skipping the init() method for enums.
return;
}
List args = n.getArguments();
List params = methodElement.getParameters();
assert (args.size() == params.size()) : "Number of arguments in " +
"the method call " + n.toString() + " is different from the" +
" number of parameters for the method declaration: "
+ methodElement.getSimpleName().toString();
AnnotatedExecutableType annotatedType = factory.getAnnotatedType(methodElement);
List paramTypes = annotatedType.getParameterTypes();
for (int i = 0; i < args.size(); i++) {
Node arg = args.get(i);
AnnotatedTypeMirror paramType = paramTypes.get(i);
if (!paramType.hasAnnotation(NonLeaked.class)
&& !paramType.hasAnnotation(LeakedToResult.class)) {
store.clearValue(FlowExpressions.internalReprOf(factory, arg));
}
}
// Now, doing the same as above for the receiver parameter
Node receiver = n.getTarget().getReceiver();
AnnotatedDeclaredType receiverType = annotatedType.getReceiverType();
if (receiverType != null && !receiverType.hasAnnotation(LeakedToResult.class)
&& !receiverType.hasAnnotation(NonLeaked.class)) {
store.clearValue(FlowExpressions.internalReprOf(factory, receiver));
}
}
/**
* Case 3: Given a method invocation expression, if the parent of the
* expression is not a statement, check if there are any arguments of the
* method call annotated as {@literal @}LeakedToResult and remove it from the
* store, since it might be leaked.
*/
@Override
public TransferResult visitMethodInvocation(
MethodInvocationNode n, TransferInput in) {
Tree parent = n.getTreePath().getParentPath().getLeaf();
boolean parentIsStatement = parent.getKind() == Kind.EXPRESSION_STATEMENT;
if (!parentIsStatement) {
ExecutableElement methodElement = TreeUtils.elementFromUse(n.getTree());
List args = n.getArguments();
List params = methodElement
.getParameters();
assert (args.size() == params.size()) : "Number of arguments in " +
"the method call " + n.toString() + " is different from the" +
" number of parameters for the method declaration: "
+ methodElement.getSimpleName().toString();
CFStore store = in.getRegularStore();
for (int i = 0; i < args.size(); i++) {
Node arg = args.get(i);
VariableElement param = params.get(i);
if (factory.getAnnotatedType(param).hasAnnotation(LeakedToResult.class)) {
// If argument can leak to result, and parent is not a
// single statement, remove that node from store.
store.clearValue(FlowExpressions.internalReprOf(factory,
arg));
}
}
// Now, doing the same as above for the receiver parameter
Node receiver = n.getTarget().getReceiver();
AnnotatedExecutableType annotatedType = factory.getAnnotatedType(methodElement);
AnnotatedDeclaredType receiverType = annotatedType.getReceiverType();
if (receiverType != null && receiverType.hasAnnotation(LeakedToResult.class)) {
store.clearValue(FlowExpressions.internalReprOf(factory, receiver));
}
}
// If parent is a statement, processPostconditions will handle the
// pseudo-assignments.
return super.visitMethodInvocation(n, in);
}
}