| 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); |
| } |
| } |