use of org.checkerframework.dataflow.cfg.node.StringConversionNode in project bazel by bazelbuild.
the class FlowExpressions method internalReprOf.
/**
* We ignore operations such as widening and
* narrowing when computing the internal representation.
*
* @return The internal representation (as {@link Receiver}) of any
* {@link Node}. Might contain {@link Unknown}.
*/
public static Receiver internalReprOf(AnnotationProvider provider, Node receiverNode, boolean allowNonDeterminitic) {
Receiver receiver = null;
if (receiverNode instanceof FieldAccessNode) {
FieldAccessNode fan = (FieldAccessNode) receiverNode;
if (fan.getFieldName().equals("this")) {
// For some reason, "className.this" is considered a field access.
// We right this wrong here.
receiver = new ThisReference(fan.getReceiver().getType());
} else {
receiver = internalReprOfFieldAccess(provider, fan);
}
} else if (receiverNode instanceof ExplicitThisLiteralNode) {
receiver = new ThisReference(receiverNode.getType());
} else if (receiverNode instanceof ThisLiteralNode) {
receiver = new ThisReference(receiverNode.getType());
} else if (receiverNode instanceof SuperNode) {
receiver = new ThisReference(receiverNode.getType());
} else if (receiverNode instanceof LocalVariableNode) {
LocalVariableNode lv = (LocalVariableNode) receiverNode;
receiver = new LocalVariable(lv);
} else if (receiverNode instanceof ArrayAccessNode) {
ArrayAccessNode a = (ArrayAccessNode) receiverNode;
receiver = internalReprOfArrayAccess(provider, a);
} else if (receiverNode instanceof StringConversionNode) {
// ignore string conversion
return internalReprOf(provider, ((StringConversionNode) receiverNode).getOperand());
} else if (receiverNode instanceof WideningConversionNode) {
// ignore widening
return internalReprOf(provider, ((WideningConversionNode) receiverNode).getOperand());
} else if (receiverNode instanceof NarrowingConversionNode) {
// ignore narrowing
return internalReprOf(provider, ((NarrowingConversionNode) receiverNode).getOperand());
} else if (receiverNode instanceof ClassNameNode) {
ClassNameNode cn = (ClassNameNode) receiverNode;
receiver = new ClassName(cn.getType());
} else if (receiverNode instanceof ValueLiteralNode) {
ValueLiteralNode vn = (ValueLiteralNode) receiverNode;
receiver = new ValueLiteral(vn.getType(), vn);
} else if (receiverNode instanceof MethodInvocationNode) {
MethodInvocationNode mn = (MethodInvocationNode) receiverNode;
ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn.getTree());
// check if this represents a boxing operation of a constant, in which
// case we treat the method call as deterministic, because there is no way
// to behave differently in two executions where two constants are being used.
boolean considerDeterministic = false;
if (invokedMethod.toString().equals("valueOf(long)") && mn.getTarget().getReceiver().toString().equals("Long")) {
Node arg = mn.getArgument(0);
if (arg instanceof ValueLiteralNode) {
considerDeterministic = true;
}
}
if (PurityUtils.isDeterministic(provider, invokedMethod) || allowNonDeterminitic || considerDeterministic) {
List<Receiver> parameters = new ArrayList<>();
for (Node p : mn.getArguments()) {
parameters.add(internalReprOf(provider, p));
}
Receiver methodReceiver;
if (ElementUtils.isStatic(invokedMethod)) {
methodReceiver = new ClassName(mn.getTarget().getReceiver().getType());
} else {
methodReceiver = internalReprOf(provider, mn.getTarget().getReceiver());
}
receiver = new PureMethodCall(mn.getType(), invokedMethod, methodReceiver, parameters);
}
}
if (receiver == null) {
receiver = new Unknown(receiverNode.getType());
}
return receiver;
}
use of org.checkerframework.dataflow.cfg.node.StringConversionNode in project checker-framework by typetools.
the class ValueTransfer method createAnnotationForStringConcatenation.
/**
* Creates an annotation for a result of string concatenation.
*/
private AnnotationMirror createAnnotationForStringConcatenation(Node leftOperand, Node rightOperand, TransferInput<CFValue, CFStore> p) {
// Try using sets of string values
List<String> leftValues = getStringValues(leftOperand, p);
List<String> rightValues = getStringValues(rightOperand, p);
boolean nonNullStringConcat = atypeFactory.getChecker().hasOption("nonNullStringsConcatenation");
if (leftValues != null && rightValues != null) {
// Both operands have known string values, compute set of results
if (!nonNullStringConcat) {
if (isNullable(leftOperand)) {
leftValues = CollectionsPlume.append(leftValues, "null");
}
if (isNullable(rightOperand)) {
rightValues = CollectionsPlume.append(rightValues, "null");
}
} else {
if (leftOperand instanceof StringConversionNode) {
if (((StringConversionNode) leftOperand).getOperand().getType().getKind() == TypeKind.NULL) {
leftValues = CollectionsPlume.append(leftValues, "null");
}
}
if (rightOperand instanceof StringConversionNode) {
if (((StringConversionNode) rightOperand).getOperand().getType().getKind() == TypeKind.NULL) {
rightValues = CollectionsPlume.append(rightValues, "null");
}
}
}
List<String> concatValues = new ArrayList<>(leftValues.size() * rightValues.size());
for (String left : leftValues) {
for (String right : rightValues) {
concatValues.add(left + right);
}
}
return atypeFactory.createStringAnnotation(concatValues);
}
// Try using sets of lengths
List<Integer> leftLengths = leftValues != null ? ValueCheckerUtils.getLengthsForStringValues(leftValues) : getStringLengths(leftOperand, p);
List<Integer> rightLengths = rightValues != null ? ValueCheckerUtils.getLengthsForStringValues(rightValues) : getStringLengths(rightOperand, p);
if (leftLengths != null && rightLengths != null) {
// Both operands have known lengths, compute set of result lengths
if (!nonNullStringConcat) {
if (isNullable(leftOperand)) {
leftLengths = new ArrayList<>(leftLengths);
// "null"
leftLengths.add(4);
}
if (isNullable(rightOperand)) {
rightLengths = new ArrayList<>(rightLengths);
// "null"
rightLengths.add(4);
}
}
List<Integer> 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
if (!nonNullStringConcat) {
if (isNullable(leftOperand)) {
// "null"
leftLengthRange = leftLengthRange.union(Range.create(4, 4));
}
if (isNullable(rightOperand)) {
// "null"
rightLengthRange = rightLengthRange.union(Range.create(4, 4));
}
}
Range concatLengthRange = calculateLengthRangeAddition(leftLengthRange, rightLengthRange);
return atypeFactory.createArrayLenRangeAnnotation(concatLengthRange);
}
return atypeFactory.UNKNOWNVAL;
}
use of org.checkerframework.dataflow.cfg.node.StringConversionNode in project checker-framework by typetools.
the class ValueTransfer method getStringLengths.
/**
* 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<Integer> getStringLengths(Node subNode, TransferInput<CFValue, CFStore> p) {
CFValue value = p.getValueOfSubNode(subNode);
AnnotationMirror anno = getValueAnnotation(value);
if (anno == null) {
return null;
}
String annoName = AnnotationUtils.annotationName(anno);
if (annoName.equals(ValueAnnotatedTypeFactory.ARRAYLEN_NAME)) {
return atypeFactory.getArrayLength(anno);
} else if (annoName.equals(ValueAnnotatedTypeFactory.BOTTOMVAL_NAME)) {
return Collections.emptyList();
}
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(Range.create(1, 4), Integer.class);
} else if (subNodeTypeKind == TypeKind.SHORT) {
// shorts are between 1 and 6 characters long
return ValueCheckerUtils.getValuesFromRange(Range.create(1, 6), Integer.class);
} else {
return null;
}
}
use of org.checkerframework.dataflow.cfg.node.StringConversionNode in project checker-framework by typetools.
the class ValueTransfer method getStringValues.
/**
* 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.
*
* @param subNode a subNode of p
* @param p TransferInput
* @return a list of possible values for {@code subNode} or null
*/
private List<String> getStringValues(Node subNode, TransferInput<CFValue, CFStore> p) {
CFValue value = p.getValueOfSubNode(subNode);
AnnotationMirror anno = getValueAnnotation(value);
if (anno == null) {
return null;
}
String annoName = AnnotationUtils.annotationName(anno);
switch(annoName) {
case ValueAnnotatedTypeFactory.UNKNOWN_NAME:
return null;
case ValueAnnotatedTypeFactory.BOTTOMVAL_NAME:
return Collections.emptyList();
case ValueAnnotatedTypeFactory.STRINGVAL_NAME:
return atypeFactory.getStringValues(anno);
default:
}
// @IntVal, @IntRange, @DoubleVal, @BoolVal (have to be converted to string)
List<? extends Object> values;
if (annoName.equals(ValueAnnotatedTypeFactory.BOOLVAL_NAME)) {
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<Long> longValues = ValueCheckerUtils.getValuesFromRange(range, Long.class);
values = NumberUtils.castNumbers(subNode.getType(), longValues);
} else {
values = getNumericalValues(subNode, p);
}
if (values == null) {
return null;
}
List<String> stringValues = CollectionsPlume.mapList(Object::toString, values);
// Empty list means bottom value
return stringValues.isEmpty() ? Collections.singletonList("null") : stringValues;
}
use of org.checkerframework.dataflow.cfg.node.StringConversionNode in project checker-framework by typetools.
the class FlowExpressions method internalReprOf.
/**
* We ignore operations such as widening and narrowing when computing the internal
* representation.
*
* @return the internal representation (as {@link Receiver}) of any {@link Node}. Might contain
* {@link Unknown}.
*/
public static Receiver internalReprOf(AnnotationProvider provider, Node receiverNode, boolean allowNonDeterministic) {
Receiver receiver = null;
if (receiverNode instanceof FieldAccessNode) {
FieldAccessNode fan = (FieldAccessNode) receiverNode;
if (fan.getFieldName().equals("this")) {
// For some reason, "className.this" is considered a field access.
// We right this wrong here.
receiver = new ThisReference(fan.getReceiver().getType());
} else if (fan.getFieldName().equals("class")) {
// "className.class" is considered a field access. This makes sense,
// since .class is similar to a field access which is the equivalent
// of a call to getClass(). However for the purposes of dataflow
// analysis, and value stores, this is the equivalent of a ClassNameNode.
receiver = new ClassName(fan.getReceiver().getType());
} else {
receiver = internalReprOfFieldAccess(provider, fan);
}
} else if (receiverNode instanceof ExplicitThisLiteralNode) {
receiver = new ThisReference(receiverNode.getType());
} else if (receiverNode instanceof ThisLiteralNode) {
receiver = new ThisReference(receiverNode.getType());
} else if (receiverNode instanceof SuperNode) {
receiver = new ThisReference(receiverNode.getType());
} else if (receiverNode instanceof LocalVariableNode) {
LocalVariableNode lv = (LocalVariableNode) receiverNode;
receiver = new LocalVariable(lv);
} else if (receiverNode instanceof ArrayAccessNode) {
ArrayAccessNode a = (ArrayAccessNode) receiverNode;
receiver = internalReprOfArrayAccess(provider, a);
} else if (receiverNode instanceof StringConversionNode) {
// ignore string conversion
return internalReprOf(provider, ((StringConversionNode) receiverNode).getOperand());
} else if (receiverNode instanceof WideningConversionNode) {
// ignore widening
return internalReprOf(provider, ((WideningConversionNode) receiverNode).getOperand());
} else if (receiverNode instanceof NarrowingConversionNode) {
// ignore narrowing
return internalReprOf(provider, ((NarrowingConversionNode) receiverNode).getOperand());
} else if (receiverNode instanceof ClassNameNode) {
ClassNameNode cn = (ClassNameNode) receiverNode;
receiver = new ClassName(cn.getType());
} else if (receiverNode instanceof ValueLiteralNode) {
ValueLiteralNode vn = (ValueLiteralNode) receiverNode;
receiver = new ValueLiteral(vn.getType(), vn);
} else if (receiverNode instanceof ArrayCreationNode) {
ArrayCreationNode an = (ArrayCreationNode) receiverNode;
List<Receiver> dimensions = new ArrayList<>();
for (Node dimension : an.getDimensions()) {
dimensions.add(internalReprOf(provider, dimension, allowNonDeterministic));
}
List<Receiver> initializers = new ArrayList<>();
for (Node initializer : an.getInitializers()) {
initializers.add(internalReprOf(provider, initializer, allowNonDeterministic));
}
receiver = new ArrayCreation(an.getType(), dimensions, initializers);
} else if (receiverNode instanceof MethodInvocationNode) {
MethodInvocationNode mn = (MethodInvocationNode) receiverNode;
ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn.getTree());
// check if this represents a boxing operation of a constant, in which
// case we treat the method call as deterministic, because there is no way
// to behave differently in two executions where two constants are being used.
boolean considerDeterministic = false;
if (isLongValueOf(mn, invokedMethod)) {
Node arg = mn.getArgument(0);
if (arg instanceof ValueLiteralNode) {
considerDeterministic = true;
}
}
if (PurityUtils.isDeterministic(provider, invokedMethod) || allowNonDeterministic || considerDeterministic) {
List<Receiver> parameters = new ArrayList<>();
for (Node p : mn.getArguments()) {
parameters.add(internalReprOf(provider, p));
}
Receiver methodReceiver;
if (ElementUtils.isStatic(invokedMethod)) {
methodReceiver = new ClassName(mn.getTarget().getReceiver().getType());
} else {
methodReceiver = internalReprOf(provider, mn.getTarget().getReceiver());
}
receiver = new MethodCall(mn.getType(), invokedMethod, methodReceiver, parameters);
}
}
if (receiver == null) {
receiver = new Unknown(receiverNode.getType());
}
return receiver;
}
Aggregations