blob: f4607dad53633522948c6f4f032356662e00dc3b [file] [log] [blame]
package org.checkerframework.dataflow.expression;
import com.sun.source.tree.Tree;
import java.util.Objects;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.analysis.Store;
import org.checkerframework.dataflow.cfg.node.BinaryOperationNode;
import org.checkerframework.javacutil.AnnotationProvider;
/** JavaExpression for binary operations. */
public class BinaryOperation extends JavaExpression {
/** The binary operation kind. */
protected final Tree.Kind operationKind;
/** The left operand. */
protected final JavaExpression left;
/** The right operand. */
protected final JavaExpression right;
/**
* Create a binary operation.
*
* @param type the result type
* @param operationKind the operator
* @param left the left operand
* @param right the right operand
*/
public BinaryOperation(
TypeMirror type, Tree.Kind operationKind, JavaExpression left, JavaExpression right) {
super(type);
this.operationKind = operationKind;
this.left = left;
this.right = right;
}
/**
* Create a binary operation.
*
* @param node the binary operation node
* @param left the left operand
* @param right the right operand
*/
public BinaryOperation(BinaryOperationNode node, JavaExpression left, JavaExpression right) {
this(node.getType(), node.getTree().getKind(), left, right);
}
/**
* Returns the operator of this binary operation.
*
* @return the binary operation kind
*/
public Tree.Kind getOperationKind() {
return operationKind;
}
/**
* Returns the left operand of this binary operation.
*
* @return the left operand
*/
public JavaExpression getLeft() {
return left;
}
/**
* Returns the right operand of this binary operation.
*
* @return the right operand
*/
public JavaExpression getRight() {
return right;
}
@Override
public boolean containsOfClass(Class<? extends JavaExpression> clazz) {
if (getClass() == clazz) {
return true;
}
return left.containsOfClass(clazz) || right.containsOfClass(clazz);
}
@Override
public boolean isDeterministic(AnnotationProvider provider) {
return left.isDeterministic(provider) && right.isDeterministic(provider);
}
@Override
public boolean isUnassignableByOtherCode() {
return left.isUnassignableByOtherCode() && right.isUnassignableByOtherCode();
}
@Override
public boolean isUnmodifiableByOtherCode() {
return left.isUnmodifiableByOtherCode() && right.isUnmodifiableByOtherCode();
}
@Override
public boolean syntacticEquals(JavaExpression je) {
if (!(je instanceof BinaryOperation)) {
return false;
}
BinaryOperation other = (BinaryOperation) je;
return operationKind == other.getOperationKind()
&& left.syntacticEquals(other.left)
&& right.syntacticEquals(other.right);
}
@Override
public boolean containsSyntacticEqualJavaExpression(JavaExpression other) {
return this.syntacticEquals(other)
|| left.containsSyntacticEqualJavaExpression(other)
|| right.containsSyntacticEqualJavaExpression(other);
}
@Override
public boolean containsModifiableAliasOf(Store<?> store, JavaExpression other) {
return left.containsModifiableAliasOf(store, other)
|| right.containsModifiableAliasOf(store, other);
}
@Override
public int hashCode() {
return Objects.hash(operationKind, left, right);
}
@Override
public boolean equals(@Nullable Object other) {
if (!(other instanceof BinaryOperation)) {
return false;
}
BinaryOperation biOp = (BinaryOperation) other;
if (!(operationKind == biOp.getOperationKind())) {
return false;
}
if (isCommutative()) {
return (left.equals(biOp.left) && right.equals(biOp.right))
|| (left.equals(biOp.right) && right.equals(biOp.left));
}
return left.equals(biOp.left) && right.equals(biOp.right);
}
/**
* Returns true if the binary operation is commutative, e.g., x + y == y + x.
*
* @return true if the binary operation is commutative
*/
private boolean isCommutative() {
switch (operationKind) {
case PLUS:
case MULTIPLY:
case AND:
case OR:
case XOR:
case EQUAL_TO:
case NOT_EQUAL_TO:
case CONDITIONAL_AND:
case CONDITIONAL_OR:
return true;
default:
return false;
}
}
@Override
public String toString() {
return left.toString() + " " + operationKindToString(operationKind) + " " + right.toString();
}
/**
* Return the Java source code representation of the given operation.
*
* @param operationKind an unary operation kind
* @return the Java source code representation of the given operation
*/
private String operationKindToString(Tree.Kind operationKind) {
switch (operationKind) {
case CONDITIONAL_AND:
return "&&";
case AND:
return "&";
case OR:
return "|";
case DIVIDE:
return "/";
case EQUAL_TO:
return "==";
case GREATER_THAN:
return ">";
case GREATER_THAN_EQUAL:
return ">=";
case LEFT_SHIFT:
return "<<";
case LESS_THAN:
return "<";
case LESS_THAN_EQUAL:
return "<=";
case MINUS:
return "-";
case MULTIPLY:
return "*";
case NOT_EQUAL_TO:
return "!=";
case CONDITIONAL_OR:
return "||";
case PLUS:
return "+";
case REMAINDER:
return "%";
case RIGHT_SHIFT:
return ">>";
case UNSIGNED_RIGHT_SHIFT:
return ">>>";
case XOR:
return "^";
default:
throw new Error("unhandled " + operationKind);
}
}
@Override
public <R, P> R accept(JavaExpressionVisitor<R, P> visitor, P p) {
return visitor.visitBinaryOperation(this, p);
}
}