ltd-graph-builder/src/main/java/grafos/CFG.java
2019-04-04 17:14:14 +02:00

260 lines
6.5 KiB
Java

package grafos;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.expr.BooleanLiteralExpr;
import com.github.javaparser.ast.expr.SimpleName;
import com.github.javaparser.ast.stmt.*;
import java.util.*;
public class CFG {
private final List<Node> nodes = new LinkedList<>();
private final LinkedList<Block> blocks = new LinkedList<>();
private final Stack<Block> blockStack = new Stack<>();
private final List<Edge> edges = new LinkedList<>();
private final Node beginNode = new EmptyStmt(), endNode = new EmptyStmt();
private final LinkedList<Node> nodeList = new LinkedList<>();
private final Map<Node, String> labelMap = new HashMap<>();
private final Map<SimpleName, List<BreakStmt>> breakMap = new HashMap<>();
private final Stack<List<BreakStmt>> breakStack = new Stack<>();
private void registerNode(Node stmt) {
nodes.add(stmt);
if (!blockStack.isEmpty())
blockStack.peek().add(stmt);
System.out.println("NODO: " + node2str(stmt));
}
public void setNode(Node node) {
clearNode();
appendNode(node);
}
public void clearNode() {
nodeList.clear();
}
public void connectTo(Node node) {
connectTo(node, true);
}
public void connectTo(Node node, boolean overwrite) {
if (!nodesContains(node))
registerNode(node);
for (Node begin : nodeList)
connect(begin, node);
if (overwrite)
setNode(node);
}
private boolean nodesContains(Object object) {
for (Node n : nodes)
if (n == object)
return true;
return false;
}
public void removeNode(Node node) {
nodeList.remove(node);
}
public void appendNode(Node node) {
nodeList.add(node);
}
public void appendNodes(Collection<? extends Node> nodes) {
nodeList.addAll(nodes);
}
public List<Node> getNodeList() {
return Collections.unmodifiableList(nodeList);
}
public void setNodeList(List<Node> nodes) {
nodeList.clear();
nodeList.addAll(nodes);
}
public void beginMethod() {
nodeList.clear();
nodeList.add(beginNode);
}
public void finishMethod() {
for (Node begin : nodeList)
if (begin != endNode)
connect(begin, endNode);
}
public void beginBlock(Node node, Node condition) {
Block b = new Block(node, condition);
if (condition != null)
registerNode(condition);
if (blockStack.isEmpty())
blocks.addFirst(b);
else
blockStack.peek().addSubBlock(b);
blockStack.push(b);
}
public void endBlock() {
blockStack.pop();
}
public void setNextLabel(boolean b) {
labelMap.put(nodeList.getLast(), String.valueOf(b));
}
private void connect(Node begin, Node end) {
edges.add(new Edge(begin, end, labelMap.get(begin)));
labelMap.remove(begin);
}
public Node getEndNode() {
return endNode;
}
public void beginBreakSection(SimpleName name) {
breakMap.put(name, new LinkedList<>());
}
public void beginBreakSection() {
breakStack.push(new LinkedList<>());
}
public void registerBreak(BreakStmt stmt) {
List<BreakStmt> list;
if (stmt.getLabel().isPresent()) {
list = breakMap.get(stmt.getLabel().get());
} else {
list = breakStack.peek();
}
list.add(stmt);
}
public void endBreakSection(SimpleName name) {
nodeList.addAll(breakMap.remove(name));
}
public void endBreakSection() {
nodeList.addAll(breakStack.pop());
}
public List<String> toStringList(GraphViz gv) {
List<String> res = new LinkedList<>();
res.add(gv.start_graph());
for (Block b : blocks)
res.addAll(b.toStringList(gv));
for (Edge e : edges) {
res.add(e.toString());
}
res.add(gv.end_graph());
return res;
}
private String node2str(Node n) {
if (n == beginNode) return "Start";
if (n == endNode) return "Stop";
return String.format("\"(%d, %d) %s\"", indexOfNode(n) + 1, n.getRange().get().begin.line, escape(n.toString()));
}
private int indexOfNode(Node n) {
for (int i = 0; i < nodes.size(); i++)
if (nodes.get(i) == n)
return i;
throw new RuntimeException("Internal error, can't find node in list");
}
private String escape(String s) {
return s.replace("\"", "\\\"");
}
private static int clusterId = 0;
class Block extends LinkedList<Node> {
private final Node container, condition;
private final List<Block> inners = new LinkedList<>();
Block(Node container, Node condition) {
this.container = container;
this.condition = condition;
}
void addSubBlock(Block block) {
inners.add(block);
}
List<String> toStringList(GraphViz gv) {
List<String> res = new LinkedList<>();
res.add(gv.start_subgraph(clusterId++));
res.add("label = \"" + getTitle() + "\"");
if (condition != null) {
res.add("node [ shape = rectangle ]; " + node2str(condition));
res.add("node [ shape = ellipse ]");
}
for (Block b : inners) {
res.addAll(b.toStringList(gv));
}
for (Node n : this) {
res.add(node2str(n));
}
res.add(gv.end_subgraph());
return res;
}
String getTitle() {
String template;
Object[] filler;
if (container instanceof IfStmt) {
template = "if (%s)";
filler = new Object[]{((IfStmt) container).getCondition()};
} else if (container instanceof WhileStmt) {
template = "while (%s)";
filler = new Object[]{((WhileStmt) container).getCondition()};
} else if (container instanceof DoStmt) {
template = "do-while (%s)";
filler = new Object[]{((DoStmt) container).getCondition()};
} else if (container instanceof ForStmt) {
template = "for (%s; %s; %s)";
filler = new Object[]{
((ForStmt) container).getInitialization(),
((ForStmt) container).getCompare().orElse(new BooleanLiteralExpr(true)),
((ForStmt) container).getUpdate()};
} else if (container instanceof ForeachStmt) {
template = "for (%s : %s)";
filler = new Object[]{
((ForeachStmt) container).getVariable(),
((ForeachStmt) container).getIterable()};
} else if (container instanceof BlockStmt) {
template = "";
filler = new Object[0];
} else if (container instanceof SwitchStmt) {
template = "switch (%s)";
filler = new Object[]{((SwitchStmt) container).getSelector()};
} else {
throw new RuntimeException("Unimplemented!!!");
// TODO: remove
}
return String.format(template, filler);
}
}
class Edge {
private final Node origin, destination;
private final String label;
Edge(Node origin, Node destination, String label) {
this.origin = origin;
this.destination = destination;
this.label = label;
}
public String toString() {
String s = node2str(origin) + " -> " + node2str(destination);
if (label != null) {
s += " [ label = \"" + label + "\" ]";
}
return s;
}
}
}