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

org.checkerframework.checker.index.samelen.SameLenTransfer 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.

The newest version!
package org.checkerframework.checker.index.samelen;

import com.sun.source.tree.MethodTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;

import org.checkerframework.checker.index.IndexUtil;
import org.checkerframework.checker.index.qual.SameLen;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.analysis.ConditionalTransferResult;
import org.checkerframework.dataflow.analysis.TransferInput;
import org.checkerframework.dataflow.analysis.TransferResult;
import org.checkerframework.dataflow.cfg.UnderlyingAST;
import org.checkerframework.dataflow.cfg.node.ArrayCreationNode;
import org.checkerframework.dataflow.cfg.node.AssignmentNode;
import org.checkerframework.dataflow.cfg.node.FieldAccessNode;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.dataflow.expression.JavaExpression;
import org.checkerframework.framework.flow.CFAnalysis;
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.util.JavaExpressionParseUtil;
import org.checkerframework.javacutil.AnnotationUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

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

/**
 * The transfer function for the SameLen checker. Contains three cases:
 *
 * 
    *
  • new array: "b = new T[a.length]" implies that b is the same length as a. *
  • length equality: after "if (a.length == b.length)", a and b have the same length. *
  • object equality: after "if (a == b)", a and b have the same length, if they are arrays or * strings. *
*/ public class SameLenTransfer extends CFTransfer { /** The annotated type factory. */ private final SameLenAnnotatedTypeFactory atypeFactory; /** Shorthand for atypeFactory.UNKNOWN. */ private final AnnotationMirror UNKNOWN; /** * Create a new SameLenTransfer. * * @param analysis the CFAnalysis */ public SameLenTransfer(CFAnalysis analysis) { super(analysis); this.atypeFactory = (SameLenAnnotatedTypeFactory) analysis.getTypeFactory(); this.UNKNOWN = atypeFactory.UNKNOWN; } /** * Gets the receiver sequence of a length access node, or null if {@code lengthNode} is not a * length access. */ private @Nullable Node getLengthReceiver(Node lengthNode) { if (isArrayLengthAccess(lengthNode)) { // lengthNode is a.length FieldAccessNode lengthFieldAccessNode = (FieldAccessNode) lengthNode; return lengthFieldAccessNode.getReceiver(); } else if (atypeFactory.getMethodIdentifier().isLengthOfMethodInvocation(lengthNode)) { // lengthNode is s.length() MethodInvocationNode lengthMethodInvocationNode = (MethodInvocationNode) lengthNode; return lengthMethodInvocationNode.getTarget().getReceiver(); } else { return null; } } @Override public TransferResult visitAssignment( AssignmentNode node, TransferInput in) { TransferResult result = super.visitAssignment(node, in); // Handle b = new T[a.length] if (node.getExpression() instanceof ArrayCreationNode) { ArrayCreationNode acNode = (ArrayCreationNode) node.getExpression(); if (acNode.getDimensions().size() == 1) { Node lengthNode = acNode.getDimension(0); Node lengthNodeReceiver = getLengthReceiver(lengthNode); if (lengthNodeReceiver != null) { // "new T[a.length]" or "new T[s.length()]" is the right hand side of the // assignment. // lengthNode is known to be "lengthNodeReceiver.length" or // "lengthNodeReceiver.length()" // targetRec is the receiver for the left hand side of the assignment. JavaExpression targetRec = JavaExpression.fromNode(node.getTarget()); JavaExpression otherRec = JavaExpression.fromNode(lengthNodeReceiver); AnnotationMirror lengthNodeAnnotation = atypeFactory .getAnnotatedType(lengthNodeReceiver.getTree()) .getAnnotationInHierarchy(UNKNOWN); AnnotationMirror combinedSameLen = atypeFactory.createCombinedSameLen( Arrays.asList(targetRec, otherRec), Arrays.asList(UNKNOWN, lengthNodeAnnotation)); propagateCombinedSameLen(combinedSameLen, node, result.getRegularStore()); return result; } } } AnnotationMirror rightAnno = atypeFactory .getAnnotatedType(node.getExpression().getTree()) .getAnnotationInHierarchy(UNKNOWN); // If the left side of the assignment is an array or a string, then have both the right and // left side be SameLen of each other. JavaExpression targetRec = JavaExpression.fromNode(node.getTarget()); JavaExpression exprRec = JavaExpression.fromNode(node.getExpression()); if (IndexUtil.isSequenceType(node.getTarget().getType()) || (rightAnno != null && atypeFactory.areSameByClass(rightAnno, SameLen.class))) { AnnotationMirror rightAnnoOrUnknown = rightAnno == null ? UNKNOWN : rightAnno; AnnotationMirror combinedSameLen = atypeFactory.createCombinedSameLen( Arrays.asList(targetRec, exprRec), Arrays.asList(UNKNOWN, rightAnnoOrUnknown)); propagateCombinedSameLen(combinedSameLen, node, result.getRegularStore()); } return result; } /** * Insert a @SameLen annotation into the store as the SameLen type of each array listed in it. * * @param sameLenAnno a {@code @SameLen} annotation. Not just an annotation in the SameLen * hierarchy; this annotation must be {@code @SameLen(...)}. * @param node the node in the tree where the combination is happening. Used for context. * @param store the store to modify */ private void propagateCombinedSameLen(AnnotationMirror sameLenAnno, Node node, CFStore store) { TreePath currentPath = atypeFactory.getPath(node.getTree()); if (currentPath == null) { return; } for (String exprString : AnnotationUtils.getElementValueArray( sameLenAnno, atypeFactory.sameLenValueElement, String.class)) { JavaExpression je; try { je = atypeFactory.parseJavaExpressionString(exprString, currentPath); } catch (JavaExpressionParseUtil.JavaExpressionParseException e) { continue; } store.clearValue(je); store.insertValue(je, sameLenAnno); } } /** Returns true if node is of the form "someArray.length". */ private boolean isArrayLengthAccess(Node node) { return (node instanceof FieldAccessNode && ((FieldAccessNode) node).getFieldName().equals("length") && ((FieldAccessNode) node).getReceiver().getType().getKind() == TypeKind.ARRAY); } /** * Handles refinement of equality comparisons. Assumes "a == b" or "a.length == b.length" * evaluates to true. The method gives a and b SameLen of each other in the store. * * @param left the first argument to the equality operator * @param right the second argument to the equality operator * @param store the store in which to perform refinement */ private void refineEq(Node left, Node right, CFStore store) { List exprs = new ArrayList<>(2); List annos = new ArrayList<>(2); for (Node internal : splitAssignments(left)) { exprs.add(JavaExpression.fromNode(internal)); annos.add(getAnno(internal)); } for (Node internal : splitAssignments(right)) { exprs.add(JavaExpression.fromNode(internal)); annos.add(getAnno(internal)); } AnnotationMirror combinedSameLen = atypeFactory.createCombinedSameLen(exprs, annos); propagateCombinedSameLen(combinedSameLen, left, store); } /** * Returns {@code n}'s annotation from the SameLen hierarchy. * *

analysis.getValue fails if called on an lvalue. However, this method needs to always * succeed, even when n is an lvalue. Consider this code: * *

{@code if ((a = b) == c) {...}}
* * where a, b, and c are all arrays, and a has type {@code @SameLen("d")}. Afterwards, all three * should have the type {@code @SameLen({"a", "b", "c", "d"})}, but in order to accomplish this, * this method must return the type of a, which is an lvalue. * * @param n a node whose SameLen annotation to return * @return {@code n}'s annotation from the SameLen hierarchy */ AnnotationMirror getAnno(Node n) { if (n.isLValue()) { return atypeFactory.getAnnotatedType(n.getTree()).getAnnotationInHierarchy(UNKNOWN); } CFValue cfValue = analysis.getValue(n); if (cfValue == null) { return UNKNOWN; } return atypeFactory .getQualifierHierarchy() .findAnnotationInHierarchy(cfValue.getAnnotations(), UNKNOWN); } /** Implements the transfer rules for both equal nodes and not-equals nodes. */ @Override protected TransferResult strengthenAnnotationOfEqualTo( TransferResult result, Node firstNode, Node secondNode, CFValue firstValue, CFValue secondValue, boolean notEqualTo) { // If result is a Regular transfer, then the elseStore is a copy of the then store, that is // created when getElseStore is called. So do that before refining any values. CFStore elseStore = result.getElseStore(); CFStore thenStore = result.getThenStore(); CFStore equalStore = notEqualTo ? elseStore : thenStore; Node firstLengthReceiver = getLengthReceiver(firstNode); Node secondLengthReceiver = getLengthReceiver(secondNode); if (firstLengthReceiver != null && secondLengthReceiver != null) { // Refinement in the else store if this is a.length == b.length (or length() in case of // strings). refineEq(firstLengthReceiver, secondLengthReceiver, equalStore); } else if (IndexUtil.isSequenceType(firstNode.getType()) || IndexUtil.isSequenceType(secondNode.getType())) { refineEq(firstNode, secondNode, equalStore); } return new ConditionalTransferResult<>(result.getResultValue(), thenStore, elseStore); } /** Overridden to ensure that SameLen annotations on method parameters are symmetric. */ @Override protected void addInformationFromPreconditions( CFStore info, AnnotatedTypeFactory factory, UnderlyingAST.CFGMethod method, MethodTree methodTree, ExecutableElement methodElement) { super.addInformationFromPreconditions(info, factory, method, methodTree, methodElement); List paramTrees = methodTree.getParameters(); int numParams = paramTrees.size(); List paramNames = new ArrayList<>(numParams); List params = new ArrayList<>(numParams); for (VariableTree tree : paramTrees) { paramNames.add(tree.getName().toString()); params.add(atypeFactory.getAnnotatedType(tree)); } for (int index = 0; index < numParams; index++) { // If the parameter has a samelen annotation, then look for other parameters in that // annotation and propagate default the other annotation so that it is symmetric. AnnotatedTypeMirror atm = params.get(index); AnnotationMirror sameLenAnno = atm.getAnnotation(SameLen.class); if (sameLenAnno == null) { continue; } List values = AnnotationUtils.getElementValueArray( sameLenAnno, atypeFactory.sameLenValueElement, String.class); for (String value : values) { int otherParamIndex = paramNames.indexOf(value); if (otherParamIndex == -1) { continue; } // the SameLen value is in the list of params, so modify the type of // that param in the store AnnotationMirror newSameLen = atypeFactory.createSameLen( Collections.singletonList(paramNames.get(index))); JavaExpression otherParamRec = JavaExpression.fromVariableTree(paramTrees.get(otherParamIndex)); info.insertValuePermitNondeterministic(otherParamRec, newSameLen); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy