package ai.cfg.verifiers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.ConstructorInvocation;
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.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.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
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.actions.AnalysisErrorHandler;
import ai.cfg.CFGVertice;
import ai.cfg.MethodControlFlowGraph;
import ai.common.Pair;
import ai.domain.AbstractSemanticsIntf;
import ai.domain.DomainIntf;
import ai.domain.Variable;
import ai.domain.expressions.AbstractExpressionVisitor;
import ai.domain.intervals.eval.EvaluationUtils;
import ai.domain.widening.DefaultWideningOperator;
import ai.domain.widening.WideningOperator;
import ai.interpreter.Interpreter;
import ai.interpreter.StopCondition;
public class VariablesVerifier {
public static class VerifierException extends RuntimeException{
private static final long serialVersionUID = -1202020406445208236L;
public VerifierException(String format, Object... args) {
super(String.format(format, args));
}
}
private static class Variables implements DomainIntf<Variables>{
private final Set<Variable> vars;
public Variables(Set<Variable> vars) {
this.vars = vars;
}
@Override
public boolean isBottom() {
return false;
}
@Override
public boolean isTop() {
return false;
}
@Override
public Variables join(Variables other) {
if (!vars.equals(other.vars))
throw new VerifierException("Not the same variable sets");
return this;
}
@Override
public Variables meet(Variables other) {
throw new VerifierException("Not handled");
}
@Override
public Variables widen(Variables other) {
return join(other);
}
@Override
public boolean leq(Variables other) {
return false;
}
@Override
public boolean equals(Variables other) {
return this.vars.equals(other.vars);
}
@Override
public Variables getBottom() {
throw new VerifierException("Not handled");
}
private Variable getVar(SimpleName name){
Variable var = EvaluationUtils.tryGetVariable(name);
if (var == null)
throw new VerifierException("null variable");
return var;
}
private Variables addVariable(SimpleName name, boolean asAssignment) {
Variable var = getVar(name);
if (asAssignment) {
if (!vars.contains(var))
throw new VerifierException("missing variable");
return this;
}
if (vars.contains(var))
throw new VerifierException("variable already exists");
HashSet<Variable> newVars = new HashSet<Variable>(vars);
newVars.add(var);
return new Variables(newVars);
}
private Variables removeVariables(Collection<SimpleName> namesToRemove) {
HashSet<Variable> newVars = new HashSet<Variable>(vars);
for(SimpleName name: namesToRemove) {
Variable var = getVar(name);
if (!newVars.contains(var))
throw new VerifierException("variable not present");
newVars.remove(var);
}
return new Variables(newVars);
}
public String toString(){
return "Variables(" + vars +")";
}
}
private static class VarExpressionVisitor extends AbstractExpressionVisitor {
private final Set<Variable> vars;
public VarExpressionVisitor(Set<Variable> vars){
this.vars = vars;
}
@Override
public boolean visit(ArrayAccess node) {
return true;
}
@Override
public boolean visit(ArrayCreation node) {
return true;
}
@Override
public boolean visit(ArrayInitializer node) {
return true;
}
@Override
public boolean visit(Assignment node) {
return true;
}
@Override
public boolean visit(BooleanLiteral node) {
return true;
}
@Override
public boolean visit(CastExpression node) {
return true;
}
@Override
public boolean visit(CharacterLiteral node) {
return true;
}
private void acceptChild(ASTNode node) {
if (node != null)
node.accept(this);
}
private void acceptChildren(List<?> nodes) {
if (nodes == null)
return;
for(Object node: nodes)
((ASTNode)node).accept(this);
}
@Override
public boolean visit(ClassInstanceCreation node) {
//do not visit subclass definition
acceptChild(node.getExpression());
acceptChildren(node.arguments());
return false;
}
@Override
public boolean visit(ConditionalExpression node) {
return true;
}
@Override
public boolean visit(FieldAccess node) {
return false;
}
@Override
public boolean visit(InfixExpression node) {
return true;
}
@Override
public boolean visit(InstanceofExpression node) {
return true;
}
@Override
public boolean visit(MethodInvocation node) {
return true;
}
@Override
public boolean visit(SimpleName node) {
Variable var = EvaluationUtils.tryGetVariable(node);
if (var!=null && !vars.contains(var))
throw new VerifierException("Missing variable: '%s' expr: '%s'", var, node);
return false;
}
@Override
public boolean visit(QualifiedName node) {
return false;
}
@Override
public boolean visit(NullLiteral node) {
return false;
}
@Override
public boolean visit(NumberLiteral node) {
return false;
}
@Override
public boolean visit(ParenthesizedExpression node) {
return true;
}
@Override
public boolean visit(PostfixExpression node) {
return true;
}
@Override
public boolean visit(PrefixExpression node) {
return true;
}
@Override
public boolean visit(StringLiteral node) {
return false;
}
@Override
public boolean visit(SuperFieldAccess node) {
return false;
}
@Override
public boolean visit(SuperMethodInvocation node) {
return true;
}
@Override
public boolean visit(ThisExpression node) {
return false;
}
@Override
public boolean visit(TypeLiteral node) {
return false;
}
@Override
public boolean visit(VariableDeclarationExpression node) {
return false;
}
}
private static class ComputeVariablesSemantics implements AbstractSemanticsIntf<Variables> {
@Override
public Variables processArgument(Variables input, SimpleName name) {
return input.addVariable(name, false);
}
@Override
public Variables processEmptyEdge(Variables input, List<SimpleName> variablesToRemove) {
return input.removeVariables(variablesToRemove);
}
@Override
public Variables processExpression(Variables input, Expression expression) {
expression.accept(new VarExpressionVisitor(input.vars));
return input;
}
@Override
public Variables processNewVariable(Variables input, SimpleName name, Expression initializerOrNull,
boolean asAssignment) {
if (initializerOrNull != null)
initializerOrNull.accept(new VarExpressionVisitor(input.vars));
return input.addVariable(name, asAssignment);
}
@Override
public Variables getInitialValue() {
return new Variables(new HashSet<Variable>());
}
@Override
public Variables processFinallyOrException(Variables input, SingleVariableDeclaration excOrNull) {
if (excOrNull != null)
return input.addVariable(excOrNull.getName(), false);
return input;
}
@Override
public Variables processConstructorInvocation(Variables input, ConstructorInvocation constructorInvocation) {
constructorInvocation.accept(new VarExpressionVisitor(input.vars));
return input;
}
@Override
public Variables processSuperConstructorInvocation(Variables input,
SuperConstructorInvocation superConstructorInvocation) {
superConstructorInvocation.accept(new VarExpressionVisitor(input.vars));
return input;
}
@Override
public Pair<Variables, Variables> processCondition(Variables input, Expression conditionOrNull) {
if (conditionOrNull != null) {
try {
conditionOrNull.accept(new VarExpressionVisitor(input.vars));
} catch (RuntimeException e) {
System.err.println("Error evaluating: " + conditionOrNull);
throw e;
}
}
return Pair.create(input, input);
}
@Override
public Pair<Variables, ArrayList<Variables>> processSwitchCases(Variables input, Expression expr,
Expression[] switchCases) {
expr.accept(new VarExpressionVisitor(input.vars));
ArrayList<Variables> res = new ArrayList<Variables>(switchCases.length);
for(int i=0; i< switchCases.length; i++)
res.add(input);
return Pair.create(input, res);
}
@Override
public WideningOperator<Variables> getWideningOperator() {
return new DefaultWideningOperator<Variables>();
}
}
public static Map<CFGVertice, Set<Variable>> verifyGraphVariables(MethodControlFlowGraph graph,
AnalysisErrorHandler aeh) {
ComputeVariablesSemantics semantics = new ComputeVariablesSemantics();
Map<CFGVertice, Variables> x;
try {
x = Interpreter.analyse(graph, semantics, new StopCondition.NoStopCondition());
} catch (RuntimeException e) {
if (aeh.handleError(graph.getCodeFragment(), e, semantics))
return null;
throw e;
}
Map<CFGVertice, Set<Variable>> result = new HashMap<CFGVertice, Set<Variable>>();
for(Map.Entry<CFGVertice, Variables> entry: x.entrySet())
result.put(entry.getKey(), entry.getValue().vars);
return result;
}
}