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

org.checkerframework.checker.index.upperbound.UpperBoundTransfer 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-eisop4
Show newest version
package org.checkerframework.checker.index.upperbound;

import com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;

import org.checkerframework.checker.index.IndexAbstractTransfer;
import org.checkerframework.checker.index.IndexRefinementInfo;
import org.checkerframework.checker.index.Subsequence;
import org.checkerframework.checker.index.inequality.LessThanAnnotatedTypeFactory;
import org.checkerframework.checker.index.qual.LessThan;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.index.qual.Positive;
import org.checkerframework.checker.index.qual.SubstringIndexFor;
import org.checkerframework.checker.index.upperbound.UBQualifier.LessThanLengthOf;
import org.checkerframework.checker.index.upperbound.UBQualifier.UpperBoundUnknownQualifier;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.common.value.ValueCheckerUtils;
import org.checkerframework.dataflow.analysis.RegularTransferResult;
import org.checkerframework.dataflow.analysis.TransferInput;
import org.checkerframework.dataflow.analysis.TransferResult;
import org.checkerframework.dataflow.cfg.node.ArrayCreationNode;
import org.checkerframework.dataflow.cfg.node.AssignmentNode;
import org.checkerframework.dataflow.cfg.node.CaseNode;
import org.checkerframework.dataflow.cfg.node.FieldAccessNode;
import org.checkerframework.dataflow.cfg.node.IntegerLiteralNode;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.dataflow.cfg.node.NumericalAdditionNode;
import org.checkerframework.dataflow.cfg.node.NumericalMultiplicationNode;
import org.checkerframework.dataflow.cfg.node.NumericalSubtractionNode;
import org.checkerframework.dataflow.cfg.node.TypeCastNode;
import org.checkerframework.dataflow.expression.FieldAccess;
import org.checkerframework.dataflow.expression.JavaExpression;
import org.checkerframework.dataflow.expression.MethodCall;
import org.checkerframework.dataflow.util.NodeUtils;
import org.checkerframework.framework.flow.CFAbstractStore;
import org.checkerframework.framework.flow.CFAnalysis;
import org.checkerframework.framework.flow.CFStore;
import org.checkerframework.framework.flow.CFValue;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.AnnotationUtils;

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

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

