package ai.interpreter;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.apache.log4j.Logger;
import ai.cfg.CFGVertice;
import ai.cfg.MethodControlFlowGraph;
import ai.cfg.Utils;
import ai.cfg.edges.CFGMultiTargetEdge;
import ai.cfg.edges.CFGMultiTargetEdge.CFGSingleEdge;
import ai.common.MutableInt;
import ai.common.Pair;
import ai.domain.AbstractSemanticsIntf;
import ai.domain.DomainIntf;
import ai.domain.widening.WideningOperator;
public class Interpreter<DI extends DomainIntf<DI>> {
private class MyQueue {
private Queue<CFGVertice> queue = new LinkedList<CFGVertice>();
private Set<CFGVertice> vertices = new HashSet<CFGVertice>();
public void add(CFGVertice vertice) {
if (vertices.contains(vertice)) return;
queue.add(vertice);
vertices.add(vertice);
}
public CFGVertice poll() {
CFGVertice result = queue.poll();
vertices.remove(result);
return result;
}
public boolean isEmpty() {
return queue.isEmpty();
}
}
private final static Logger log = Logger.getLogger(Interpreter.class.getName());
private final MethodControlFlowGraph graph;
private final DI initialValue;
private final StopCondition sc;
private Interpreter(MethodControlFlowGraph graph, DI initialValue, StopCondition sc){
this.graph = graph;
this.initialValue = initialValue;
this.sc = sc;
}
private static <DI extends DomainIntf<DI>, T> DI calcualteCumulativeInput(List<T> inputs, Map<T, DI> edgeValues) {
DI result = null;
for (T inEdge : inputs) {
if (!edgeValues.containsKey(inEdge))
continue;
DI value = edgeValues.get(inEdge);
if (result == null) {
result = value;
} else {
result = result.join(value);
}
}
return result;
}
private static <T, DI> void putNonNullValue(Map<T, DI> map, T key, DI value) {
if (value == null)
throw new InterpreterException("Invalid domain null value");
map.put(key, value);
}
private static final String MESSAGE_TEMPLATE = "Widening required(method:'%s',semantics:'%s'), iteration: %s";
private static <DI extends DomainIntf<DI>> void logWidening(MethodControlFlowGraph cfg, AbstractSemanticsIntf<DI> semantics, int iteration) {
log.debug("Widening required, iteration " + iteration);
if (iteration > 1)
log.warn(String.format(MESSAGE_TEMPLATE, cfg.getCodeFragment().getUniqueName(), semantics, iteration));
}
private Pair<CFGSingleEdge, DI> computeInputEdgeValue(AbstractSemanticsIntf<DI> semantics) {
List<Pair<CFGSingleEdge, DI>> x = graph.getInput().acceptVerified(semantics, initialValue!=null ? initialValue : semantics.getInitialValue());
if (x.size()!=1)
throw new InterpreterException("This shouldn't happen");
return x.get(0);
}
private static final Set<CFGSingleEdge> EMPTY_EDGES_SET = new HashSet<CFGSingleEdge>();
private boolean allInputsCalculated(List<CFGSingleEdge> inputs, Map<CFGSingleEdge, DI> values,
Collection<CFGSingleEdge> loopEdges){
if (loopEdges == null)
loopEdges = EMPTY_EDGES_SET;
for(CFGSingleEdge input: inputs)
if (values.get(input) == null && !loopEdges.contains(input))
return false;
return true;
}
private Map<CFGVertice, DI> performAnalysis(AbstractSemanticsIntf<DI> semantics){
// Utils.printCFG(graph);
Map<CFGVertice, DI> values = new HashMap<CFGVertice, DI>();
Map<CFGSingleEdge, DI> edgeValues = new HashMap<CFGSingleEdge, DI>();
Map<CFGVertice, List<CFGSingleEdge>> inputs = Utils.computeInputEdges(graph);
//find loops and prepare counting widening iterations
Map<CFGVertice, MutableInt> wideningCounts = new HashMap<CFGVertice, MutableInt>();
Map<CFGVertice, Collection<CFGSingleEdge>> loopVertices = Utils.findLoopVertices(graph);
Map<CFGVertice, WideningOperator<DI>> widenings = new HashMap<CFGVertice, WideningOperator<DI>>();
for(CFGVertice loopVertice: loopVertices.keySet()) {
wideningCounts.put(loopVertice, new MutableInt(0));
widenings.put(loopVertice, semantics.getWideningOperator());
}
Pair<CFGSingleEdge, DI> start = computeInputEdgeValue(semantics);
MyQueue queue = new MyQueue();
putNonNullValue(edgeValues, start.left, start.right);
queue.add(start.left.target);
while(!queue.isEmpty()) {
if (sc.shouldStop()) {
System.out.println("---------------interrupted!");
return null;
}
CFGVertice vertice = queue.poll();
//compute inputs
log.debug("Processing vertice:" + vertice);
DI oldValue = values.get(vertice);
log.debug("Cumulative input before: " + oldValue);
DI newValue = calcualteCumulativeInput(inputs.get(vertice), edgeValues);
log.debug("Cumulative input after: " + newValue);
//perform widening if required
if (oldValue != null && wideningCounts.containsKey(vertice)) {
int iteration = wideningCounts.get(vertice).inc();
WideningOperator<DI> wid = widenings.get(vertice);
newValue = wid.widen(oldValue, newValue);
logWidening(graph, semantics, iteration);
}
// //verify if semantics is "correct"
// if (newValue == null || (oldValue != null && !oldValue.leq(newValue)))
// throw new InterpreterException("Invalid semantics!!, old value: '%s', new value: '%s'", oldValue, newValue);
//no changes, no propagation required
if (oldValue !=null && oldValue.equals(newValue))
continue;
values.put(vertice, newValue);
//we have to propagate!!
for(CFGMultiTargetEdge multiEdge: vertice.getOutgoingMultiEdges())
for(Pair<CFGSingleEdge, DI> res: multiEdge.acceptVerified(semantics, newValue)){
log.debug(" Edge: "+res.left);
CFGSingleEdge edge = res.left;
DI newEdgeValue = res.right;
// DI oldEdgeValue = edgeValues.get(res.left);
// //verify if the semantics is "correct"
// if (newEdgeValue == null || (oldEdgeValue != null && !oldEdgeValue.leq(newEdgeValue)))
// throw new InterpreterException("Invalid semantics of '%s'!!", edge.toString());
edgeValues.put(edge, newEdgeValue);
//add target to queue
if (allInputsCalculated(inputs.get(edge.target), edgeValues, loopVertices.get(edge.target)))
queue.add(edge.target);
}
}
return values;
}
public static <DI extends DomainIntf<DI>> Map<CFGVertice, DI> analyse(MethodControlFlowGraph graph, AbstractSemanticsIntf<DI> semantics,
StopCondition sc) {
return analyse(graph, semantics, null, sc);
}
/**
* FOR TESTS ONLY!!
* @param graph
* @param semantics
* @param initialValue
* @return
*/
public static <DI extends DomainIntf<DI>> Map<CFGVertice, DI> analyse(MethodControlFlowGraph graph, AbstractSemanticsIntf<DI> semantics,
DI initialValue, StopCondition sc) {
Interpreter<DI> interpreter = new Interpreter<DI>(graph, initialValue, sc);
Map<CFGVertice, DI> values = interpreter.performAnalysis(semantics);
sc.stop();
return values == null ? null : Collections.unmodifiableMap(values);
}
}