package ai.domain.intervals.eval;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CharacterLiteral;
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.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import ai.common.ASTUtils;
import ai.common.FatalAIError;
import ai.common.Pair;
import ai.domain.DomainException;
import ai.domain.DomainIntf;
import ai.domain.Variable;
import ai.domain.expressions.ExpressionEvaluatorBase;
import ai.domain.expressions.ExpressionEvaluatorIntf;
import ai.domain.expressions.bool.BooleanConditionEvaluatorIntf;
import ai.domain.expressions.bool.BooleanEvaluationState;
import ai.domain.expressions.integer.ExpressionEvaluationMap;
import ai.domain.generic.NonRelationalDomain;
import ai.domain.interval.Interval;
import ai.domain.interval.IntervalComparisonEvaluator;
import ai.domain.interval.IntervalValue;
public class EvaluationUtils {
private static abstract class ValueGetter<T> {
public abstract T getValue(Object constant);
public T getValue(Expression exprNode) {
T value = getValue(exprNode.resolveConstantExpressionValue());
if (value != null)
return value;
// field access??
if (exprNode.getNodeType() == ASTNode.FIELD_ACCESS) {
IVariableBinding fieldBinding = ((FieldAccess) exprNode).resolveFieldBinding();
return getValue(fieldBinding.getConstantValue());
} else if (exprNode.getNodeType() == ASTNode.SUPER_FIELD_ACCESS) {
IVariableBinding fieldBinding = ((SuperFieldAccess) exprNode).resolveFieldBinding();
return getValue(fieldBinding.getConstantValue());
} else
return null;
}
}
private static final ValueGetter<IntervalValue> INTERVAL_VALUE_GETTER = new ValueGetter<IntervalValue>() {
@Override
public IntervalValue getValue(Object constant) {
if (constant == null)
return null;
if (constant instanceof Integer)
return new IntervalValue((Integer) constant);
if (constant instanceof Long)
return new IntervalValue((Long) constant);
if (constant instanceof Short)
return new IntervalValue((Short) constant);
if (constant instanceof Byte)
return new IntervalValue((Byte) constant);
if (constant instanceof Character)
return new IntervalValue((Character) constant);
throw new EvaluatorException("Unhandled constant type '%s'(%s)", constant, constant.getClass());
}
};
private static final ValueGetter<Boolean> BOOLEAN_VALUE_GETTER = new ValueGetter<Boolean>() {
@Override
public Boolean getValue(Object constant) {
if (constant == null)
return null;
if (constant instanceof Boolean)
return (Boolean) constant;
throw new EvaluatorException("Not a boolean constant '%s'(%s)", constant, constant.getClass());
}
};
private static boolean isBooleanTypeBoxing(ITypeBinding typeBinding) {
return typeBinding.getQualifiedName().equals("java.lang.Boolean");
}
public static boolean isBooleanType(Expression expr) {
ITypeBinding typeBinding = expr.resolveTypeBinding();
if (typeBinding == null)
throw new FatalAIError("Cannot resolve type binding for: '%s'", expr);
return typeBinding.isPrimitive() && typeBinding.getQualifiedName().equals("boolean");
}
public static boolean isBooleanTypeBoxing(Expression expr) {
return isBooleanTypeBoxing(expr.resolveTypeBinding());
}
private static final Set<String> INTEGER_PRIMITIVE_TYPES = new HashSet<String>(Arrays.asList(new String[] { "int",
"byte", "short", "long", "char" }));
private static final Set<String> INTEGER_TYPES = new HashSet<String>(Arrays.asList(new String[] { "java.lang.Integer",
"java.lang.Byte", "java.lang.Short", "java.lang.Long", "java.lang.Character" }));
private static final Set<String> FLOAT_PRIMITIVE_TYPES = new HashSet<String>(Arrays.asList(new String[] { "float", "double" }));
private static boolean isIntegerType(ITypeBinding aType) {
String typeName = aType.getQualifiedName();
return aType.isPrimitive() && INTEGER_PRIMITIVE_TYPES.contains(typeName);
}
private static boolean isFloatType(ITypeBinding aType) {
String typeName = aType.getQualifiedName();
return aType.isPrimitive() && FLOAT_PRIMITIVE_TYPES.contains(typeName);
}
private static boolean isIntegerTypeBoxing(ITypeBinding aType) {
return INTEGER_TYPES.contains(aType.getQualifiedName());
}
public static boolean isIntegerType(Expression expr) {
return isIntegerType(expr.resolveTypeBinding());
}
public static boolean isFloatType(Expression expr) {
return isFloatType(expr.resolveTypeBinding());
}
public static boolean isIntegerType(VariableDeclaration variableDeclaration) {
IVariableBinding variableBinding = variableDeclaration.resolveBinding();
return isIntegerType(variableBinding.getType());
}
public static boolean isIntegerTypeBoxing(Expression expr) {
return isIntegerTypeBoxing(expr.resolveTypeBinding());
}
// public static boolean isInterestingType(Expression expr) {
// return isInterestingType(expr.resolveTypeBinding());
// }
//
// private static boolean isInterestingType(ITypeBinding aType) {
// return aType.isPrimitive() && INTERESTING_TYPES.contains(aType.getQualifiedName());
// }
//
public static long getIntegerValue(NumberLiteral node) {
Object constant = node.resolveConstantExpressionValue();
if (constant == null)
throw new DomainException("Hmm");
if (constant instanceof Integer)
return (Integer) constant;
if (constant instanceof Long)
return (Long) constant;
if (constant instanceof Short)
return (Short) constant;
if (constant instanceof Byte)
return (Byte) constant;
throw new DomainException("Unhandled number literal type '%s'(%s)", constant, constant.getClass());
}
public static long getIntegerValue(CharacterLiteral node) {
Object constant = node.resolveConstantExpressionValue();
if (constant == null)
throw new DomainException("Hmm");
if (constant instanceof Character)
return (Character) constant;
throw new DomainException("Unhandled character literal type '%s'(%s)", constant, constant.getClass());
}
public static boolean getBooleanValue(BooleanLiteral node) {
Object constant = node.resolveConstantExpressionValue();
if (constant == null)
throw new DomainException("Hmm");
if (constant instanceof Boolean)
return (Boolean) constant;
throw new DomainException("Unhandled boolean literal type '%s'(%s)", constant, constant.getClass());
}
/**
* return variable (only local and parameters are interesting)
*
* @param expr
* @return
*/
public static Variable tryGetVariable(Expression expr) {
if (expr.getNodeType() == ASTNode.FIELD_ACCESS)
return null;
if (expr.getNodeType() == ASTNode.ARRAY_ACCESS)
return null;
if (expr.getNodeType() == ASTNode.SUPER_FIELD_ACCESS)
return null;
if (!(expr instanceof Name)) {
throw new EvaluatorException("Not a variable node '%s(%s)'", expr.getClass(), expr);
}
return tryGetVariable((Name) expr);
}
public static Variable tryGetVariable(Name name) {
IBinding binding = name.resolveBinding();
if (binding == null)
throw new FatalAIError("Cannot resolve type binding for: '%s'", name);
if (binding.getKind() != IBinding.VARIABLE)
return null;
IVariableBinding varBinding = (IVariableBinding) binding;
if (varBinding.isField())
return null;
IMethodBinding variableMethod = varBinding.getDeclaringMethod();
//match declaration block
ASTNode enclosingDeclarationBlock = ASTUtils.findAncestorOfType(name, ASTNode.METHOD_DECLARATION, ASTNode.INITIALIZER);
if (variableMethod != null) {
if (enclosingDeclarationBlock.getNodeType() != ASTNode.METHOD_DECLARATION)
return null; // we are in initializer
IMethodBinding enclosingMethod = ((MethodDeclaration)enclosingDeclarationBlock).resolveBinding();
if (varBinding.getDeclaringMethod() != enclosingMethod)
return null;
} else {//variable is defined in initializer
if (enclosingDeclarationBlock.getNodeType() != ASTNode.INITIALIZER)
return null; // we are some method
//hopefully we are ok :/
}
return new Variable(varBinding);
}
public static IntervalValue tryGetIntervalValue(Expression node) {
return INTERVAL_VALUE_GETTER.getValue(node);
}
public static Boolean tryGetBooleanValue(Expression node) {
return BOOLEAN_VALUE_GETTER.getValue(node);
}
public static <DI extends DomainIntf<DI>> Pair<DI, ArrayList<DI>> defaultSwitchProcess(ExpressionEvaluatorIntf<DI> eval,
DI input, Expression expr, Expression[] switchCases){
input = eval.evaluate(expr, input);
ArrayList<DI> result = new ArrayList<DI>(switchCases.length);
for(int i=0; i< switchCases.length; i++)
result.add(input);
return Pair.create(input, result);
}
public static <DI extends DomainIntf<DI>> Pair<DI, ArrayList<DI>> evaluateSwitchInteger(DI input, Expression expr,
Expression[] switchCases, IntervalComparisonEvaluator<DI> eval) {
Expression[] allExpressions = new Expression[switchCases.length + 1];
allExpressions[0] = expr;
for(int i=0; i< switchCases.length; i++)
allExpressions[i+1] = switchCases[i];
Pair<ExpressionEvaluationMap<Interval>, DI> evalMap = eval.prepareEvaluationMap(input, allExpressions);
ArrayList<DI> result = new ArrayList<DI>(switchCases.length);
for(int i=0; i< switchCases.length; i++) {
BooleanEvaluationState<DI> res = eval.evaluateComparisonNoEval(input, InfixExpression.Operator.EQUALS, expr, switchCases[i], evalMap);
input = res.conditionNotMet;
result.add(res.conditionMet);
}
return Pair.create(input, result);
}
public static <DI extends DomainIntf<DI>> Pair<Interval, DI> evaluateConditionalExpression(ConditionalExpression expr, BooleanConditionEvaluatorIntf<DI> condEval,
Pair<Interval, DI> input, ExpressionEvaluatorBase<DI, Pair<Interval, DI>> eval) {
BooleanEvaluationState<DI> condEvalResult = condEval.evaluateCondition(input.right, expr.getExpression());
Pair<Interval, DI> thenResult = null;
if (!condEvalResult.conditionMet.isBottom())
thenResult = eval.evaluateExpression(expr.getThenExpression(), Pair.create(Interval.BOTTOM, condEvalResult.conditionMet));
Pair<Interval, DI> elseResult = null;
if (!condEvalResult.conditionNotMet.isBottom())
elseResult = eval.evaluateExpression(expr.getElseExpression(), Pair.create(Interval.BOTTOM, condEvalResult.conditionNotMet));
if (thenResult == null)
return elseResult;
if (elseResult == null)
return thenResult;
return Pair.create(thenResult.left.join(elseResult.left), thenResult.right.join(elseResult.right));
}
public static <DI extends DomainIntf<DI>> DI getValueForVariable(NonRelationalDomain<DI> di, Variable var) {
if (di.containsValue(var))
return di.getValueFor(var);
throw new DomainException("Missing variable '%s': '%s'", var, di);
}
// public static Bool getValueForVariable(NonRelationalDomain<Bool> di, Variable var) {
// if (di.containsValue(var))
// return di.getValueFor(var);
// System.err.println("MISSING BOOLEAN VARIABLE:" + var);
// return Bool.TOP;
// }
//
// public static Interval getValueForVariable(NonRelationalDomain<Interval> di, Variable var) {
// if (di.containsValue(var))
// return di.getValueFor(var);
// System.err.println("MISSING INTEGER VARIABLE:" + var);
// return Interval.TOP;
// }
//
public static <DI extends DomainIntf<DI>> NonRelationalDomain<DI> updateValueOfVariable(NonRelationalDomain<DI> di,
Variable var, DI value) {
if (di.containsValue(var))
return di.updateVariable(var, value);
System.err.println("MISSING VARIABLE TO UPDATE:" + var);
return di;
}
}