package ai.codestatistics;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import ai.JavaAIPlugin;
import ai.actions.AnalysisErrorHandler;
import ai.actions.AnalysisErrorHandler.ErrorEntry;
import ai.actions.ExecutionUtils;
import ai.cfg.CFGVertice;
import ai.cfg.InterestingCodeFragment;
import ai.cfg.MethodCFGBuilder;
import ai.cfg.MethodControlFlowGraph;
import ai.cfg.MethodFinder;
import ai.codestatistics.DomainComparator.ComparisonResult;
import ai.codestatistics.preferences.AnalysisPreferences;
import ai.codestatistics.preferences.DomainEntry;
import ai.common.Pair;
import ai.domain.AbstractSemanticsIntf;
import ai.domain.DomainIntf;
import ai.domain.boolintv.BoolIntvSemantics;
import ai.domain.boxes.IntegerBoxesSemantics;
import codeStatistics.extension.IAnalysisFunction;
import codeStatistics.extension.IAnalysisPlugin;
import com.google.common.collect.Multimap;
public class AIAnalysis implements IAnalysisPlugin{
private int timeoutSeconds = 60;
public class AIAnalysisResult {
public Map<String, Object> after = new HashMap<String, Object>();
public Map<String, Object> before = new HashMap<String, Object>();
public Map<String, Object> loops = new HashMap<String, Object>();
void setResultBefore(String semantics, Object result) {
if (before.containsKey(semantics))
throw new RuntimeException("Duplicate before value!!");
before.put(semantics, result);
}
void setResultAfter(String semantics, Object result) {
if (after.containsKey(semantics))
throw new RuntimeException("Duplicate after value!!");
after.put(semantics, result);
}
void setLoopInvariant(String semantics, Object result) {
if (loops.containsKey(semantics))
throw new RuntimeException("Duplicate loop invariant!!");
loops.put(semantics, result);
}
}
private AnalysisErrorHandler aeh;
private Map<String, AbstractSemanticsIntf<?>> semanticsToApply = null;
@Override
public String getName() {
return "JavaAI analysis";
}
private AbstractSemanticsIntf<?> getSemantics(String repr) {
if (repr.equals("intv"))
return new BoolIntvSemantics();
if (repr.equals("boxes"))
return new IntegerBoxesSemantics("");
if (repr.startsWith("boxes:"))
return new IntegerBoxesSemantics(repr.substring("boxes:".length()));
throw new RuntimeException("Unknown domain description");
}
private Map<String, AbstractSemanticsIntf<?>> getSemanticsToUse(){
Map<String, AbstractSemanticsIntf<?>> result = new HashMap<String,AbstractSemanticsIntf<?>>();
timeoutSeconds = JavaAIPlugin.getDefault().getPreferenceStore().getInt(AnalysisPreferences.ANALYSIS_TIMEOUT);
// String domains = JavaAIPlugin.getDefault().getPreferenceStore().getString(AnalysisPreferences.DOMAINS_PREFERENCE);
for(DomainEntry entry: AnalysisPreferences.loadDomains(JavaAIPlugin.getDefault().getPreferenceStore().getString(AnalysisPreferences.DOMAINS_PREFERENCE)))
result.put(entry.name, getSemantics(entry.repr));
return result;
}
private MethodControlFlowGraph buildGraph(InterestingCodeFragment codeFragment){
//TODO: log errors!!
try {
return MethodCFGBuilder.buildCFG(codeFragment);
} catch (RuntimeException e) {
System.err.println("Error building graph for:" + codeFragment.getUniqueName());
e.printStackTrace(System.err);
throw e;
}
}
private AIAnalysisResult getOrCreateResult(Map<ASTNode, AIAnalysisResult> result, ASTNode node) {
AIAnalysisResult res = result.get(node);
if (res != null)
return res;
res = new AIAnalysisResult();
result.put(node, res);
return res;
}
private void analyseResult(String semantics, Map<CFGVertice, ?> results,
Pair<Multimap<CFGVertice, ASTNode>, Multimap<CFGVertice, ASTNode>> mappings,
Map<ASTNode, AIAnalysisResult> result, Map<CFGVertice, ASTNode> loopVertices) {
if (results == null) // something was wrong, ignore
return;
for(Map.Entry<CFGVertice, ?> resultEntry: results.entrySet()){
CFGVertice vertice = resultEntry.getKey();
for(ASTNode nodeBeforeVertice: mappings.left.get(vertice))
getOrCreateResult(result, nodeBeforeVertice).setResultAfter(semantics, resultEntry.getValue());
for(ASTNode nodeAfterVertice: mappings.right.get(vertice))
getOrCreateResult(result, nodeAfterVertice).setResultBefore(semantics, resultEntry.getValue());
ASTNode loopNode = loopVertices.get(vertice);
if (loopNode !=null)
getOrCreateResult(result, loopNode).setLoopInvariant(semantics, resultEntry.getValue());
}
}
@SuppressWarnings("unchecked")
@Override
public Map<ASTNode, Object> analyse(CompilationUnit cu) {
System.gc(); // FIXME: trying to solve memory issues
Map<ASTNode, AIAnalysisResult> result = new HashMap<ASTNode, AIAnalysisResult>();
for (InterestingCodeFragment codeFragment: MethodFinder.findMethods(cu)) {
MethodControlFlowGraph graph = buildGraph(codeFragment);
if (graph == null)
continue;
Pair<Multimap<CFGVertice, ASTNode>, Multimap<CFGVertice, ASTNode>> mappings = graph.getVerticeMappings();
Map<CFGVertice, ASTNode> loopVerticesMap = graph.getLoopVerticesMapping();
for(Map.Entry<String, AbstractSemanticsIntf<?>> entry: semanticsToApply.entrySet()) {
Map<CFGVertice, ?> results = ExecutionUtils.analyseOne(graph, entry.getValue(), aeh, codeFragment, timeoutSeconds);
if (results == null) {
results = new HashMap<CFGVertice, Object>();
for(CFGVertice vertice: graph.getVertices())
results.put(vertice, null);
}
analyseResult(entry.getKey(), results, mappings, result, loopVerticesMap);
}
}
Map<ASTNode, ?> res = result;
return (Map<ASTNode, Object>) res;
}
@Override
public void initialiseAnalysis() {
this.aeh = new AnalysisErrorHandler();
this.semanticsToApply = getSemanticsToUse();
}
// functions
private class GetDomainValue implements IAnalysisFunction {
private final boolean beforeOrAfter;
public GetDomainValue(boolean beforeOrAfter) {
this.beforeOrAfter = beforeOrAfter;
}
@Override
public String getName() {
String suffix = beforeOrAfter ? "before" : "after";
return "dom_"+suffix;
}
@Override
public Object execute(Object[] args, Map<ASTNode, Object> analysisResult) {
AIAnalysisResult res = (AIAnalysisResult) analysisResult.get(args[1]);
if (beforeOrAfter)
return res.before.get(args[0]);
else
return res.after.get(args[0]);
}
@Override
public Class<?>[] getArgumentType() {
return new Class<?>[]{String.class, ASTNode.class};
}
@Override
public Class<?> getResultType() {
return DomainIntf.class;
}
}
private class GetLoopInvariantValue implements IAnalysisFunction {
public GetLoopInvariantValue() {
}
@Override
public String getName() {
return "dom_invariant";
}
@Override
public Object execute(Object[] args, Map<ASTNode, Object> analysisResult) {
AIAnalysisResult res = (AIAnalysisResult) analysisResult.get(args[1]);
return res.loops.get(args[0]);
}
@Override
public Class<?>[] getArgumentType() {
return new Class<?>[]{String.class, ASTNode.class};
}
@Override
public Class<?> getResultType() {
return DomainIntf.class;
}
}
private class IsTop implements IAnalysisFunction {
@Override
public String getName() {
return "isTop";
}
@Override
public Object execute(Object[] object, Map<ASTNode, Object> context) {
DomainIntf<?> di = (DomainIntf<?>) object[0];
return di.isTop();
}
@Override
public Class<?>[] getArgumentType() {
return new Class<?>[]{DomainIntf.class};
}
@Override
public Class<?> getResultType() {
return Boolean.class;
}
}
private class LtDomains implements IAnalysisFunction {
@Override
public String getName() {
return "lt";
}
@Override
public Object execute(Object[] args, Map<ASTNode, Object> context) {
DomainIntf<?> left = (DomainIntf<?>) args[0];
DomainIntf<?> right = (DomainIntf<?>) args[1];
ComparisonResult res = DomainComparator.compare(left, right);
return res == ComparisonResult.RIGHT;
}
@Override
public Class<?>[] getArgumentType() {
return new Class<?>[]{DomainIntf.class, DomainIntf.class};
}
@Override
public Class<?> getResultType() {
return Boolean.class;
}
}
private class IncomparableDomains implements IAnalysisFunction {
@Override
public String getName() {
return "incomparable";
}
@Override
public Object execute(Object[] args, Map<ASTNode, Object> context) {
DomainIntf<?> left = (DomainIntf<?>) args[0];
DomainIntf<?> right = (DomainIntf<?>) args[1];
ComparisonResult res = DomainComparator.compare(left, right);
return res == ComparisonResult.UNCOMPARABLE;
}
@Override
public Class<?>[] getArgumentType() {
return new Class<?>[]{DomainIntf.class, DomainIntf.class};
}
@Override
public Class<?> getResultType() {
return Boolean.class;
}
}
@Override
public List<IAnalysisFunction> getExtensionFunctions() {
List<IAnalysisFunction> result = new LinkedList<IAnalysisFunction>();
result.add(new GetDomainValue(true));
result.add(new GetDomainValue(false));
result.add(new IsTop());
result.add(new LtDomains());
result.add(new IncomparableDomains());
result.add(new GetLoopInvariantValue());
return result;
}
@Override
public String getSummary() {
StringBuilder builder = new StringBuilder();
for(ErrorEntry entry: aeh.errors){
builder.append("Code fragment: " + entry.codeFragment.getUniqueName());
builder.append("\n");
builder.append("ERROR:" + entry.error);
builder.append("\n");
builder.append("Domain:" + entry.semantics);
builder.append("\n");
builder.append("-----------------------------------\n");
}
return builder.toString();
}
}