package ai.domain.interval;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;
import org.apache.log4j.Logger;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CharacterLiteral;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InfixExpression.Operator;
import org.eclipse.jdt.core.dom.InstanceofExpression;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import ai.common.Pair;
import ai.domain.DomainIntf;
import ai.domain.expressions.AbstractExpressionVisitor;
import ai.domain.expressions.bool.BooleanEvaluationState;
import ai.domain.expressions.bool.ComparisonEvaluatorIntf;
import ai.domain.expressions.integer.BackwardExpressionSemantics;
import ai.domain.expressions.integer.BackwardTestSemantics;
import ai.domain.expressions.integer.ExpressionEvaluationMap;
import ai.domain.generic.GenericReverseExpressionSemantics;
import ai.domain.intervals.eval.EvaluationUtils;
public class IntervalComparisonEvaluator<DI extends DomainIntf<DI>> implements
ComparisonEvaluatorIntf<DI> {
private final static Logger log = Logger.getLogger(IntervalComparisonEvaluator.class.getName());
private static final int ITERATION_LIMIT = 10;
public static class IntegerComparisonEvaluatorException extends RuntimeException {
private static final long serialVersionUID = 4875197670183556218L;
public IntegerComparisonEvaluatorException(String format, Object... args) {
super(String.format(format, args));
}
}
private static class WeWontHandleThis extends RuntimeException {
private final Expression node;
private static final long serialVersionUID = -8956297034163496655L;
public WeWontHandleThis(Expression node) {
super(String.format("Unhandled node '%s'", node));
this.node = node;
}
}
protected void ensureCorrectType(Expression expr) {
if (!EvaluationUtils.isIntegerType(expr))
throw new IntegerComparisonEvaluatorException("I shouldn't be here? '%s'", expr);
}
private class ReverseExpressionWalker extends AbstractExpressionVisitor {
private DI state; // IMPORTANT: we do not change state!!
private ExpressionEvaluationMap<Interval> nodeValuation;
private Interval newValue;
private RuntimeException unhandledNodeException(Expression node) {
return new IntegerComparisonEvaluatorException("Unhandled node '%s'", node);
}
public DI process(Interval newValue, DI input, Expression node, ExpressionEvaluationMap<Interval> valuation) {
this.state = input;
this.nodeValuation = valuation;
this.newValue = newValue;
node.accept(this);
return this.state;
}
@Override
public boolean preVisit2(ASTNode node) {
if (!(node instanceof Expression))
throw new IntegerComparisonEvaluatorException("SHouldn't be here '%s'", node);
Expression expr = (Expression) node;
Interval existingValue = nodeValuation.get(expr);
// we do not go inside if it is not required (note we do not change
// state)
return !newValue.equals(existingValue);
}
@Override
public boolean visit(ArrayAccess node) {
// TODO Auto-generated method stub
throw new WeWontHandleThis(node);
//
// throw unhandledNodeException(node);
}
@Override
public boolean visit(ArrayCreation node) {
// TODO Auto-generated method stub
throw unhandledNodeException(node);
}
@Override
public boolean visit(ArrayInitializer node) {
// TODO Auto-generated method stub
throw unhandledNodeException(node);
}
@Override
public boolean visit(Assignment node) {
// TODO Auto-generated method stub
throw new WeWontHandleThis(node);
//
// throw unhandledNodeException(node);
}
@Override
public boolean visit(BooleanLiteral node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(CastExpression node) {
// TODO Auto-generated method stub
throw new WeWontHandleThis(node);
// throw unhandledNodeException(node);
}
@Override
public boolean visit(CharacterLiteral node) {
long value = EvaluationUtils.getIntegerValue(node);
Interval v = backwardExprSemantics.getConstantValue(value);
if (!v.equals(newValue))
throw new IntegerComparisonEvaluatorException("Hmmm, something strange??");
return false;
//
// // TODO Auto-generated method stub
// throw unhandledNodeException(node);
}
@Override
public boolean visit(ClassInstanceCreation node) {
// TODO Auto-generated method stub
throw unhandledNodeException(node);
}
@Override
public boolean visit(ConditionalExpression node) {
// TODO Auto-generated method stub
throw new WeWontHandleThis(node);
// throw unhandledNodeException(node);
}
@Override
public boolean visit(FieldAccess node) {
throw new WeWontHandleThis(node);
// // TODO Auto-generated method stub
// throw unhandledNodeException(node);
}
private ArrayList<Expression> getOperands(InfixExpression node) {
ArrayList<Expression> result = new ArrayList<Expression>(2 + (node.hasExtendedOperands() ? node
.extendedOperands().size() : 0));
result.add(node.getLeftOperand());
result.add(node.getRightOperand());
if (node.hasExtendedOperands())
for (Object elem : node.extendedOperands())
result.add((Expression) elem);
return result;
}
private Pair<Interval, Interval> applyReverseOperator(InfixExpression.Operator op, Interval leftArg, Interval rightArg, Interval oldResult,
Interval newResult) {
if (newResult.equals(oldResult))
return Pair.create(leftArg, rightArg);
if (op == InfixExpression.Operator.PLUS)
return res.backwardPlus(leftArg, rightArg, newResult);
if (op == InfixExpression.Operator.MINUS)
return res.backwardMinus(leftArg, rightArg, newResult);
if (op == InfixExpression.Operator.TIMES)
return res.backwardTimes(leftArg, rightArg, newResult);
if (op == InfixExpression.Operator.DIVIDE)
return res.backwardDivide(leftArg, rightArg, newResult);
//we do not change anything since for these operators
if (op == InfixExpression.Operator.AND)
return Pair.create(leftArg, rightArg);
if (op == InfixExpression.Operator.RIGHT_SHIFT_SIGNED)
return Pair.create(leftArg, rightArg);
if (op == InfixExpression.Operator.OR)
return Pair.create(leftArg, rightArg);
if (op == InfixExpression.Operator.REMAINDER)
return Pair.create(leftArg, rightArg);
if (op == InfixExpression.Operator.LEFT_SHIFT)
return Pair.create(leftArg, rightArg);
if (op == InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED)
return Pair.create(leftArg, rightArg);
throw new IntegerComparisonEvaluatorException("Unhandled operator '%s'", op);
}
private void processChild(Expression child, Interval newChildValue) {
this.newValue = newChildValue;
child.accept(this);
}
private void processOperandsHelper(InfixExpression.Operator op, ListIterator<Interval> valueIterator,
ListIterator<Expression> operandsIterator, Interval oldCummulativeValue, Interval newCummulativeValue) {
if (valueIterator.hasPrevious()) {
Interval leftValue = valueIterator.previous();
Expression rightOperand = operandsIterator.previous();
Interval rightValue = nodeValuation.get(rightOperand);
Pair<Interval, Interval> newValues = applyReverseOperator(op, leftValue, rightValue, oldCummulativeValue,
newCummulativeValue);
processOperandsHelper(op, valueIterator, operandsIterator, leftValue, newValues.left);
processChild(rightOperand, newValues.right);
} else {// last one
Expression rightOperand = operandsIterator.previous();
Expression leftOperand = operandsIterator.previous();
if (operandsIterator.hasPrevious())
throw new IntegerComparisonEvaluatorException("Shouldn't happen");
Interval leftValue = nodeValuation.get(leftOperand);
Interval rightValue = nodeValuation.get(rightOperand);
Pair<Interval, Interval> newValues = applyReverseOperator(op, leftValue, rightValue, oldCummulativeValue,
newCummulativeValue);
processChild(leftOperand, newValues.left);
processChild(rightOperand, newValues.right);
}
}
@Override
public boolean visit(InfixExpression node) {
ensureCorrectType(node);
ArrayList<Expression> operands = getOperands(node);
LinkedList<Interval> operationValues = nodeValuation.get(node);
if (operationValues.size() != operands.size() - 1)
throw new IntegerComparisonEvaluatorException("Hmm, invalid node valuation map??");
// prepare arguments
// we iterate backwards
ListIterator<Interval> valueIterator = operationValues.listIterator(operationValues.size());
ListIterator<Expression> operandsIterator = operands.listIterator(operands.size());
// int rightOperandIdx = operands.size()-1;
processOperandsHelper(node.getOperator(), valueIterator, operandsIterator, valueIterator.previous(),
newValue);
return false;
}
@Override
public boolean visit(InstanceofExpression node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(MethodInvocation node) {
//FIXME: we should probably check if arguments are changed??
throw new WeWontHandleThis(node);
// // TODO Auto-generated method stub
// throw unhandledNodeException(node);
}
@Override
public boolean visit(SimpleName node) {
state = backwardExprSemantics.processIntegerNode(state, node, newValue);
return false;
}
@Override
public boolean visit(QualifiedName node) {
//this does not change state so nothing happens
return false;
// // TODO Auto-generated method stub
// throw unhandledNodeException(node);
}
@Override
public boolean visit(NullLiteral node) {
// TODO Auto-generated method stub
throw unhandledNodeException(node);
}
@Override
public boolean visit(NumberLiteral node) {
long value = EvaluationUtils.getIntegerValue(node);
//
// Object constant = node.resolveConstantExpressionValue();
// long value;
// if (constant == null)
// throw new IntegerComparisonEvaluatorException("Hmm");
// if (constant instanceof Integer)
// value = (Integer) constant;
// else if (constant instanceof Long)
// value = (Long) constant;
// else
// throw new IntegerComparisonEvaluatorException("Unhandled integer literal type '%s'(%s)", constant,
// constant.getClass());
Interval v = backwardExprSemantics.getConstantValue(value);
if (!v.equals(newValue))
throw new IntegerComparisonEvaluatorException("Hmmm, something strange??");
return false;
}
@Override
public boolean visit(ParenthesizedExpression node) {
//we visit child with the same value
processChild(node.getExpression(), newValue);
return false;
}
@Override
public boolean visit(PostfixExpression node) {
throw new WeWontHandleThis(node);
}
@Override
public boolean visit(PrefixExpression node) {
PrefixExpression.Operator op = node.getOperator();
if (op == PrefixExpression.Operator.PLUS)
processChild(node.getOperand(), newValue);
else if (op == PrefixExpression.Operator.MINUS) {
Interval operandValue = nodeValuation.get(node.getOperand());
Interval newOperandValue = res.backwardMinus(operandValue, newValue);
processChild(node.getOperand(), newOperandValue);
} else
throw new WeWontHandleThis(node);
return false;
}
@Override
public boolean visit(StringLiteral node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(SuperFieldAccess node) {
// TODO Auto-generated method stub
throw unhandledNodeException(node);
}
@Override
public boolean visit(SuperMethodInvocation node) {
// TODO Auto-generated method stub
throw unhandledNodeException(node);
}
@Override
public boolean visit(ThisExpression node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(TypeLiteral node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(VariableDeclarationExpression node) {
throw unhandledNodeException(node);
}
}
private final IntervalExpressionEvaluator<DI> eval;
private final BackwardTestSemantics<Interval> bts;
private final BackwardExpressionSemantics<DI, Interval> backwardExprSemantics;
private final ReverseExpressionWalker walker = new ReverseExpressionWalker();
private final GenericReverseExpressionSemantics<Interval> res;
private Pair<Interval, Interval> applyBackwardTestSemantics(Operator op, Interval leftValue, Interval rightValue) {
if (op == Operator.LESS)
return bts.less(leftValue, rightValue);
if (op == Operator.LESS_EQUALS)
return bts.leq(leftValue, rightValue);
if (op == Operator.GREATER) {
Pair<Interval, Interval> res = bts.less(rightValue, leftValue);
return res == null ? null : res.swap();
}
if (op == Operator.GREATER_EQUALS) {
Pair<Interval, Interval> res = bts.leq(rightValue, leftValue);
return res == null ? null : res.swap();
}
if (op == Operator.EQUALS)
return bts.equality(leftValue, rightValue);
if (op == Operator.NOT_EQUALS)
return bts.neq(leftValue, rightValue);
throw new IntegerComparisonEvaluatorException("Unhandled operator '%s'", op);
}
private DI processOnce(DI input, Operator op, Pair<ExpressionEvaluationMap<Interval>, DI> evalResult,
Expression left, Expression right) {
ExpressionEvaluationMap<Interval> map = evalResult.left;
Pair<Interval, Interval> before = Pair.create(map.get(left), map.get(right));
Pair<Interval, Interval> after = applyBackwardTestSemantics(op, before.left, before.right);
if (after == null)
return input.getBottom();
if (after.equals(before))
return evalResult.right;
try{
input = walker.process(after.left, input, left, map);
input = walker.process(after.right, input, right, map);
} catch (WeWontHandleThis e) {
log.warn(String.format("Unhandled node '%s' appered in '%s %s %s'", e.node, left, op, right));
return evalResult.right;
}
return input;
}
private DI process(DI input, Operator op, Expression left, Expression right) {
int iteration = 0;
while (true) {
Pair<ExpressionEvaluationMap<Interval>, DI> evalResult = eval.evaluateExpressionsMap(input, left, right);
if (iteration == ITERATION_LIMIT) {
log.warn(String.format("Iteration limit '%s' reached when computing '%s %s %s'", ITERATION_LIMIT, left, op, right));
return evalResult.right;
} else
log.info(String.format("Processing iteration: '%s'", ++iteration));
ExpressionEvaluationMap<Interval> map = evalResult.left;
Pair<Interval, Interval> before = Pair.create(map.get(left), map.get(right));
Pair<Interval, Interval> after = applyBackwardTestSemantics(op, before.left, before.right);
if (after == null)
return input.getBottom();
if (after.equals(before))
return evalResult.right;
DI output = input;
try{
output = walker.process(after.left, input, left, map);
output = walker.process(after.right, output, right, map);
} catch (WeWontHandleThis e) {
log.warn(String.format("Unhandled node '%s' appered in '%s %s %s'", e.node, left, op, right));
return evalResult.right;
}
if (input.equals(output))
return input;
input = output;
}
}
private Operator negate(Operator op) {
if (op == Operator.GREATER)
return Operator.LESS_EQUALS;
if (op == Operator.GREATER_EQUALS)
return Operator.LESS;
if (op == Operator.LESS)
return Operator.GREATER_EQUALS;
if (op == Operator.LESS_EQUALS)
return Operator.GREATER;
if (op == Operator.EQUALS)
return Operator.NOT_EQUALS;
if (op == Operator.NOT_EQUALS)
return Operator.EQUALS;
throw new IntegerComparisonEvaluatorException("Unknown comparision operator '%s'", op);
}
private DI evaluateComparison(DI input, InfixExpression comparison, boolean negated) {
if (input.isBottom())
return input;
Operator op = comparison.getOperator();
if (negated)
op = negate(op);
if (negated)
log.info("Processing negated comparison: " + comparison);
else
log.info("Processing comparison: " + comparison);
DI result = process(input, op, comparison.getLeftOperand(), comparison.getRightOperand());
if (result == null)
throw new IntegerComparisonEvaluatorException("Something is wrong!!");
return result;
}
@Override
public BooleanEvaluationState<DI> evaluateComparison(DI input, InfixExpression comparison) {
if (!EvaluationUtils.isIntegerType(comparison.getLeftOperand())
|| !EvaluationUtils.isIntegerType(comparison.getRightOperand()))
return null;
return BooleanEvaluationState.create(evaluateComparison(input, comparison, false),
evaluateComparison(input, comparison, true));
}
private DI evaluateComparisonOnce(Pair<ExpressionEvaluationMap<Interval>, DI> evalResult,
DI input, InfixExpression.Operator op, Expression left, Expression right, boolean negated) {
if (input.isBottom())
return input;
if (negated)
op = negate(op);
if (negated)
log.info("Processing once negated comparison");
else
log.info("Processing once comparison");
DI result = processOnce(input, op, evalResult, left, right);
if (result == null)
throw new IntegerComparisonEvaluatorException("Something is wrong!!");
return result;
}
public BooleanEvaluationState<DI> evaluateComparisonNoEval(DI input, InfixExpression.Operator op,
Expression left, Expression right, Pair<ExpressionEvaluationMap<Interval>, DI> evalMap) {
return BooleanEvaluationState.create(evaluateComparisonOnce(evalMap, input, op, left, right, false),
evaluateComparisonOnce(evalMap, input, op, left, right, true));
}
public Pair<ExpressionEvaluationMap<Interval>, DI> prepareEvaluationMap(DI input, Expression... expressions){
return eval.evaluateExpressionsMap(input, expressions);
}
private IntervalComparisonEvaluator(IntervalExpressionEvaluator<DI> eval,
BackwardTestSemantics<Interval> backwardTestSemantics, GenericReverseExpressionSemantics<Interval> res,
BackwardExpressionSemantics<DI, Interval> backwardExprSemantics) {
this.eval = eval;
this.bts = backwardTestSemantics;
this.backwardExprSemantics = backwardExprSemantics;
this.res = res;
}
public static <DI extends DomainIntf<DI>> IntervalComparisonEvaluator<DI> create(
IntervalExpressionEvaluator<DI> eval, BackwardTestSemantics<Interval> backwardTestSemantics,
GenericReverseExpressionSemantics<Interval> res, BackwardExpressionSemantics<DI, Interval> backwardExprSemantics) {
return new IntervalComparisonEvaluator<DI>(eval, backwardTestSemantics, res, backwardExprSemantics);
}
}