/**
 * Contains the transfer functions for the upper bound type system, a part of the Index Checker.
 * This class implements the following refinement rules:
 *
 * 
    *
  • 1. Refine the type of expressions used as an array dimension to be less than length of the * array to which the new array is assigned. For example, in {@code int[] array = new * int[expr];}, the type of expr is {@code @LTEqLength("array")}. *
  • 2. If {@code other * node} has type {@code typeOfMultiplication}, then if {@code other} is * positive, then {@code node} is {@code typeOfMultiplication}. *
  • 3. If {@code other * node} has type {@code typeOfMultiplication}, if {@code other} is * greater than 1, then {@code node} is {@code typeOfMultiplication} plus 1. *
  • 4. Given a subtraction node, {@code node}, that is known to have type {@code * typeOfSubtraction}. An offset can be applied to the left node (i.e. the left node has the * same type, but with an offset based on the right node). *
  • 5. In an addition expression, refine the two operands based on the type of the whole * expression with appropriate offsets. *
  • 6. If an addition expression has a type that is less than length of an array, and one of * the operands is non-negative, then the other is less than or equal to the length of the * array. *
  • 7. If an addition expression has a type that is less than length of an array, and one of * the operands is positive, then the other is also less than the length of the array. *
  • 8. if x < y, and y has a type that is related to the length of an array, then x has the * same type, with an offset that is one less. *
  • 9. if x ≤ y, and y has a type that is related to the length of an array, then x has the * same type. *
  • 10. refine the subtrahend in a subtraction which is greater than or equal to a certain * offset. The type of the subtrahend is refined to the type of the minuend with the offset * added. *
  • 11. if two variables are equal, they have the same type *
  • 12. If one node in a != expression is an sequence length field or method access (optionally * with a constant offset subtracted) and the other node is less than or equal to that * sequence length (minus the offset), then refine the other node's type to less than the * sequence length (minus the offset). *
  • 13. If some Node a is known to be less than the length of some array, x, then, the type of * a + b, is {@code @LTLengthOf(value="x", offset="-b")}. If b is known to be less than the * length of some other array, y, then the type of a + b is {@code @LTLengthOf(value={"x", * "y"}, offset={"-b", "-a"})}. *
  • 14. If a is known to be less than the length of x when some offset, o, is added to a (the * type of a is {@code @LTLengthOf(value="x", offset="o"))}, then the type of a + b is * {@code @LTLengthOf(value="x",offset="o - b")}. (Note, if "o - b" can be computed, then it * is and the result is used in the annotation.) *
  • 15. If expression i has type {@code @LTLengthOf(value = "f2", offset = "f1.length")} int * and expression j is less than or equal to the length of f1, then the type of i + j is * {@code @LTLengthOf("f2")}. *
  • 16. If some Node a is known to be less than the length of some sequence x, then the type of * a - b is {@code @LTLengthOf(value="x", offset="b")}. *
  • 17. If some Node a is known to be less than the length of some sequence x, and if b is * non-negative or positive, then a - b should keep the types of a. *
  • 18. The type of a sequence length access (i.e. array.length) is * {@code @LTLength(value={"array"...}, offset="-1")} where "array"... is the set of all * sequences that are the same length (via the SameLen checker) as "array" *
  • 19. If n is an array length field access, then the type of a.length is the glb of * {@code @LTEqLengthOf("a")} and the value of a.length in the store. *
  • 20. If n is a String.length() method invocation, then the type of s.length() is the glb of * {@code @LTEqLengthOf("s")} and the value of s.length() in the store. *
*/ public class UpperBoundTransfer extends IndexAbstractTransfer { /** The type factory associated with this transfer function. */ private final UpperBoundAnnotatedTypeFactory atypeFactory; /** The int TypeMirror. */ private final TypeMirror intTM; /** * Creates a new UpperBoundTransfer. * * @param analysis the analysis for this transfer function */ public UpperBoundTransfer(CFAnalysis analysis) { super(analysis); atypeFactory = (UpperBoundAnnotatedTypeFactory) analysis.getTypeFactory(); intTM = atypeFactory.types.getPrimitiveType(TypeKind.INT); } /** * Case 1: Refine the type of expressions used as an array dimension to be less than length of * the array to which the new array is assigned. For example, in "int[] array = new int[expr];", * the type of expr is @LTEqLength("array"). */ @Override public TransferResult visitAssignment( AssignmentNode node, TransferInput in) { TransferResult result = super.visitAssignment(node, in); Node expNode = node.getExpression(); // strip off typecast if any Node expNodeSansCast = (expNode instanceof TypeCastNode) ? ((TypeCastNode) expNode).getOperand() : expNode; // null if right-hand-side is not an array creation expression ArrayCreationNode acNode = (expNodeSansCast instanceof ArrayCreationNode) ? acNode = (ArrayCreationNode) expNodeSansCast : null; if (acNode != null) { // Right-hand side of assignment is an array creation expression List nodeList = acNode.getDimensions(); if (nodeList.size() < 1) { return result; } Node dim = acNode.getDimension(0); UBQualifier previousQualifier = getUBQualifier(dim, in); JavaExpression arrayExpr = JavaExpression.fromNode(node.getTarget()); String arrayString = arrayExpr.toString(); LessThanLengthOf newInfo = (LessThanLengthOf) UBQualifier.createUBQualifier(arrayString, "-1"); UBQualifier combined = previousQualifier.glb(newInfo); AnnotationMirror newAnno = atypeFactory.convertUBQualifierToAnnotation(combined); JavaExpression dimExpr = JavaExpression.fromNode(dim); result.getRegularStore().insertValue(dimExpr, newAnno); propagateToOperands(newInfo, dim, in, result.getRegularStore()); } return result; } /** * {@code node} is known to be {@code typeOfNode}. If the node is a plus or a minus then the * types of the left and right operands can be refined to include offsets. If the node is a * multiplication, its operands can also be refined. See {@link * #propagateToAdditionOperand(LessThanLengthOf, Node, Node, TransferInput, CFStore)}, {@link * #propagateToSubtractionOperands(LessThanLengthOf, NumericalSubtractionNode, TransferInput, * CFStore)}, and {@link #propagateToMultiplicationOperand(LessThanLengthOf, Node, Node, * TransferInput, CFStore)} for details. */ private void propagateToOperands( LessThanLengthOf typeOfNode, Node node, TransferInput in, CFStore store) { if (node instanceof NumericalAdditionNode) { Node right = ((NumericalAdditionNode) node).getRightOperand(); Node left = ((NumericalAdditionNode) node).getLeftOperand(); propagateToAdditionOperand(typeOfNode, left, right, in, store); propagateToAdditionOperand(typeOfNode, right, left, in, store); } else if (node instanceof NumericalSubtractionNode) { propagateToSubtractionOperands(typeOfNode, (NumericalSubtractionNode) node, in, store); } else if (node instanceof NumericalMultiplicationNode) { if (atypeFactory.hasLowerBoundTypeByClass(node, Positive.class)) { Node right = ((NumericalMultiplicationNode) node).getRightOperand(); Node left = ((NumericalMultiplicationNode) node).getLeftOperand(); propagateToMultiplicationOperand(typeOfNode, left, right, in, store); propagateToMultiplicationOperand(typeOfNode, right, left, in, store); } } } /** * {@code other} times {@code node} is known to be {@code typeOfMultiplication}. * *

This implies that if {@code other} is positive, then {@code node} is {@code * typeOfMultiplication}. If {@code other} is greater than 1, then {@code node} is {@code * typeOfMultiplication} plus 1. These are cases 2 and 3, respectively. */ private void propagateToMultiplicationOperand( LessThanLengthOf typeOfMultiplication, Node node, Node other, TransferInput in, CFStore store) { if (atypeFactory.hasLowerBoundTypeByClass(other, Positive.class)) { Long minValue = ValueCheckerUtils.getMinValue( other.getTree(), atypeFactory.getValueAnnotatedTypeFactory()); if (minValue != null && minValue > 1) { typeOfMultiplication = (LessThanLengthOf) typeOfMultiplication.plusOffset(1); } UBQualifier qual = getUBQualifier(node, in); UBQualifier newQual = qual.glb(typeOfMultiplication); JavaExpression je = JavaExpression.fromNode(node); store.insertValue(je, atypeFactory.convertUBQualifierToAnnotation(newQual)); } } /** * The subtraction node, {@code node}, is known to be {@code typeOfSubtraction}. * *

This means that the left node is less than or equal to the length of the array when the * right node is subtracted from the left node. Note that unlike {@link * #propagateToAdditionOperand(LessThanLengthOf, Node, Node, TransferInput, CFStore)} and {@link * #propagateToMultiplicationOperand(LessThanLengthOf, Node, Node, TransferInput, CFStore)}, * this method takes the NumericalSubtractionNode instead of the two operand nodes. This * implements case 4. * * @param typeOfSubtraction type of node * @param node subtraction node that has typeOfSubtraction * @param in a TransferInput * @param store location to store the refined type */ private void propagateToSubtractionOperands( LessThanLengthOf typeOfSubtraction, NumericalSubtractionNode node, TransferInput in, CFStore store) { UBQualifier left = getUBQualifier(node.getLeftOperand(), in); UBQualifier newInfo = typeOfSubtraction.minusOffset(node.getRightOperand(), atypeFactory); UBQualifier newLeft = left.glb(newInfo); JavaExpression leftJe = JavaExpression.fromNode(node.getLeftOperand()); store.insertValue(leftJe, atypeFactory.convertUBQualifierToAnnotation(newLeft)); } /** * Refines the type of {@code operand} to {@code typeOfAddition} plus {@code other}. If {@code * other} is non-negative, then {@code operand} also less than the length of the arrays in * {@code typeOfAddition}. If {@code other} is positive, then {@code operand} also less than the * length of the arrays in {@code typeOfAddition} plus 1. These are cases 5, 6, and 7. * * @param typeOfAddition type of {@code operand + other} * @param operand the Node to refine * @param other the Node added to {@code operand} * @param in a TransferInput * @param store location to store the refined types */ private void propagateToAdditionOperand( LessThanLengthOf typeOfAddition, Node operand, Node other, TransferInput in, CFStore store) { UBQualifier operandQual = getUBQualifier(operand, in); UBQualifier newQual = operandQual.glb(typeOfAddition.plusOffset(other, atypeFactory)); // If the node is NonNegative, add an LTEL to the qual. If Positive, add an LTL. if (atypeFactory.hasLowerBoundTypeByClass(other, Positive.class)) { newQual = newQual.glb(typeOfAddition.plusOffset(1)); } else if (atypeFactory.hasLowerBoundTypeByClass(other, NonNegative.class)) { newQual = newQual.glb(typeOfAddition); } JavaExpression operandJe = JavaExpression.fromNode(operand); store.insertValue(operandJe, atypeFactory.convertUBQualifierToAnnotation(newQual)); } /** * Case 8: if x < y, and y has a type that is related to the length of an array, then x has * the same type, with an offset that is one less. */ @Override protected void refineGT( Node larger, AnnotationMirror largerAnno, Node smaller, AnnotationMirror smallerAnno, CFStore store, TransferInput in) { // larger > smaller UBQualifier largerQual = UBQualifier.createUBQualifier( largerAnno, (UpperBoundChecker) atypeFactory.getChecker()); // larger + 1 >= smaller UBQualifier largerQualPlus1 = largerQual.plusOffset(1); UBQualifier rightQualifier = UBQualifier.createUBQualifier( smallerAnno, (UpperBoundChecker) atypeFactory.getChecker()); UBQualifier refinedRight = rightQualifier.glb(largerQualPlus1); if (largerQualPlus1.isLessThanLengthQualifier()) { propagateToOperands((LessThanLengthOf) largerQualPlus1, smaller, in, store); } refineSubtrahendWithOffset(larger, smaller, true, in, store); JavaExpression rightJe = JavaExpression.fromNode(smaller); store.insertValue(rightJe, atypeFactory.convertUBQualifierToAnnotation(refinedRight)); } /** * Case 9: if x ≤ y, and y has a type that is related to the length of an array, then x has * the same type. */ @Override protected void refineGTE( Node left, AnnotationMirror leftAnno, Node right, AnnotationMirror rightAnno, CFStore store, TransferInput in) { UBQualifier leftQualifier = UBQualifier.createUBQualifier( leftAnno, (UpperBoundChecker) atypeFactory.getChecker()); UBQualifier rightQualifier = UBQualifier.createUBQualifier( rightAnno, (UpperBoundChecker) atypeFactory.getChecker()); UBQualifier refinedRight = rightQualifier.glb(leftQualifier); if (leftQualifier.isLessThanLengthQualifier()) { propagateToOperands((LessThanLengthOf) leftQualifier, right, in, store); } refineSubtrahendWithOffset(left, right, false, in, store); JavaExpression rightJe = JavaExpression.fromNode(right); store.insertValue(rightJe, atypeFactory.convertUBQualifierToAnnotation(refinedRight)); } /** * Refines the subtrahend in a subtraction which is greater than or equal to a certain offset. * The type of the subtrahend is refined to the type of the minuend with the offset added. This * is case 10. * *

This is based on the fact that if {@code (minuend - subtrahend) >= offset}, and {@code * minuend + o < l}, then {@code subtrahend + o + offset < l}. * *

If {@code gtNode} is not a {@link NumericalSubtractionNode}, the method does nothing. * * @param gtNode the node that is greater or equal to the offset * @param offsetNode a node part of the offset * @param offsetAddOne whether to add one to the offset * @param in input of the transfer function * @param store location to store the refined types */ private void refineSubtrahendWithOffset( Node gtNode, Node offsetNode, boolean offsetAddOne, TransferInput in, CFStore store) { if (gtNode instanceof NumericalSubtractionNode) { NumericalSubtractionNode subtractionNode = (NumericalSubtractionNode) gtNode; Node minuend = subtractionNode.getLeftOperand(); UBQualifier minuendQual = getUBQualifier(minuend, in); Node subtrahend = subtractionNode.getRightOperand(); UBQualifier subtrahendQual = getUBQualifier(subtrahend, in); UBQualifier newQual = subtrahendQual.glb( minuendQual .plusOffset(offsetNode, atypeFactory) .plusOffset(offsetAddOne ? 1 : 0)); JavaExpression subtrahendJe = JavaExpression.fromNode(subtrahend); store.insertValue(subtrahendJe, atypeFactory.convertUBQualifierToAnnotation(newQual)); } } /** Implements case 11. */ @Override protected TransferResult strengthenAnnotationOfEqualTo( TransferResult res, Node firstNode, Node secondNode, CFValue firstValue, CFValue secondValue, boolean notEqualTo) { TransferResult result = super.strengthenAnnotationOfEqualTo( res, firstNode, secondNode, firstValue, secondValue, notEqualTo); IndexRefinementInfo rfi = new IndexRefinementInfo(result, analysis, firstNode, secondNode); if (rfi.leftAnno == null || rfi.rightAnno == null) { return result; } CFStore equalsStore = notEqualTo ? rfi.elseStore : rfi.thenStore; CFStore notEqualStore = notEqualTo ? rfi.thenStore : rfi.elseStore; refineEq(rfi.left, rfi.leftAnno, rfi.right, rfi.rightAnno, equalsStore); refineNeqSequenceLength(rfi.left, rfi.right, rfi.rightAnno, notEqualStore); refineNeqSequenceLength(rfi.right, rfi.left, rfi.leftAnno, notEqualStore); return rfi.newResult; } /** Refines the type of the left and right node to glb of the left and right annotation. */ private void refineEq( Node left, AnnotationMirror leftAnno, Node right, AnnotationMirror rightAnno, CFStore store) { UBQualifier leftQualifier = UBQualifier.createUBQualifier( leftAnno, (UpperBoundChecker) atypeFactory.getChecker()); UBQualifier rightQualifier = UBQualifier.createUBQualifier( rightAnno, (UpperBoundChecker) atypeFactory.getChecker()); UBQualifier glb = rightQualifier.glb(leftQualifier); AnnotationMirror glbAnno = atypeFactory.convertUBQualifierToAnnotation(glb); List internalsRight = splitAssignments(right); for (Node internal : internalsRight) { JavaExpression rightJe = JavaExpression.fromNode(internal); store.insertValue(rightJe, glbAnno); } List internalsLeft = splitAssignments(left); for (Node internal : internalsLeft) { JavaExpression leftJe = JavaExpression.fromNode(internal); store.insertValue(leftJe, glbAnno); } } /** * If lengthAccess node is an sequence length field or method access (optionally with a constant * offset subtracted) and the other node is less than or equal to that sequence length (minus * the offset), then refine the other node's type to less than the sequence length (minus the * offset). This is case 12. */ private void refineNeqSequenceLength( Node lengthAccess, Node otherNode, AnnotationMirror otherNodeAnno, CFStore store) { // If lengthAccess is "receiver.length - c" where c is an integer constant, // then lengthOffset is "c". int lengthOffset = 0; if (lengthAccess instanceof NumericalSubtractionNode) { NumericalSubtractionNode subtraction = (NumericalSubtractionNode) lengthAccess; Node offsetNode = subtraction.getRightOperand(); Long offsetValue = ValueCheckerUtils.getExactValue( offsetNode.getTree(), atypeFactory.getValueAnnotatedTypeFactory()); if (offsetValue != null && offsetValue > Integer.MIN_VALUE && offsetValue <= Integer.MAX_VALUE) { lengthOffset = offsetValue.intValue(); lengthAccess = subtraction.getLeftOperand(); } else { // Subtraction of non-constant expressions is not supported return; } } JavaExpression receiver = null; if (NodeUtils.isArrayLengthFieldAccess(lengthAccess)) { FieldAccess fa = (FieldAccess) JavaExpression.fromNodeFieldAccess((FieldAccessNode) lengthAccess); receiver = fa.getReceiver(); } else if (atypeFactory.getMethodIdentifier().isLengthOfMethodInvocation(lengthAccess)) { JavaExpression ma = JavaExpression.fromNode(lengthAccess); if (ma instanceof MethodCall) { receiver = ((MethodCall) ma).getReceiver(); } } if (receiver != null && !receiver.containsUnknown()) { UBQualifier otherQualifier = UBQualifier.createUBQualifier( otherNodeAnno, (UpperBoundChecker) atypeFactory.getChecker()); String sequence = receiver.toString(); // Check if otherNode + c - 1 < receiver.length if (otherQualifier.hasSequenceWithOffset(sequence, lengthOffset - 1)) { // Add otherNode + c < receiver.length UBQualifier newQualifier = UBQualifier.createUBQualifier(sequence, Integer.toString(lengthOffset)); otherQualifier = otherQualifier.glb(newQualifier); for (Node internal : splitAssignments(otherNode)) { JavaExpression leftJe = JavaExpression.fromNode(internal); store.insertValue( leftJe, atypeFactory.convertUBQualifierToAnnotation(otherQualifier)); } } } } /** * If some Node a is known to be less than the length of some array, x, then, the type of a + b, * is @LTLengthOf(value="x", offset="-b"). If b is known to be less than the length of some * other array, y, then the type of a + b is @LTLengthOf(value={"x", "y"}, offset={"-b", "-a"}). * *

Alternatively, if a is known to be less than the length of x when some offset, o, is added * to a (the type of a is @LTLengthOf(value="x", offset="o")), then the type of a + b * is @LTLengthOf(value="x",offset="o - b"). (Note, if "o - b" can be computed, then it is and * the result is used in the annotation.) * *

In addition, If expression i has type @LTLengthOf(value = "f2", offset = "f1.length") int * and expression j is less than or equal to the length of f1, then the type of i + j is * .@LTLengthOf("f2"). * *

These three cases correspond to cases 13-15. */ @Override public TransferResult visitNumericalAddition( NumericalAdditionNode n, TransferInput in) { // type of leftNode + rightNode is glb(t, s) where // t = minusOffset(type(leftNode), rightNode) and // s = minusOffset(type(rightNode), leftNode) UBQualifier left = getUBQualifierForAddition(n.getLeftOperand(), in); UBQualifier t = left.minusOffset(n.getRightOperand(), atypeFactory); UBQualifier right = getUBQualifierForAddition(n.getRightOperand(), in); UBQualifier s = right.minusOffset(n.getLeftOperand(), atypeFactory); UBQualifier glb = t.glb(s); if (left.isLessThanLengthQualifier() && right.isLessThanLengthQualifier()) { // If expression i has type @LTLengthOf(value = "f2", offset = "f1.length") int and // expression j is less than or equal to the length of f1, then the type of i + j is // @LTLengthOf("f2"). UBQualifier r = removeSequenceLengths((LessThanLengthOf) left, (LessThanLengthOf) right); glb = glb.glb(r); UBQualifier l = removeSequenceLengths((LessThanLengthOf) right, (LessThanLengthOf) left); glb = glb.glb(l); } return createTransferResult(n, in, glb); } /** * Return the result of adding i to j. * *

When expression i has type {@code @LTLengthOf(value = "f2", offset = "f1.length") int} and * expression j is less than or equal to the length of f1, then the type of i + j * is @LTLengthOf("f2"). * *

When expression i has type {@code @LTLengthOf (value = "f2", offset = "f1.length - 1") * int} and expression j is less than the length of f1, then the type of i + j * is @LTLengthOf("f2"). * * @param i the type of the expression added to j * @param j the type of the expression added to i * @return the type of i + j */ private UBQualifier removeSequenceLengths(LessThanLengthOf i, LessThanLengthOf j) { List lessThan = new ArrayList<>(); List lessThanOrEqual = new ArrayList<>(); for (String sequence : i.getSequences()) { if (i.isLessThanLengthOf(sequence)) { lessThan.add(sequence); } else if (i.hasSequenceWithOffset(sequence, -1)) { lessThanOrEqual.add(sequence); } } // Creates a qualifier that is the same a j with the array.length offsets removed. If // an offset doesn't have an array.length, then the offset/array pair is removed. If // there are no such pairs, Unknown is returned. UBQualifier lessThanEqQ = j.removeSequenceLengthAccess(lessThanOrEqual); // Creates a qualifier that is the same a j with the array.length - 1 offsets removed. If // an offset doesn't have an array.length, then the offset/array pair is removed. If // there are no such pairs, Unknown is returned. UBQualifier lessThanQ = j.removeSequenceLengthAccessAndNeg1(lessThan); return lessThanEqQ.glb(lessThanQ); } /** * If some Node a is known to be less than the length of some sequence x, then the type of a - b * is @LTLengthOf(value="x", offset="b"). If b is known to be less than the length of some other * sequence, this doesn't add any information about the type of a - b. But, if b is non-negative * or positive, then a - b should keep the types of a. This corresponds to cases 16 and 17. */ @Override public TransferResult visitNumericalSubtraction( NumericalSubtractionNode n, TransferInput in) { UBQualifier left = getUBQualifier(n.getLeftOperand(), in); UBQualifier leftWithOffset = left.plusOffset(n.getRightOperand(), atypeFactory); if (atypeFactory.hasLowerBoundTypeByClass(n.getRightOperand(), NonNegative.class) || atypeFactory.hasLowerBoundTypeByClass(n.getRightOperand(), Positive.class)) { // If the right side of the expression is NN or POS, then all the left side's // annotations should be kept. if (left.isLessThanLengthQualifier()) { leftWithOffset = left.glb(leftWithOffset); } } // If the result of a numerical subtraction would be LTEL(b) or LTL(b), and b is HSS(a, // from, to), and the subtraction node itself is i - from where i is LTEL(b), then the // result is LTEL(a). If i is LTL(b) instead, the result is LTL(a). if (leftWithOffset.isLessThanLengthQualifier()) { LessThanLengthOf subtractionResult = (LessThanLengthOf) leftWithOffset; for (String b : subtractionResult.getSequences()) { if (subtractionResult.hasSequenceWithOffset(b, -1) || subtractionResult.hasSequenceWithOffset(b, 0)) { TreePath currentPath = this.atypeFactory.getPath(n.getTree()); JavaExpression je; try { je = UpperBoundVisitor.parseJavaExpressionString( b, atypeFactory, currentPath); } catch (NullPointerException npe) { // I have no idea why this seems to happen only on a few JDK classes. It // appears to only happen during the preprocessing step - the NPE is thrown // while trying to find the enclosing class of a class tree, which is null. // I can't find a reproducible test case that's smaller than the size of // DualPivotQuicksort. Since this refinement is optional, but useful // elsewhere, catching this NPE here and returning is always safe. return createTransferResult(n, in, leftWithOffset); } Subsequence subsequence = Subsequence.getSubsequenceFromReceiver(je, atypeFactory); if (subsequence != null) { String from = subsequence.from; String to = subsequence.to; String a = subsequence.array; JavaExpression leftOp = JavaExpression.fromNode(n.getLeftOperand()); JavaExpression rightOp = JavaExpression.fromNode(n.getRightOperand()); if (rightOp.toString().equals(from)) { LessThanAnnotatedTypeFactory lessThanAtypeFactory = atypeFactory.getLessThanAnnotatedTypeFactory(); AnnotationMirror lessThanType = lessThanAtypeFactory .getAnnotatedType(n.getLeftOperand().getTree()) .getAnnotation(LessThan.class); if (lessThanType != null && lessThanAtypeFactory.isLessThan(lessThanType, to)) { UBQualifier ltlA = UBQualifier.createUBQualifier(a, "0"); leftWithOffset = leftWithOffset.glb(ltlA); } else if (leftOp.toString().equals(to) || (lessThanType != null && lessThanAtypeFactory.isLessThanOrEqual( lessThanType, to))) { // It's necessary to check if leftOp == to because LessThan doesn't // infer that things are less than or equal to themselves. UBQualifier ltelA = UBQualifier.createUBQualifier(a, "-1"); leftWithOffset = leftWithOffset.glb(ltelA); } } } } } } return createTransferResult(n, in, leftWithOffset); } /** * Computes a type of a sequence length access. This is case 18. * * @param n sequence length access node */ private @Nullable TransferResult visitLengthAccess( Node n, TransferInput in, JavaExpression sequenceJe, Tree sequenceTree) { if (sequenceTree == null) { return null; } // Look up the SameLen type of the sequence. AnnotationMirror sameLenAnno = atypeFactory.sameLenAnnotationFromTree(sequenceTree); List sameLenSequences; if (sameLenAnno == null) { sameLenSequences = Collections.singletonList(sequenceJe.toString()); } else { sameLenSequences = AnnotationUtils.getElementValueArray( sameLenAnno, atypeFactory.sameLenValueElement, String.class); String sequenceString = sequenceJe.toString(); if (!sameLenSequences.contains(sequenceString)) { sameLenSequences.add(sequenceString); } } List offsets = Collections.nCopies(sameLenSequences.size(), "-1"); if (CFAbstractStore.canInsertJavaExpression(sequenceJe)) { UBQualifier qualifier = UBQualifier.createUBQualifier(sameLenSequences, offsets); UBQualifier previous = getUBQualifier(n, in); return createTransferResult(n, in, qualifier.glb(previous)); } return null; } /** * If n is an array length field access, then the type of a.length is the glb * of @LTEqLengthOf("a") and the value of a.length in the store. This is case 19. */ @Override public TransferResult visitFieldAccess( FieldAccessNode n, TransferInput in) { if (NodeUtils.isArrayLengthFieldAccess(n)) { FieldAccess arrayLength = (FieldAccess) JavaExpression.fromNodeFieldAccess(n); JavaExpression arrayJe = arrayLength.getReceiver(); Tree arrayTree = n.getReceiver().getTree(); TransferResult result = visitLengthAccess(n, in, arrayJe, arrayTree); if (result != null) { return result; } } return super.visitFieldAccess(n, in); } /** * If n is a String.length() method invocation, then the type of s.length() is the glb * of @LTEqLengthOf("s") and the value of s.length() in the store. This is case 20. */ @Override public TransferResult visitMethodInvocation( MethodInvocationNode n, TransferInput in) { if (atypeFactory.getMethodIdentifier().isLengthOfMethodInvocation(n)) { JavaExpression stringLength = JavaExpression.fromNode(n); if (stringLength instanceof MethodCall) { JavaExpression receiverJe = ((MethodCall) stringLength).getReceiver(); Tree receiverTree = n.getTarget().getReceiver().getTree(); // receiverTree is null when the receiver is implicit "this". if (receiverTree != null) { TransferResult result = visitLengthAccess(n, in, receiverJe, receiverTree); if (result != null) { return result; } } } } return super.visitMethodInvocation(n, in); } /** * Returns the UBQualifier for a node, with additional refinement useful specifically for * integer addition, based on the information from subcheckers of the Index Checker. * * @param n the node * @param in dataflow analysis transfer input * @return the UBQualifier for {@code node} */ private UBQualifier getUBQualifierForAddition(Node n, TransferInput in) { // The method takes the greatest lower bound of the qualifier returned by // getUBQualifier and a qualifier created from a SubstringIndexFor annotation, if such // annotation is present and the index is known to be non-negative. UBQualifier ubQualifier = getUBQualifier(n, in); Tree nodeTree = n.getTree(); // Annotation from the Substring Index hierarchy AnnotatedTypeMirror substringIndexType = atypeFactory.getSubstringIndexAnnotatedTypeFactory().getAnnotatedType(nodeTree); AnnotationMirror substringIndexAnno = substringIndexType.getAnnotation(SubstringIndexFor.class); // Annotation from the Lower bound hierarchy AnnotatedTypeMirror lowerBoundType = atypeFactory.getLowerBoundAnnotatedTypeFactory().getAnnotatedType(nodeTree); // If the index has an SubstringIndexFor annotation and at the same time is non-negative, // convert the SubstringIndexFor annotation to a upper bound qualifier. if (substringIndexAnno != null && (lowerBoundType.hasAnnotation(NonNegative.class) || lowerBoundType.hasAnnotation(Positive.class))) { UBQualifier substringIndexQualifier = UBQualifier.createUBQualifier( substringIndexAnno, (UpperBoundChecker) atypeFactory.getChecker()); ubQualifier = ubQualifier.glb(substringIndexQualifier); } return ubQualifier; } /** * Returns the UBQualifier for node. It does this by finding a {@link CFValue} for node. First * it checks the store in the transfer input. If one isn't there, the analysis is checked. If * the UNKNOWN qualifier is returned, then the AnnotatedTypeMirror from the type factory is * used. * * @param n node * @param in transfer input * @return the UBQualifier for node */ private UBQualifier getUBQualifier(Node n, TransferInput in) { QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); JavaExpression je = JavaExpression.fromNode(n); CFValue value = null; if (CFAbstractStore.canInsertJavaExpression(je)) { value = in.getRegularStore().getValue(je); } if (value == null) { value = analysis.getValue(n); } UBQualifier qualifier = getUBQualifier(hierarchy, value); if (qualifier.isUnknown()) { // The qualifier from the store or analysis might be UNKNOWN if there was some error. // For example, // @LTLength("a") int i = 4; // error // The type of i in the store is @UpperBoundUnknown, but the type of i as computed by // the type factory is @LTLength("a"), so use that type. CFValue valueFromFactory = getValueFromFactory(n.getTree(), n); return getUBQualifier(hierarchy, valueFromFactory); } return qualifier; } private UBQualifier getUBQualifier(QualifierHierarchy hierarchy, CFValue value) { if (value == null) { return UpperBoundUnknownQualifier.UNKNOWN; } AnnotationMirrorSet set = value.getAnnotations(); AnnotationMirror anno = hierarchy.findAnnotationInHierarchy(set, atypeFactory.UNKNOWN); if (anno == null) { return UpperBoundUnknownQualifier.UNKNOWN; } return UBQualifier.createUBQualifier(anno, (UpperBoundChecker) atypeFactory.getChecker()); } private TransferResult createTransferResult( Node n, TransferInput in, UBQualifier qualifier) { AnnotationMirror newAnno = atypeFactory.convertUBQualifierToAnnotation(qualifier); CFValue value = analysis.createSingleAnnotationValue(newAnno, n.getType()); return createTransferResult(value, in); } @Override public TransferResult visitCase( CaseNode n, TransferInput in) { TransferResult result = super.visitCase(n, in); // Refines subtrahend in the switch expression // TODO: This cannot be done in strengthenAnnotationOfEqualTo, because that does not provide // transfer input. List caseNodes = n.getCaseOperands(); AssignmentNode assign = n.getSwitchOperand(); Node switchNode = assign.getExpression(); for (Node caseNode : caseNodes) { refineSubtrahendWithOffset(switchNode, caseNode, false, in, result.getThenStore()); } return result; } @Override public TransferResult visitIntegerLiteral( IntegerLiteralNode n, TransferInput pi) { TransferResult result = super.visitIntegerLiteral(n, pi); int intValue = n.getValue(); AnnotationMirror newAnno; switch (intValue) { case 0: newAnno = atypeFactory.ZERO; break; case -1: newAnno = atypeFactory.NEGATIVEONE; break; default: return result; } CFValue c = new CFValue(analysis, AnnotationMirrorSet.singleton(newAnno), intTM); return new RegularTransferResult<>(c, result.getRegularStore()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy