
org.checkerframework.common.value.ValueTransfer Maven / Gradle / Ivy
package org.checkerframework.common.value;
import com.sun.source.tree.Tree;
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;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.common.value.qual.ArrayLen;
import org.checkerframework.common.value.qual.ArrayLenRange;
import org.checkerframework.common.value.qual.BoolVal;
import org.checkerframework.common.value.qual.BottomVal;
import org.checkerframework.common.value.qual.DoubleVal;
import org.checkerframework.common.value.qual.IntVal;
import org.checkerframework.common.value.qual.StringVal;
import org.checkerframework.common.value.qual.UnknownVal;
import org.checkerframework.common.value.util.NumberMath;
import org.checkerframework.common.value.util.NumberUtils;
import org.checkerframework.common.value.util.Range;
import org.checkerframework.dataflow.analysis.ConditionalTransferResult;
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.BitwiseAndNode;
import org.checkerframework.dataflow.cfg.node.BitwiseComplementNode;
import org.checkerframework.dataflow.cfg.node.BitwiseOrNode;
import org.checkerframework.dataflow.cfg.node.BitwiseXorNode;
import org.checkerframework.dataflow.cfg.node.ConditionalAndNode;
import org.checkerframework.dataflow.cfg.node.ConditionalNotNode;
import org.checkerframework.dataflow.cfg.node.ConditionalOrNode;
import org.checkerframework.dataflow.cfg.node.FieldAccessNode;
import org.checkerframework.dataflow.cfg.node.FloatingDivisionNode;
import org.checkerframework.dataflow.cfg.node.FloatingRemainderNode;
import org.checkerframework.dataflow.cfg.node.GreaterThanNode;
import org.checkerframework.dataflow.cfg.node.GreaterThanOrEqualNode;
import org.checkerframework.dataflow.cfg.node.IntegerDivisionNode;
import org.checkerframework.dataflow.cfg.node.IntegerRemainderNode;
import org.checkerframework.dataflow.cfg.node.LeftShiftNode;
import org.checkerframework.dataflow.cfg.node.LessThanNode;
import org.checkerframework.dataflow.cfg.node.LessThanOrEqualNode;
import org.checkerframework.dataflow.cfg.node.MethodAccessNode;
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.NumericalMinusNode;
import org.checkerframework.dataflow.cfg.node.NumericalMultiplicationNode;
import org.checkerframework.dataflow.cfg.node.NumericalPlusNode;
import org.checkerframework.dataflow.cfg.node.NumericalSubtractionNode;
import org.checkerframework.dataflow.cfg.node.SignedRightShiftNode;
import org.checkerframework.dataflow.cfg.node.StringConcatenateAssignmentNode;
import org.checkerframework.dataflow.cfg.node.StringConcatenateNode;
import org.checkerframework.dataflow.cfg.node.StringConversionNode;
import org.checkerframework.dataflow.cfg.node.UnsignedRightShiftNode;
import org.checkerframework.dataflow.util.NodeUtils;
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.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.javacutil.TypesUtils;
public class ValueTransfer extends CFTransfer {
ValueAnnotatedTypeFactory atypefactory;
public ValueTransfer(CFAbstractAnalysis analysis) {
super(analysis);
atypefactory = (ValueAnnotatedTypeFactory) analysis.getTypeFactory();
}
/** Returns a range of possible lengths for an integer from a range, as casted to a String. */
private Range getIntRangeStringLengthRange(Node subNode, TransferInput p) {
Range valueRange = getIntRange(subNode, p);
// Get lengths of the bounds
int fromLength = Long.toString(valueRange.from).length();
int toLength = Long.toString(valueRange.to).length();
int lowerLength = Math.min(fromLength, toLength);
// In case the range contains 0, the minimum length is 1 even if both bounds are longer
if (valueRange.contains(0)) {
lowerLength = 1;
}
int upperLength = Math.max(fromLength, toLength);
return new Range(lowerLength, upperLength);
}
/** Returns a range of possible lengths for {@code subNode}, as casted to a String. */
private Range getStringLengthRange(Node subNode, TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
AnnotationMirror arrayLenRangeAnno =
AnnotationUtils.getAnnotationByClass(value.getAnnotations(), ArrayLenRange.class);
if (arrayLenRangeAnno != null) {
return ValueAnnotatedTypeFactory.getRange(arrayLenRangeAnno);
}
// @BottomVal
if (AnnotationUtils.containsSameByClass(value.getAnnotations(), BottomVal.class)) {
return Range.NOTHING;
}
TypeKind subNodeTypeKind = subNode.getType().getKind();
// handle values converted to string (ints, longs, longs with @IntRange)
if (subNode instanceof StringConversionNode) {
return getStringLengthRange(((StringConversionNode) subNode).getOperand(), p);
} else if (isIntRange(subNode, p)) {
return getIntRangeStringLengthRange(subNode, p);
} else if (subNodeTypeKind == TypeKind.INT) {
// ints are between 1 and 11 characters long
return new Range(1, 11);
} else if (subNodeTypeKind == TypeKind.LONG) {
// longs are between 1 and 20 characters long
return new Range(1, 20);
}
return new Range(0, Integer.MAX_VALUE);
}
/**
* Returns a list of possible lengths for {@code subNode}, as casted to a String. Returns null
* if {@code subNode}'s type is top/unknown. Returns an empty list if {@code subNode}'s type is
* bottom.
*/
private List getStringLengths(Node subNode, TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
// @ArrayLen
AnnotationMirror arrayLenAnno =
AnnotationUtils.getAnnotationByClass(value.getAnnotations(), ArrayLen.class);
if (arrayLenAnno != null) {
return ValueAnnotatedTypeFactory.getArrayLength(arrayLenAnno);
}
// @BottomVal
if (AnnotationUtils.containsSameByClass(value.getAnnotations(), BottomVal.class)) {
return new ArrayList();
}
TypeKind subNodeTypeKind = subNode.getType().getKind();
// handle values converted to string (characters, bytes, shorts, ints with @IntRange)
if (subNode instanceof StringConversionNode) {
return getStringLengths(((StringConversionNode) subNode).getOperand(), p);
} else if (subNodeTypeKind == TypeKind.CHAR) {
// characters always have length 1
return Collections.singletonList(1);
} else if (isIntRange(subNode, p)) {
// Try to get a list of lengths from a range of integer values converted to string
// @IntVal is not checked for, because if it is present, we would already have the
// actual string values
Range lengthRange = getIntRangeStringLengthRange(subNode, p);
return ValueCheckerUtils.getValuesFromRange(lengthRange, Integer.class);
} else if (subNodeTypeKind == TypeKind.BYTE) {
// bytes are between 1 and 4 characters long
return ValueCheckerUtils.getValuesFromRange(new Range(1, 4), Integer.class);
} else if (subNodeTypeKind == TypeKind.SHORT) {
// shorts are between 1 and 6 characters long
return ValueCheckerUtils.getValuesFromRange(new Range(1, 6), Integer.class);
} else {
return null;
}
}
/**
* Returns a list of possible values for {@code subNode}, as casted to a String. Returns null if
* {@code subNode}'s type is top/unknown. Returns an empty list if {@code subNode}'s type is
* bottom.
*/
private List getStringValues(Node subNode, TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
// @StringVal, @UnknownVal, @BottomVal
AnnotationMirror stringAnno =
AnnotationUtils.getAnnotationByClass(value.getAnnotations(), StringVal.class);
if (stringAnno != null) {
return ValueAnnotatedTypeFactory.getStringValues(stringAnno);
}
AnnotationMirror topAnno =
AnnotationUtils.getAnnotationByClass(value.getAnnotations(), UnknownVal.class);
if (topAnno != null) {
return null;
}
AnnotationMirror bottomAnno =
AnnotationUtils.getAnnotationByClass(value.getAnnotations(), BottomVal.class);
if (bottomAnno != null) {
return new ArrayList();
}
// @IntVal, @IntRange, @DoubleVal, @BoolVal (have to be converted to string)
List extends Object> values;
AnnotationMirror numberAnno =
AnnotationUtils.getAnnotationByClass(value.getAnnotations(), BoolVal.class);
if (numberAnno != null) {
values = getBooleanValues(subNode, p);
} else if (subNode.getType().getKind() == TypeKind.CHAR) {
values = getCharValues(subNode, p);
} else if (subNode instanceof StringConversionNode) {
return getStringValues(((StringConversionNode) subNode).getOperand(), p);
} else if (isIntRange(subNode, p)) {
Range range = getIntRange(subNode, p);
List longValues = ValueCheckerUtils.getValuesFromRange(range, Long.class);
values = NumberUtils.castNumbers(subNode.getType(), longValues);
} else {
values = getNumericalValues(subNode, p);
}
if (values == null) {
return null;
}
List stringValues = new ArrayList();
for (Object o : values) {
stringValues.add(o.toString());
}
// Empty list means bottom value
return stringValues.isEmpty() ? Collections.singletonList("null") : stringValues;
}
/** Get possible boolean values from @BoolVal. */
private List getBooleanValues(Node subNode, TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
AnnotationMirror intAnno =
AnnotationUtils.getAnnotationByClass(value.getAnnotations(), BoolVal.class);
return ValueAnnotatedTypeFactory.getBooleanValues(intAnno);
}
/** Get possible char values from annotation @IntRange or @IntVal. */
private List getCharValues(Node subNode, TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
AnnotationMirror intAnno;
intAnno = AnnotationUtils.getAnnotationByClass(value.getAnnotations(), IntVal.class);
if (intAnno != null) {
return ValueAnnotatedTypeFactory.getCharValues(intAnno);
}
if (atypefactory.isIntRange(value.getAnnotations())) {
intAnno =
atypefactory
.getQualifierHierarchy()
.findAnnotationInHierarchy(
value.getAnnotations(), atypefactory.UNKNOWNVAL);
Range range = ValueAnnotatedTypeFactory.getRange(intAnno);
return ValueCheckerUtils.getValuesFromRange(range, Character.class);
}
return new ArrayList();
}
private AnnotationMirror getValueAnnotation(Node subNode, TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
return getValueAnnotation(value);
}
private AnnotationMirror getValueAnnotation(CFValue cfValue) {
return atypefactory
.getQualifierHierarchy()
.findAnnotationInHierarchy(cfValue.getAnnotations(), atypefactory.UNKNOWNVAL);
}
/**
* Returns a list of possible values, or null if no estimate is available and any value is
* possible.
*/
private List extends Number> getNumericalValues(
Node subNode, TransferInput p) {
AnnotationMirror valueAnno = getValueAnnotation(subNode, p);
return getNumericalValues(subNode, valueAnno);
}
private List extends Number> getNumericalValues(Node subNode, AnnotationMirror valueAnno) {
if (valueAnno == null || AnnotationUtils.areSameByClass(valueAnno, UnknownVal.class)) {
return null;
} else if (AnnotationUtils.areSameByClass(valueAnno, BottomVal.class)) {
return new ArrayList<>();
}
List extends Number> values;
if (AnnotationUtils.areSameByClass(valueAnno, IntVal.class)) {
values = ValueAnnotatedTypeFactory.getIntValues(valueAnno);
} else if (AnnotationUtils.areSameByClass(valueAnno, DoubleVal.class)) {
values = ValueAnnotatedTypeFactory.getDoubleValues(valueAnno);
} else {
return null;
}
return NumberUtils.castNumbers(subNode.getType(), values);
}
/** Get possible integer range from annotation. */
private Range getIntRange(Node subNode, TransferInput p) {
AnnotationMirror val = getValueAnnotation(subNode, p);
return getIntRangeFromAnnotation(subNode, val);
}
private Range getIntRangeFromAnnotation(Node node, AnnotationMirror val) {
Range range;
if (val == null || AnnotationUtils.areSameByClass(val, UnknownVal.class)) {
range = Range.EVERYTHING;
} else if (atypefactory.isIntRange(val)) {
range = ValueAnnotatedTypeFactory.getRange(val);
} else if (AnnotationUtils.areSameByClass(val, IntVal.class)) {
List values = ValueAnnotatedTypeFactory.getIntValues(val);
range = ValueCheckerUtils.getRangeFromValues(values);
} else if (AnnotationUtils.areSameByClass(val, DoubleVal.class)) {
List values = ValueAnnotatedTypeFactory.getDoubleValues(val);
range = ValueCheckerUtils.getRangeFromValues(values);
} else if (AnnotationUtils.areSameByClass(val, BottomVal.class)) {
return Range.NOTHING;
} else {
range = Range.EVERYTHING;
}
return NumberUtils.castRange(node.getType(), range);
}
/** a helper function to determine if this node is annotated with {@code @IntRange} */
private boolean isIntRange(Node subNode, TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
return atypefactory.isIntRange(value.getAnnotations());
}
/** a helper function to determine if this node is annotated with {@code @UnknownVal} */
private boolean isIntegralUnknownVal(Node node, AnnotationMirror anno) {
return AnnotationUtils.areSameByClass(anno, UnknownVal.class)
&& TypesUtils.isIntegral(node.getType());
}
/**
* a helper function to determine if this node is annotated with {@code @IntRange} or
* {@code @UnknownVal}
*/
private boolean isIntRangeOrIntegralUnknownVal(Node node, TransferInput p) {
AnnotationMirror anno = getValueAnnotation(p.getValueOfSubNode(node));
return isIntRange(node, p) || isIntegralUnknownVal(node, anno);
}
/**
* Create a new transfer result based on the original result and the new annotation.
*
* @param result the original result
* @param resultAnno the new annotation
* @return the new transfer result
*/
private TransferResult createNewResult(
TransferResult result, AnnotationMirror resultAnno) {
CFValue newResultValue =
analysis.createSingleAnnotationValue(
resultAnno, result.getResultValue().getUnderlyingType());
return new RegularTransferResult<>(newResultValue, result.getRegularStore());
}
/** Create a boolean transfer result. */
private TransferResult createNewResultBoolean(
CFStore thenStore,
CFStore elseStore,
List resultValues,
TypeMirror underlyingType) {
AnnotationMirror boolVal = atypefactory.createBooleanAnnotation(resultValues);
CFValue newResultValue = analysis.createSingleAnnotationValue(boolVal, underlyingType);
if (elseStore != null) {
return new ConditionalTransferResult<>(newResultValue, thenStore, elseStore);
} else {
return new RegularTransferResult<>(newResultValue, thenStore);
}
}
@Override
public TransferResult visitFieldAccess(
FieldAccessNode node, TransferInput in) {
TransferResult result = super.visitFieldAccess(node, in);
refineArrayAtLengthAccess(node, result.getRegularStore());
return result;
}
@Override
public TransferResult visitMethodInvocation(
MethodInvocationNode n, TransferInput p) {
TransferResult result = super.visitMethodInvocation(n, p);
refineStringAtLengthInvocation(n, result.getRegularStore());
return result;
}
/**
* If array.length is encountered, transform its @IntVal annotation into an @ArrayLen annotation
* for array.
*/
private void refineArrayAtLengthAccess(FieldAccessNode arrayLengthNode, CFStore store) {
if (!NodeUtils.isArrayLengthFieldAccess(arrayLengthNode)) {
return;
}
refineAtLengthAccess(arrayLengthNode, arrayLengthNode.getReceiver(), store);
}
/**
* If string.length() is encountered, transform its @IntVal annotation into an @ArrayLen
* annotation for string.
*/
private void refineStringAtLengthInvocation(
MethodInvocationNode stringLengthNode, CFStore store) {
MethodAccessNode methodAccessNode = stringLengthNode.getTarget();
if (atypefactory.getMethodIdentifier().isStringLengthMethod(methodAccessNode.getMethod())) {
refineAtLengthAccess(stringLengthNode, methodAccessNode.getReceiver(), store);
}
}
/** Gets a value checker annotation relevant for an array or a string. */
private AnnotationMirror getArrayOrStringAnnotation(Node arrayOrStringNode) {
AnnotationMirror arrayOrStringAnno =
atypefactory.getAnnotationMirror(arrayOrStringNode.getTree(), StringVal.class);
if (arrayOrStringAnno == null) {
arrayOrStringAnno =
atypefactory.getAnnotationMirror(arrayOrStringNode.getTree(), ArrayLen.class);
}
if (arrayOrStringAnno == null) {
arrayOrStringAnno =
atypefactory.getAnnotationMirror(
arrayOrStringNode.getTree(), ArrayLenRange.class);
}
return arrayOrStringAnno;
}
/**
* Transform @IntVal or @IntRange annotations of a array or string length into an @ArrayLen
* or @ArrayLenRange annotation for the array or string.
*/
private void refineAtLengthAccess(Node lengthNode, Node receiverNode, CFStore store) {
Receiver lengthRec = FlowExpressions.internalReprOf(analysis.getTypeFactory(), lengthNode);
// If the expression is not representable (for example if String.length() for some reason is
// not marked @Pure, then do not refine.
if (lengthRec instanceof FlowExpressions.Unknown) {
return;
}
CFValue value = store.getValue(lengthRec);
if (value == null) {
return;
}
AnnotationMirror lengthAnno = getValueAnnotation(value);
if (lengthAnno == null) {
return;
}
if (AnnotationUtils.areSameByClass(lengthAnno, BottomVal.class)) {
// If the length is bottom, then this is dead code, so the receiver type
// should also be bottom.
Receiver receiver = FlowExpressions.internalReprOf(atypefactory, receiverNode);
store.insertValue(receiver, lengthAnno);
return;
}
RangeOrListOfValues rolv;
if (atypefactory.isIntRange(lengthAnno)) {
rolv = new RangeOrListOfValues(ValueAnnotatedTypeFactory.getRange(lengthAnno));
} else if (AnnotationUtils.areSameByClass(lengthAnno, IntVal.class)) {
List lengthValues = ValueAnnotatedTypeFactory.getIntValues(lengthAnno);
rolv = new RangeOrListOfValues(RangeOrListOfValues.convertLongsToInts(lengthValues));
} else {
return;
}
AnnotationMirror newRecAnno = rolv.createAnnotation(atypefactory);
AnnotationMirror oldRecAnno = getArrayOrStringAnnotation(receiverNode);
AnnotationMirror combinedRecAnno;
// If the receiver doesn't have an @ArrayLen annotation, use the new annotation.
// If it does have an annotation, combine the facts known about the receiver
// with the facts known about its length using GLB.
if (oldRecAnno == null) {
combinedRecAnno = newRecAnno;
} else {
combinedRecAnno =
atypefactory.getQualifierHierarchy().greatestLowerBound(oldRecAnno, newRecAnno);
}
Receiver receiver = FlowExpressions.internalReprOf(analysis.getTypeFactory(), receiverNode);
store.insertValue(receiver, combinedRecAnno);
}
@Override
public TransferResult visitStringConcatenateAssignment(
StringConcatenateAssignmentNode n, TransferInput p) {
TransferResult result = super.visitStringConcatenateAssignment(n, p);
return stringConcatenation(n.getLeftOperand(), n.getRightOperand(), p, result);
}
@Override
public TransferResult visitStringConcatenate(
StringConcatenateNode n, TransferInput p) {
TransferResult result = super.visitStringConcatenate(n, p);
return stringConcatenation(n.getLeftOperand(), n.getRightOperand(), p, result);
}
/**
* Calculates possible lengths of a result of string concatenation of strings with known
* lengths.
*/
private List calculateLengthAddition(
List leftLengths, List rightLengths) {
ArrayList result = new ArrayList();
for (int left : leftLengths) {
for (int right : rightLengths) {
long resultLength = (long) left + right;
// Lengths not fitting into int are not allowed
if (resultLength <= Integer.MAX_VALUE) {
result.add((int) resultLength);
}
}
}
return result;
}
/**
* Calculates a range of possible lengths of a result of string concatenation of strings with
* known ranges of lengths.
*/
private Range calculateLengthRangeAddition(Range leftLengths, Range rightLengths) {
return leftLengths.plus(rightLengths).intersect(Range.INT_EVERYTHING);
}
/** Creates an annotation for a result of string concatenation. */
private AnnotationMirror createAnnotationForStringConcatenation(
Node leftOperand, Node rightOperand, TransferInput p) {
// Try using sets of string values
List leftValues = getStringValues(leftOperand, p);
List rightValues = getStringValues(rightOperand, p);
if (leftValues != null && rightValues != null) {
// Both operands have known string values, compute set of results
List concatValues = new ArrayList<>();
if (leftValues.isEmpty()) {
leftValues = Collections.singletonList("null");
}
if (rightValues.isEmpty()) {
rightValues = Collections.singletonList("null");
}
for (String left : leftValues) {
for (String right : rightValues) {
concatValues.add(left + right);
}
}
return atypefactory.createStringAnnotation(concatValues);
}
// Try using sets of lengths
List leftLengths =
leftValues != null
? ValueCheckerUtils.getLengthsForStringValues(leftValues)
: getStringLengths(leftOperand, p);
List rightLengths =
rightValues != null
? ValueCheckerUtils.getLengthsForStringValues(rightValues)
: getStringLengths(rightOperand, p);
if (leftLengths != null && rightLengths != null) {
// Both operands have known lengths, compute set of result lengths
List concatLengths = calculateLengthAddition(leftLengths, rightLengths);
return atypefactory.createArrayLenAnnotation(concatLengths);
}
// Try using ranges of lengths
Range leftLengthRange =
leftLengths != null
? ValueCheckerUtils.getRangeFromValues(leftLengths)
: getStringLengthRange(leftOperand, p);
Range rightLengthRange =
rightLengths != null
? ValueCheckerUtils.getRangeFromValues(rightLengths)
: getStringLengthRange(rightOperand, p);
if (leftLengthRange != null && rightLengthRange != null) {
// Both operands have a length from a known range, compute a range of result lengths
Range concatLengthRange =
calculateLengthRangeAddition(leftLengthRange, rightLengthRange);
return atypefactory.createArrayLenRangeAnnotation(concatLengthRange);
}
return atypefactory.UNKNOWNVAL;
}
public TransferResult stringConcatenation(
Node leftOperand,
Node rightOperand,
TransferInput p,
TransferResult result) {
AnnotationMirror resultAnno =
createAnnotationForStringConcatenation(leftOperand, rightOperand, p);
TypeMirror underlyingType = result.getResultValue().getUnderlyingType();
CFValue newResultValue = analysis.createSingleAnnotationValue(resultAnno, underlyingType);
return new RegularTransferResult<>(newResultValue, result.getRegularStore());
}
/** binary operations that are analyzed by the value checker */
enum NumericalBinaryOps {
ADDITION,
SUBTRACTION,
DIVISION,
REMAINDER,
MULTIPLICATION,
SHIFT_LEFT,
SIGNED_SHIFT_RIGHT,
UNSIGNED_SHIFT_RIGHT,
BITWISE_AND,
BITWISE_OR,
BITWISE_XOR;
}
/**
* Get the refined annotation after a numerical binary operation.
*
* @param leftNode the node that represents the left operand
* @param rightNode the node that represents the right operand
* @param op the operator type
* @param p the transfer input
* @return the result annotation mirror
*/
private AnnotationMirror calculateNumericalBinaryOp(
Node leftNode,
Node rightNode,
NumericalBinaryOps op,
TransferInput p) {
if (!isIntRangeOrIntegralUnknownVal(leftNode, p)
&& !isIntRangeOrIntegralUnknownVal(rightNode, p)) {
List resultValues = calculateValuesBinaryOp(leftNode, rightNode, op, p);
return atypefactory.createNumberAnnotationMirror(resultValues);
} else {
Range resultRange = calculateRangeBinaryOp(leftNode, rightNode, op, p);
return atypefactory.createIntRangeAnnotation(resultRange);
}
}
/** Calculate the result range after a binary operation between two numerical type nodes */
private Range calculateRangeBinaryOp(
Node leftNode,
Node rightNode,
NumericalBinaryOps op,
TransferInput p) {
if (TypesUtils.isIntegral(leftNode.getType())
&& TypesUtils.isIntegral(rightNode.getType())) {
Range leftRange = getIntRange(leftNode, p);
Range rightRange = getIntRange(rightNode, p);
Range resultRange;
switch (op) {
case ADDITION:
resultRange = leftRange.plus(rightRange);
break;
case SUBTRACTION:
resultRange = leftRange.minus(rightRange);
break;
case MULTIPLICATION:
resultRange = leftRange.times(rightRange);
break;
case DIVISION:
resultRange = leftRange.divide(rightRange);
break;
case REMAINDER:
resultRange = leftRange.remainder(rightRange);
break;
case SHIFT_LEFT:
resultRange = leftRange.shiftLeft(rightRange);
break;
case SIGNED_SHIFT_RIGHT:
resultRange = leftRange.signedShiftRight(rightRange);
break;
case UNSIGNED_SHIFT_RIGHT:
resultRange = leftRange.unsignedShiftRight(rightRange);
break;
case BITWISE_AND:
resultRange = leftRange.bitwiseAnd(rightRange);
break;
case BITWISE_OR:
resultRange = leftRange.bitwiseOr(rightRange);
break;
case BITWISE_XOR:
resultRange = leftRange.bitwiseXor(rightRange);
break;
default:
ErrorReporter.errorAbort("ValueTransfer: unsupported operation: " + op);
throw new RuntimeException("this can't happen");
}
// Any integral type with less than 32 bits would be promoted to 32-bit int type during
// operations.
return leftNode.getType().getKind() == TypeKind.LONG
|| rightNode.getType().getKind() == TypeKind.LONG
? resultRange
: resultRange.intRange();
} else {
return Range.EVERYTHING;
}
}
/** Calculate the possible values after a binary operation between two numerical type nodes */
private List calculateValuesBinaryOp(
Node leftNode,
Node rightNode,
NumericalBinaryOps op,
TransferInput p) {
List extends Number> lefts = getNumericalValues(leftNode, p);
List extends Number> rights = getNumericalValues(rightNode, p);
if (lefts == null || rights == null) {
return null;
}
List resultValues = new ArrayList<>();
for (Number left : lefts) {
NumberMath> nmLeft = NumberMath.getNumberMath(left);
for (Number right : rights) {
switch (op) {
case ADDITION:
resultValues.add(nmLeft.plus(right));
break;
case DIVISION:
Number result = nmLeft.divide(right);
if (result != null) {
resultValues.add(result);
}
break;
case MULTIPLICATION:
resultValues.add(nmLeft.times(right));
break;
case REMAINDER:
Number resultR = nmLeft.remainder(right);
if (resultR != null) {
resultValues.add(resultR);
}
break;
case SUBTRACTION:
resultValues.add(nmLeft.minus(right));
break;
case SHIFT_LEFT:
resultValues.add(nmLeft.shiftLeft(right));
break;
case SIGNED_SHIFT_RIGHT:
resultValues.add(nmLeft.signedShiftRight(right));
break;
case UNSIGNED_SHIFT_RIGHT:
resultValues.add(nmLeft.unsignedShiftRight(right));
break;
case BITWISE_AND:
resultValues.add(nmLeft.bitwiseAnd(right));
break;
case BITWISE_OR:
resultValues.add(nmLeft.bitwiseOr(right));
break;
case BITWISE_XOR:
resultValues.add(nmLeft.bitwiseXor(right));
break;
default:
ErrorReporter.errorAbort("ValueTransfer: unsupported operation: " + op);
}
}
}
return resultValues;
}
@Override
public TransferResult visitNumericalAddition(
NumericalAdditionNode n, TransferInput p) {
TransferResult transferResult = super.visitNumericalAddition(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(), NumericalBinaryOps.ADDITION, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitNumericalSubtraction(
NumericalSubtractionNode n, TransferInput p) {
TransferResult transferResult = super.visitNumericalSubtraction(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(), NumericalBinaryOps.SUBTRACTION, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitNumericalMultiplication(
NumericalMultiplicationNode n, TransferInput p) {
TransferResult transferResult = super.visitNumericalMultiplication(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(),
n.getRightOperand(),
NumericalBinaryOps.MULTIPLICATION,
p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitIntegerDivision(
IntegerDivisionNode n, TransferInput p) {
TransferResult transferResult = super.visitIntegerDivision(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(), NumericalBinaryOps.DIVISION, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitFloatingDivision(
FloatingDivisionNode n, TransferInput p) {
TransferResult transferResult = super.visitFloatingDivision(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(), NumericalBinaryOps.DIVISION, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitIntegerRemainder(
IntegerRemainderNode n, TransferInput p) {
TransferResult transferResult = super.visitIntegerRemainder(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(), NumericalBinaryOps.REMAINDER, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitFloatingRemainder(
FloatingRemainderNode n, TransferInput p) {
TransferResult transferResult = super.visitFloatingRemainder(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(), NumericalBinaryOps.REMAINDER, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitLeftShift(
LeftShiftNode n, TransferInput p) {
TransferResult transferResult = super.visitLeftShift(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(), NumericalBinaryOps.SHIFT_LEFT, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitSignedRightShift(
SignedRightShiftNode n, TransferInput p) {
TransferResult transferResult = super.visitSignedRightShift(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(),
n.getRightOperand(),
NumericalBinaryOps.SIGNED_SHIFT_RIGHT,
p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitUnsignedRightShift(
UnsignedRightShiftNode n, TransferInput p) {
TransferResult transferResult = super.visitUnsignedRightShift(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(),
n.getRightOperand(),
NumericalBinaryOps.UNSIGNED_SHIFT_RIGHT,
p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitBitwiseAnd(
BitwiseAndNode n, TransferInput p) {
TransferResult transferResult = super.visitBitwiseAnd(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(), NumericalBinaryOps.BITWISE_AND, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitBitwiseOr(
BitwiseOrNode n, TransferInput p) {
TransferResult transferResult = super.visitBitwiseOr(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(), NumericalBinaryOps.BITWISE_OR, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitBitwiseXor(
BitwiseXorNode n, TransferInput p) {
TransferResult transferResult = super.visitBitwiseXor(n, p);
AnnotationMirror resultAnno =
calculateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(), NumericalBinaryOps.BITWISE_XOR, p);
return createNewResult(transferResult, resultAnno);
}
/** unary operations that are analyzed by the value checker */
enum NumericalUnaryOps {
PLUS,
MINUS,
BITWISE_COMPLEMENT;
}
/**
* Get the refined annotation after a numerical unary operation.
*
* @param operand the node that represents the operand
* @param op the operator type
* @param p the transfer input
* @return the result annotation mirror
*/
private AnnotationMirror calculateNumericalUnaryOp(
Node operand, NumericalUnaryOps op, TransferInput p) {
if (!isIntRange(operand, p)) {
List resultValues = calculateValuesUnaryOp(operand, op, p);
return atypefactory.createNumberAnnotationMirror(resultValues);
} else {
Range resultRange = calculateRangeUnaryOp(operand, op, p);
return atypefactory.createIntRangeAnnotation(resultRange);
}
}
/** Calculate the result range after a unary operation of a numerical type node */
private Range calculateRangeUnaryOp(
Node operand, NumericalUnaryOps op, TransferInput p) {
if (TypesUtils.isIntegral(operand.getType())) {
Range range = getIntRange(operand, p);
Range resultRange;
switch (op) {
case PLUS:
resultRange = range.unaryPlus();
break;
case MINUS:
resultRange = range.unaryMinus();
break;
case BITWISE_COMPLEMENT:
resultRange = range.bitwiseComplement();
break;
default:
ErrorReporter.errorAbort("ValueTransfer: unsupported operation: " + op);
throw new RuntimeException("this can't happen");
}
// Any integral type with less than 32 bits would be promoted to 32-bit int type during
// operations.
return operand.getType().getKind() == TypeKind.LONG
? resultRange
: resultRange.intRange();
} else {
return Range.EVERYTHING;
}
}
/** Calculate the possible values after a unary operation of a numerical type node */
private List calculateValuesUnaryOp(
Node operand, NumericalUnaryOps op, TransferInput p) {
List extends Number> lefts = getNumericalValues(operand, p);
if (lefts == null) {
return null;
}
List resultValues = new ArrayList<>();
for (Number left : lefts) {
NumberMath> nmLeft = NumberMath.getNumberMath(left);
switch (op) {
case PLUS:
resultValues.add(nmLeft.unaryPlus());
break;
case MINUS:
resultValues.add(nmLeft.unaryMinus());
break;
case BITWISE_COMPLEMENT:
resultValues.add(nmLeft.bitwiseComplement());
break;
default:
ErrorReporter.errorAbort("ValueTransfer: unsupported operation: " + op);
}
}
return resultValues;
}
@Override
public TransferResult visitNumericalMinus(
NumericalMinusNode n, TransferInput p) {
TransferResult transferResult = super.visitNumericalMinus(n, p);
AnnotationMirror resultAnno =
calculateNumericalUnaryOp(n.getOperand(), NumericalUnaryOps.MINUS, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitNumericalPlus(
NumericalPlusNode n, TransferInput p) {
TransferResult transferResult = super.visitNumericalPlus(n, p);
AnnotationMirror resultAnno =
calculateNumericalUnaryOp(n.getOperand(), NumericalUnaryOps.PLUS, p);
return createNewResult(transferResult, resultAnno);
}
@Override
public TransferResult visitBitwiseComplement(
BitwiseComplementNode n, TransferInput p) {
TransferResult transferResult = super.visitBitwiseComplement(n, p);
AnnotationMirror resultAnno =
calculateNumericalUnaryOp(n.getOperand(), NumericalUnaryOps.BITWISE_COMPLEMENT, p);
return createNewResult(transferResult, resultAnno);
}
enum ComparisonOperators {
EQUAL,
NOT_EQUAL,
GREATER_THAN,
GREATER_THAN_EQ,
LESS_THAN,
LESS_THAN_EQ;
}
private List calculateBinaryComparison(
Node leftNode,
CFValue leftValue,
Node rightNode,
CFValue rightValue,
ComparisonOperators op,
CFStore thenStore,
CFStore elseStore) {
AnnotationMirror leftAnno = getValueAnnotation(leftValue);
AnnotationMirror rightAnno = getValueAnnotation(rightValue);
if (atypefactory.isIntRange(leftAnno)
|| atypefactory.isIntRange(rightAnno)
|| isIntegralUnknownVal(rightNode, rightAnno)
|| isIntegralUnknownVal(leftNode, leftAnno)) {
// If either is @UnknownVal, then refineIntRanges will treat it as the max range and
// thus refine it if possible. Also, if either is an @IntVal, then it will be
// converted to a range. This is less precise in some cases, but avoids the
// complexity of comparing a list of values to a range. (This could be implemented in
// the future.)
return refineIntRanges(
leftNode, leftAnno, rightNode, rightAnno, op, thenStore, elseStore);
}
List resultValues = new ArrayList<>();
List extends Number> lefts = getNumericalValues(leftNode, leftAnno);
List extends Number> rights = getNumericalValues(rightNode, rightAnno);
if (lefts == null || rights == null) {
// Appropriately handle bottom when something is compared to bottom.
if (AnnotationUtils.areSame(leftAnno, atypefactory.BOTTOMVAL)
|| AnnotationUtils.areSame(rightAnno, atypefactory.BOTTOMVAL)) {
return new ArrayList<>();
}
return null;
}
// These lists are used to refine the values in the store based on the results of the
// comparison.
List thenLeftVals = new ArrayList<>();
List elseLeftVals = new ArrayList<>();
List thenRightVals = new ArrayList<>();
List elseRightVals = new ArrayList<>();
for (Number left : lefts) {
NumberMath> nmLeft = NumberMath.getNumberMath(left);
for (Number right : rights) {
Boolean result;
switch (op) {
case EQUAL:
result = nmLeft.equalTo(right);
break;
case GREATER_THAN:
result = nmLeft.greaterThan(right);
break;
case GREATER_THAN_EQ:
result = nmLeft.greaterThanEq(right);
break;
case LESS_THAN:
result = nmLeft.lessThan(right);
break;
case LESS_THAN_EQ:
result = nmLeft.lessThanEq(right);
break;
case NOT_EQUAL:
result = nmLeft.notEqualTo(right);
break;
default:
ErrorReporter.errorAbort("ValueTransfer: unsupported operation: " + op);
throw new RuntimeException("this can't happen");
}
resultValues.add(result);
if (result) {
thenLeftVals.add(left);
thenRightVals.add(right);
} else {
elseLeftVals.add(left);
elseRightVals.add(right);
}
}
}
createAnnotationFromResultsAndAddToStore(thenStore, thenLeftVals, leftNode);
createAnnotationFromResultsAndAddToStore(elseStore, elseLeftVals, leftNode);
createAnnotationFromResultsAndAddToStore(thenStore, thenRightVals, rightNode);
createAnnotationFromResultsAndAddToStore(elseStore, elseRightVals, rightNode);
return resultValues;
}
/**
* Calculates the result of a binary comparison on a pair of intRange annotations, and refines
* annotations appropriately.
*/
private List refineIntRanges(
Node leftNode,
AnnotationMirror leftAnno,
Node rightNode,
AnnotationMirror rightAnno,
ComparisonOperators op,
CFStore thenStore,
CFStore elseStore) {
Range leftRange = getIntRangeFromAnnotation(leftNode, leftAnno);
Range rightRange = getIntRangeFromAnnotation(rightNode, rightAnno);
final Range thenRightRange;
final Range thenLeftRange;
final Range elseRightRange;
final Range elseLeftRange;
switch (op) {
case EQUAL:
thenRightRange = rightRange.refineEqualTo(leftRange);
thenLeftRange = thenRightRange; // Only needs to be computed once.
elseRightRange = rightRange.refineNotEqualTo(leftRange);
elseLeftRange = leftRange.refineNotEqualTo(rightRange);
break;
case GREATER_THAN:
thenLeftRange = leftRange.refineGreaterThan(rightRange);
thenRightRange = rightRange.refineLessThan(leftRange);
elseRightRange = rightRange.refineGreaterThanEq(leftRange);
elseLeftRange = leftRange.refineLessThanEq(rightRange);
break;
case GREATER_THAN_EQ:
thenRightRange = rightRange.refineLessThanEq(leftRange);
thenLeftRange = leftRange.refineGreaterThanEq(rightRange);
elseLeftRange = leftRange.refineLessThan(rightRange);
elseRightRange = rightRange.refineGreaterThan(leftRange);
break;
case LESS_THAN:
thenLeftRange = leftRange.refineLessThan(rightRange);
thenRightRange = rightRange.refineGreaterThan(leftRange);
elseRightRange = rightRange.refineLessThanEq(leftRange);
elseLeftRange = leftRange.refineGreaterThanEq(rightRange);
break;
case LESS_THAN_EQ:
thenRightRange = rightRange.refineGreaterThanEq(leftRange);
thenLeftRange = leftRange.refineLessThanEq(rightRange);
elseLeftRange = leftRange.refineGreaterThan(rightRange);
elseRightRange = rightRange.refineLessThan(leftRange);
break;
case NOT_EQUAL:
thenRightRange = rightRange.refineNotEqualTo(leftRange);
thenLeftRange = leftRange.refineNotEqualTo(rightRange);
elseRightRange = rightRange.refineEqualTo(leftRange);
elseLeftRange = elseRightRange; // Equality only needs to be computed once.
break;
default:
ErrorReporter.errorAbort("ValueTransfer: unsupported operation: " + op);
throw new RuntimeException("this is impossible, but javac issues a warning");
}
createAnnotationFromRangeAndAddToStore(thenStore, thenRightRange, rightNode);
createAnnotationFromRangeAndAddToStore(thenStore, thenLeftRange, leftNode);
createAnnotationFromRangeAndAddToStore(elseStore, elseRightRange, rightNode);
createAnnotationFromRangeAndAddToStore(elseStore, elseLeftRange, leftNode);
// TODO: Refine the type of the comparison.
return null;
}
/**
* Takes a list of result values (i.e. the values possible after the comparison) and creates the
* appropriate annotation from them, then combines that annotation with the existing annotation
* on the node. The resulting annotation is inserted into the store.
*/
private void createAnnotationFromResultsAndAddToStore(
CFStore store, List> results, Node node) {
AnnotationMirror anno = atypefactory.createResultingAnnotation(node.getType(), results);
addAnnotationToStore(store, anno, node);
}
/**
* Takes a range and creates the appropriate annotation from it, then combines that annotation
* with the existing annotation on the node. The resulting annotation is inserted into the
* store.
*/
private void createAnnotationFromRangeAndAddToStore(CFStore store, Range range, Node node) {
AnnotationMirror anno = atypefactory.createIntRangeAnnotation(range);
addAnnotationToStore(store, anno, node);
}
private void addAnnotationToStore(CFStore store, AnnotationMirror anno, Node node) {
for (Node internal : splitAssignments(node)) {
AnnotationMirror currentAnno =
atypefactory
.getAnnotatedType(internal.getTree())
.getAnnotationInHierarchy(atypefactory.BOTTOMVAL);
Receiver rec = FlowExpressions.internalReprOf(analysis.getTypeFactory(), internal);
// Combine the new annotations based on the results of the comparison with the existing
// type.
store.insertValue(
rec,
atypefactory.getQualifierHierarchy().greatestLowerBound(anno, currentAnno));
if (node instanceof FieldAccessNode) {
refineArrayAtLengthAccess((FieldAccessNode) internal, store);
} else if (node instanceof MethodInvocationNode) {
refineStringAtLengthInvocation((MethodInvocationNode) internal, store);
}
}
}
@Override
public TransferResult visitLessThan(
LessThanNode n, TransferInput p) {
TransferResult transferResult = super.visitLessThan(n, p);
CFStore thenStore = transferResult.getThenStore();
CFStore elseStore = transferResult.getElseStore();
List resultValues =
calculateBinaryComparison(
n.getLeftOperand(),
p.getValueOfSubNode(n.getLeftOperand()),
n.getRightOperand(),
p.getValueOfSubNode(n.getRightOperand()),
ComparisonOperators.LESS_THAN,
thenStore,
elseStore);
TypeMirror underlyingType = transferResult.getResultValue().getUnderlyingType();
return createNewResultBoolean(thenStore, elseStore, resultValues, underlyingType);
}
@Override
public TransferResult visitLessThanOrEqual(
LessThanOrEqualNode n, TransferInput p) {
TransferResult transferResult = super.visitLessThanOrEqual(n, p);
CFStore thenStore = transferResult.getThenStore();
CFStore elseStore = transferResult.getElseStore();
List resultValues =
calculateBinaryComparison(
n.getLeftOperand(),
p.getValueOfSubNode(n.getLeftOperand()),
n.getRightOperand(),
p.getValueOfSubNode(n.getRightOperand()),
ComparisonOperators.LESS_THAN_EQ,
thenStore,
elseStore);
TypeMirror underlyingType = transferResult.getResultValue().getUnderlyingType();
return createNewResultBoolean(thenStore, elseStore, resultValues, underlyingType);
}
@Override
public TransferResult visitGreaterThan(
GreaterThanNode n, TransferInput p) {
TransferResult transferResult = super.visitGreaterThan(n, p);
CFStore thenStore = transferResult.getThenStore();
CFStore elseStore = transferResult.getElseStore();
List resultValues =
calculateBinaryComparison(
n.getLeftOperand(),
p.getValueOfSubNode(n.getLeftOperand()),
n.getRightOperand(),
p.getValueOfSubNode(n.getRightOperand()),
ComparisonOperators.GREATER_THAN,
thenStore,
elseStore);
TypeMirror underlyingType = transferResult.getResultValue().getUnderlyingType();
return createNewResultBoolean(thenStore, elseStore, resultValues, underlyingType);
}
@Override
public TransferResult visitGreaterThanOrEqual(
GreaterThanOrEqualNode n, TransferInput p) {
TransferResult transferResult = super.visitGreaterThanOrEqual(n, p);
CFStore thenStore = transferResult.getThenStore();
CFStore elseStore = transferResult.getElseStore();
List resultValues =
calculateBinaryComparison(
n.getLeftOperand(),
p.getValueOfSubNode(n.getLeftOperand()),
n.getRightOperand(),
p.getValueOfSubNode(n.getRightOperand()),
ComparisonOperators.GREATER_THAN_EQ,
thenStore,
elseStore);
TypeMirror underlyingType = transferResult.getResultValue().getUnderlyingType();
return createNewResultBoolean(thenStore, elseStore, resultValues, underlyingType);
}
@Override
protected TransferResult strengthenAnnotationOfEqualTo(
TransferResult transferResult,
Node firstNode,
Node secondNode,
CFValue firstValue,
CFValue secondValue,
boolean notEqualTo) {
if (firstValue == null) {
return transferResult;
}
if (TypesUtils.isNumeric(firstNode.getType())
|| TypesUtils.isNumeric(secondNode.getType())) {
CFStore thenStore = transferResult.getThenStore();
CFStore elseStore = transferResult.getElseStore();
// At least one must be a primitive otherwise reference equality is used.
List resultValues =
calculateBinaryComparison(
firstNode,
firstValue,
secondNode,
secondValue,
notEqualTo ? ComparisonOperators.NOT_EQUAL : ComparisonOperators.EQUAL,
thenStore,
elseStore);
if (transferResult.getResultValue() == null) {
// Happens for case labels
return transferResult;
}
TypeMirror underlyingType = transferResult.getResultValue().getUnderlyingType();
return createNewResultBoolean(thenStore, elseStore, resultValues, underlyingType);
}
return super.strengthenAnnotationOfEqualTo(
transferResult, firstNode, secondNode, firstValue, secondValue, notEqualTo);
}
@Override
protected void processConditionalPostconditions(
MethodInvocationNode n,
ExecutableElement methodElement,
Tree tree,
CFStore thenStore,
CFStore elseStore) {
// For String.startsWith(String) and String.endsWith(String), refine the minimum length
// of the receiver to the minimum length of the argument.
ValueMethodIdentifier methodIdentifier = atypefactory.getMethodIdentifier();
if (methodIdentifier.isStartsWithMethod(methodElement)
|| methodIdentifier.isEndsWithMethod(methodElement)) {
Node argumentNode = n.getArgument(0);
AnnotationMirror argumentAnno = getArrayOrStringAnnotation(argumentNode);
Integer minLength = atypefactory.getMinLenValue(argumentAnno);
// Update the annotation of the receiver
if (minLength != null && minLength != 0) {
Receiver receiver =
FlowExpressions.internalReprOf(atypefactory, n.getTarget().getReceiver());
AnnotationMirror minLenAnno =
atypefactory.createArrayLenRangeAnnotation(minLength, Integer.MAX_VALUE);
thenStore.insertValue(receiver, minLenAnno);
}
}
super.processConditionalPostconditions(n, methodElement, tree, thenStore, elseStore);
}
enum ConditionalOperators {
NOT,
OR,
AND;
}
private static final List ALL_BOOLEANS =
Arrays.asList(new Boolean[] {Boolean.TRUE, Boolean.FALSE});
private List calculateConditionalOperator(
Node leftNode,
Node rightNode,
ConditionalOperators op,
TransferInput p) {
List lefts = getBooleanValues(leftNode, p);
if (lefts == null) {
lefts = ALL_BOOLEANS;
}
List resultValues = new ArrayList<>();
List rights = null;
if (rightNode != null) {
rights = getBooleanValues(rightNode, p);
if (rights == null) {
rights = ALL_BOOLEANS;
}
}
switch (op) {
case NOT:
for (Boolean left : lefts) {
resultValues.add(!left);
}
return resultValues;
case OR:
for (Boolean left : lefts) {
for (Boolean right : rights) {
resultValues.add(left || right);
}
}
return resultValues;
case AND:
for (Boolean left : lefts) {
for (Boolean right : rights) {
resultValues.add(left && right);
}
}
return resultValues;
}
ErrorReporter.errorAbort("ValueTransfer: unsupported operation: " + op);
throw new RuntimeException("this can't happen");
}
@Override
public TransferResult visitConditionalNot(
ConditionalNotNode n, TransferInput p) {
TransferResult transferResult = super.visitConditionalNot(n, p);
List resultValues =
calculateConditionalOperator(n.getOperand(), null, ConditionalOperators.NOT, p);
return createNewResultBoolean(
transferResult.getThenStore(),
transferResult.getElseStore(),
resultValues,
transferResult.getResultValue().getUnderlyingType());
}
@Override
public TransferResult visitConditionalAnd(
ConditionalAndNode n, TransferInput p) {
TransferResult transferResult = super.visitConditionalAnd(n, p);
List resultValues =
calculateConditionalOperator(
n.getLeftOperand(), n.getRightOperand(), ConditionalOperators.AND, p);
return createNewResultBoolean(
transferResult.getThenStore(),
transferResult.getElseStore(),
resultValues,
transferResult.getResultValue().getUnderlyingType());
}
@Override
public TransferResult visitConditionalOr(
ConditionalOrNode n, TransferInput p) {
TransferResult transferResult = super.visitConditionalOr(n, p);
List resultValues =
calculateConditionalOperator(
n.getLeftOperand(), n.getRightOperand(), ConditionalOperators.OR, p);
return createNewResultBoolean(
transferResult.getThenStore(),
transferResult.getElseStore(),
resultValues,
transferResult.getResultValue().getUnderlyingType());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy