framework.src.org.checkerframework.common.value.ValueTransfer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checker Show documentation
Show all versions of checker Show documentation
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.
package org.checkerframework.common.value;
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.dataflow.analysis.RegularTransferResult;
import org.checkerframework.dataflow.analysis.TransferInput;
import org.checkerframework.dataflow.analysis.TransferResult;
import org.checkerframework.dataflow.cfg.node.*;
import org.checkerframework.framework.flow.CFAbstractAnalysis;
import org.checkerframework.framework.flow.CFStore;
import org.checkerframework.framework.flow.CFTransfer;
import org.checkerframework.framework.flow.CFValue;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.TypesUtils;
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;
public class ValueTransfer extends CFTransfer {
AnnotatedTypeFactory atypefactory;
public ValueTransfer(
CFAbstractAnalysis analysis) {
super(analysis);
atypefactory = analysis.getTypeFactory();
}
private List getStringValues(Node subNode,
TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
// @StringVal, @BottomVal, @UnknownVal
AnnotationMirror numberAnno = value.getType().getAnnotation(
StringVal.class);
if (numberAnno != null) {
return AnnotationUtils.getElementValueArray(numberAnno, "value",
String.class, true);
}
numberAnno = value.getType().getAnnotation(UnknownVal.class);
if (numberAnno != null) {
return new ArrayList();
}
numberAnno = value.getType().getAnnotation(BottomVal.class);
if (numberAnno != null) {
return Collections.singletonList("null");
}
//@IntVal, @DoubleVal, @BoolVal (have to be converted to string)
List values;
numberAnno = value.getType().getAnnotation(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 {
values = getNumericalValues(subNode, p);
}
List stringValues = new ArrayList();
for (Object o : values) {
stringValues.add(o.toString());
}
return stringValues;
}
private List getBooleanValues(Node subNode,
TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
AnnotationMirror intAnno = value.getType().getAnnotation(BoolVal.class);
return ValueAnnotatedTypeFactory.getBooleanValues(intAnno);
}
private List getCharValues(Node subNode,
TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
AnnotationMirror intAnno = value.getType().getAnnotation(IntVal.class);
return ValueAnnotatedTypeFactory.getCharValues(intAnno);
}
private List getNumericalValues(Node subNode,
TransferInput p) {
CFValue value = p.getValueOfSubNode(subNode);
AnnotationMirror numberAnno = value.getType().getAnnotation(
IntVal.class);
List values;
if (numberAnno == null) {
numberAnno = value.getType().getAnnotation(DoubleVal.class);
if (numberAnno != null) {
values = AnnotationUtils.getElementValueArray(numberAnno,
"value", Double.class, true);
} else {
return new ArrayList();
}
} else {
values = AnnotationUtils.getElementValueArray(numberAnno, "value",
Long.class, true);
}
return NumberUtils.castNumbers(subNode.getType(), values);
}
private AnnotationMirror createStringValAnnotationMirror(List values) {
if (values.isEmpty()) {
return ((ValueAnnotatedTypeFactory) atypefactory).UNKNOWNVAL;
}
return ((ValueAnnotatedTypeFactory) atypefactory)
.createStringAnnotation(values);
}
private AnnotationMirror createNumberAnnotationMirror(List values) {
if (values.isEmpty()) {
return ((ValueAnnotatedTypeFactory) atypefactory).UNKNOWNVAL;
}
Number first = values.get(0);
if (first instanceof Integer || first instanceof Short
|| first instanceof Long) {
List intValues = new ArrayList<>();
for (Number number : values) {
intValues.add(number.longValue());
}
return ((ValueAnnotatedTypeFactory) atypefactory)
.createIntValAnnotation(intValues);
}
if (first instanceof Double || first instanceof Float) {
List intValues = new ArrayList<>();
for (Number number : values) {
intValues.add(number.doubleValue());
}
return ((ValueAnnotatedTypeFactory) atypefactory)
.createDoubleValAnnotation(intValues);
}
throw new UnsupportedOperationException();
}
private AnnotationMirror createBooleanAnnotationMirror(List values) {
if (values.isEmpty()) {
return ((ValueAnnotatedTypeFactory) atypefactory).UNKNOWNVAL;
}
return ((ValueAnnotatedTypeFactory) atypefactory)
.createBooleanAnnotation(values);
}
private TransferResult createNewResult(
TransferResult result, List resultValues) {
AnnotationMirror stringVal = createNumberAnnotationMirror(resultValues);
CFValue newResultValue = analysis.createSingleAnnotationValue(
stringVal, result.getResultValue().getType()
.getUnderlyingType());
return new RegularTransferResult<>(newResultValue,
result.getRegularStore());
}
private TransferResult createNewResultBoolean(
TransferResult result, List resultValues) {
AnnotationMirror stringVal = createBooleanAnnotationMirror(resultValues);
CFValue newResultValue = analysis.createSingleAnnotationValue(
stringVal, result.getResultValue().getType()
.getUnderlyingType());
return new RegularTransferResult<>(newResultValue,
result.getRegularStore());
}
@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);
}
public TransferResult stringConcatenation(Node leftOperand, Node rightOperand,
TransferInput p,
TransferResult result) {
List lefts = getStringValues(leftOperand, p);
List rights = getStringValues(rightOperand, p);
List concat = new ArrayList<>();
for (String left : lefts) {
for (String right : rights) {
concat.add(left + right);
}
}
AnnotationMirror stringVal = createStringValAnnotationMirror(concat);
TypeMirror underlyingType = result.getResultValue().getType().getUnderlyingType();
CFValue newResultValue = analysis.createSingleAnnotationValue(stringVal, underlyingType);
return new RegularTransferResult<>(newResultValue, result.getRegularStore());
}
enum NumbericalBinaryOps {
ADDTION, SUBTRACTION, DIVISION, REMAINDER, MULPLICATION, SHIFT_LEFT, SIGNED_SHIFT_RIGHT, UNSIGNED_SHIFT_RIGHT, BITWISE_AND, BITWISE_OR, BITWISE_XOR;
}
private List calcutateNumericalBinaryOp(Node leftNode,
Node rightNode, NumbericalBinaryOps op,
TransferInput p) {
List lefts = getNumericalValues(leftNode, p);
List rights = getNumericalValues(rightNode, p);
List resultValues = new ArrayList<>();
for (Number left : lefts) {
NumberMath nmLeft = NumberMath.getNumberMath(left);
for (Number right : rights) {
switch (op) {
case ADDTION:
resultValues.add(nmLeft.plus(right));
break;
case DIVISION:
resultValues.add(nmLeft.divide(right));
break;
case MULPLICATION:
resultValues.add(nmLeft.times(right));
break;
case REMAINDER:
resultValues.add(nmLeft.remainder(right));
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.signedSiftRight(right));
break;
case UNSIGNED_SHIFT_RIGHT:
resultValues.add(nmLeft.unsignedSiftRight(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:
throw new UnsupportedOperationException();
}
}
}
return resultValues;
}
@Override
public TransferResult visitNumericalAddition(
NumericalAdditionNode n, TransferInput p) {
TransferResult transferResult = super
.visitNumericalAddition(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.ADDTION, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitNumericalSubtraction(
NumericalSubtractionNode n, TransferInput p) {
TransferResult transferResult = super
.visitNumericalSubtraction(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.SUBTRACTION, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitNumericalMultiplication(
NumericalMultiplicationNode n, TransferInput p) {
TransferResult transferResult = super
.visitNumericalMultiplication(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.MULPLICATION, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitIntegerDivision(
IntegerDivisionNode n, TransferInput p) {
TransferResult transferResult = super
.visitIntegerDivision(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.DIVISION, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitFloatingDivision(
FloatingDivisionNode n, TransferInput p) {
TransferResult transferResult = super
.visitFloatingDivision(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.DIVISION, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitIntegerRemainder(
IntegerRemainderNode n, TransferInput p) {
TransferResult transferResult = super
.visitIntegerRemainder(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.REMAINDER, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitFloatingRemainder(
FloatingRemainderNode n, TransferInput p) {
TransferResult transferResult = super
.visitFloatingRemainder(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.REMAINDER, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitLeftShift(LeftShiftNode n,
TransferInput p) {
TransferResult transferResult = super.visitLeftShift(
n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.SHIFT_LEFT, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitSignedRightShift(
SignedRightShiftNode n, TransferInput p) {
TransferResult transferResult = super
.visitSignedRightShift(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.SIGNED_SHIFT_RIGHT, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitUnsignedRightShift(
UnsignedRightShiftNode n, TransferInput p) {
TransferResult transferResult = super
.visitUnsignedRightShift(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.UNSIGNED_SHIFT_RIGHT, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitBitwiseAnd(BitwiseAndNode n,
TransferInput p) {
TransferResult transferResult = super
.visitBitwiseAnd(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.BITWISE_AND, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitBitwiseOr(BitwiseOrNode n,
TransferInput p) {
TransferResult transferResult = super.visitBitwiseOr(
n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.BITWISE_OR, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitBitwiseXor(BitwiseXorNode n,
TransferInput p) {
TransferResult transferResult = super
.visitBitwiseXor(n, p);
List resultValues = calcutateNumericalBinaryOp(
n.getLeftOperand(), n.getRightOperand(),
NumbericalBinaryOps.BITWISE_XOR, p);
return createNewResult(transferResult, resultValues);
}
enum NumbericalUnaryOps {
PLUS, MINUS, BITWISE_COMPLEMENT;
}
private List calcutateNumericalUnaryOp(Node operand,
NumbericalUnaryOps op, TransferInput p) {
List lefts = getNumericalValues(operand, p);
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:
throw new UnsupportedOperationException();
}
}
return resultValues;
}
@Override
public TransferResult visitNumericalMinus(
NumericalMinusNode n, TransferInput p) {
TransferResult transferResult = super
.visitNumericalMinus(n, p);
List resultValues = calcutateNumericalUnaryOp(n.getOperand(),
NumbericalUnaryOps.MINUS, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitNumericalPlus(
NumericalPlusNode n, TransferInput p) {
TransferResult transferResult = super
.visitNumericalPlus(n, p);
List resultValues = calcutateNumericalUnaryOp(n.getOperand(),
NumbericalUnaryOps.PLUS, p);
return createNewResult(transferResult, resultValues);
}
@Override
public TransferResult visitBitwiseComplement(
BitwiseComplementNode n, TransferInput p) {
TransferResult transferResult = super
.visitBitwiseComplement(n, p);
List resultValues = calcutateNumericalUnaryOp(n.getOperand(),
NumbericalUnaryOps.BITWISE_COMPLEMENT, p);
return createNewResult(transferResult, resultValues);
}
enum ComparisonOperators {
EQUAL, NOT_EQUAL, GREATER_THAN, GREATER_THAN_EQ, LESS_THAN, LESS_THAN_EQ;
}
private List calcutateBinaryComparison(Node leftNode,
Node rightNode, ComparisonOperators op,
TransferInput p) {
List lefts = getNumericalValues(leftNode, p);
List rights = getNumericalValues(rightNode, p);
List resultValues = new ArrayList<>();
for (Number left : lefts) {
NumberMath nmLeft = NumberMath.getNumberMath(left);
for (Number right : rights) {
switch (op) {
case EQUAL:
resultValues.add(nmLeft.equalTo(right));
break;
case GREATER_THAN:
resultValues.add(nmLeft.greaterThan(right));
break;
case GREATER_THAN_EQ:
resultValues.add(nmLeft.greaterThanEq(right));
break;
case LESS_THAN:
resultValues.add(nmLeft.lessThan(right));
break;
case LESS_THAN_EQ:
resultValues.add(nmLeft.lessThanEq(right));
break;
case NOT_EQUAL:
resultValues.add(nmLeft.notEqualTo(right));
break;
default:
throw new UnsupportedOperationException();
}
}
}
return resultValues;
}
@Override
public TransferResult visitLessThan(LessThanNode n,
TransferInput p) {
TransferResult transferResult = super.visitLessThan(
n, p);
List resultValues = calcutateBinaryComparison(
n.getLeftOperand(), n.getRightOperand(),
ComparisonOperators.LESS_THAN, p);
return createNewResultBoolean(transferResult, resultValues);
}
@Override
public TransferResult visitLessThanOrEqual(
LessThanOrEqualNode n, TransferInput p) {
TransferResult transferResult = super
.visitLessThanOrEqual(n, p);
List resultValues = calcutateBinaryComparison(
n.getLeftOperand(), n.getRightOperand(),
ComparisonOperators.LESS_THAN_EQ, p);
return createNewResultBoolean(transferResult, resultValues);
}
@Override
public TransferResult visitGreaterThan(GreaterThanNode n,
TransferInput p) {
TransferResult transferResult = super
.visitGreaterThan(n, p);
List resultValues = calcutateBinaryComparison(
n.getLeftOperand(), n.getRightOperand(),
ComparisonOperators.GREATER_THAN, p);
return createNewResultBoolean(transferResult, resultValues);
}
@Override
public TransferResult visitGreaterThanOrEqual(
GreaterThanOrEqualNode n, TransferInput p) {
TransferResult transferResult = super
.visitGreaterThanOrEqual(n, p);
List resultValues = calcutateBinaryComparison(
n.getLeftOperand(), n.getRightOperand(),
ComparisonOperators.GREATER_THAN_EQ, p);
return createNewResultBoolean(transferResult, resultValues);
}
@Override
public TransferResult visitEqualTo(EqualToNode n,
TransferInput p) {
TransferResult transferResult = super.visitEqualTo(n,
p);
if (TypesUtils.isPrimitive(n.getLeftOperand().getType())
|| TypesUtils.isPrimitive(n.getRightOperand().getType())) {
// At least one must be a primitive otherwise reference equality is used.
List resultValues = calcutateBinaryComparison(
n.getLeftOperand(), n.getRightOperand(),
ComparisonOperators.EQUAL, p);
return createNewResultBoolean(transferResult, resultValues);
}
return super.visitEqualTo(n, p);
}
@Override
public TransferResult visitNotEqual(NotEqualNode n,
TransferInput p) {
TransferResult transferResult = super.visitNotEqual(
n, p);
if (TypesUtils.isPrimitive(n.getLeftOperand().getType())
|| TypesUtils.isPrimitive(n.getRightOperand().getType())) {
// At least one must be a primitive otherwise reference equality is
// used.
List resultValues = calcutateBinaryComparison(
n.getLeftOperand(), n.getRightOperand(),
ComparisonOperators.NOT_EQUAL, p);
return createNewResultBoolean(transferResult, resultValues);
}
return super.visitNotEqual(n, p);
}
enum ConditionalOperators {
NOT, OR, AND;
}
private List calcutateCondtionalOperator(Node leftNode,
Node rightNode, ConditionalOperators op,
TransferInput p) {
List lefts = getBooleanValues(leftNode, p);
List resultValues = new ArrayList<>();
List rights = new ArrayList();
if (rightNode != null) {
rights = getBooleanValues(rightNode, p);
}
switch (op) {
case NOT:
for (Boolean left : lefts) {
resultValues.add(!left);
}
return resultValues;
case OR:
if (lefts.isEmpty() && rights.size() == 1) {
if (rights.get(0)) {
// unknown || true == true
return rights;
}
}
if (rights.isEmpty() && lefts.size() == 1) {
if (lefts.get(0)) {
// true || unknown == true
return lefts;
}
}
for (Boolean left : lefts) {
for (Boolean right : rights) {
resultValues.add(left || right);
}
}
return resultValues;
case AND:
if (lefts.isEmpty() && rights.size() == 1) {
if (!rights.get(0)) {
// unknown && false == false
return rights;
}
}
if (rights.isEmpty() && lefts.size() == 1) {
if (!lefts.get(0)) {
// false && unknown == false
return lefts;
}
}
for (Boolean left : lefts) {
for (Boolean right : rights) {
resultValues.add(left && right);
}
}
return resultValues;
}
return resultValues;
}
@Override
public TransferResult visitConditionalNot(
ConditionalNotNode n, TransferInput p) {
TransferResult transferResult = super
.visitConditionalNot(n, p);
List resultValues = calcutateCondtionalOperator(
n.getOperand(), null, ConditionalOperators.NOT, p);
return createNewResultBoolean(transferResult, resultValues);
}
@Override
public TransferResult visitConditionalAnd(
ConditionalAndNode n, TransferInput p) {
TransferResult transferResult = super
.visitConditionalAnd(n, p);
List resultValues = calcutateCondtionalOperator(
n.getLeftOperand(), n.getRightOperand(),
ConditionalOperators.AND, p);
return createNewResultBoolean(transferResult, resultValues);
}
@Override
public TransferResult visitConditionalOr(
ConditionalOrNode n, TransferInput p) {
TransferResult transferResult = super
.visitConditionalOr(n, p);
List resultValues = calcutateCondtionalOperator(
n.getLeftOperand(), n.getRightOperand(),
ConditionalOperators.OR, p);
return createNewResultBoolean(transferResult, resultValues);
}
}