package dk.brics.jscontrolflow.display;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import dk.brics.jscontrolflow.Block;
import dk.brics.jscontrolflow.Function;
import dk.brics.jscontrolflow.Statement;
import dk.brics.jscontrolflow.analysis.liveness.Liveness;
import dk.brics.jsutil.DotUtil;
public class Function2Dot {
public enum ExceptionalEdgeDisplay {
/**
* No exceptional edges will be displayed.
*/
HIDE,
/**
* Every block's exceptional edge will be displayed (if it has one).
*/
SHOW,
/**
* Only blocks whose exception handler is different from one of its successors'
* exception handler, and blocks without successors, will have their exceptional
* edge displayed (if they have one).
* <p/>
* This is the recommended technique when not debugging control-flow specifically.
*/
COMPRESS,
}
public static void printToFile(File file, Function function) {
printToFile(file, function, ExceptionalEdgeDisplay.SHOW);
}
public static void printToFile(File file, Function function, ExceptionalEdgeDisplay exceptionalEdges) {
PrintStream stream = null;
try {
stream = new PrintStream(file);
print(stream, function, exceptionalEdges);
} catch (IOException ex) {
throw new RuntimeException(ex);
} finally {
if (stream != null) {
stream.close();
}
}
}
/**
* Prints a single function as a Graphviz dot graph.
*/
public static void print(PrintStream out, Function function, ExceptionalEdgeDisplay exceptionalEdges) {
Liveness live = new Liveness(function);
out.println("digraph {");
out.println(" rankdir=\"TD\"");
Map<Block,Integer> block2index = new HashMap<Block,Integer>();
int nextindex=1;
for (Block block : function.getBlocks()) {
int index = nextindex++;
block2index.put(block, index);
out.printf(" BB_%d [shape=record, label=\"{", index); // start of block decl
out.print(live.getLiveBefore(block).toString());
for (Statement stm : block.getStatements()) {
String stmString = Statement2Dot.toDot(stm);
String labelString = DotUtil.escapeLabel(stmString);
// if (stm != block.getFirst()) {
out.print("|");
// }
out.print(labelString);
}
out.print("|");
out.print(live.getLiveAfter(block).toString());
out.println("}\"]"); // end of block decl
}
// print edges
List<String> colors = new ArrayList<String>();
colors.add("blue1");
colors.add("green1");
colors.add("red1");
int colorindex=0;
for (Block block : function.getBlocks()) {
int index1 = block2index.get(block);
for (Block succ : block.getSuccessors()) {
int index2 = block2index.get(succ);
String color = colors.get(++colorindex % colors.size());
out.printf(" BB_%d -> BB_%d [tailport=s, headport=n, color=%s]\n", index1, index2, color);
}
if (block.getExceptionHandler() != null && exceptionalEdges != ExceptionalEdgeDisplay.HIDE) {
int index2 = block2index.get(block.getExceptionHandler());
boolean canCompress = exceptionalEdges == ExceptionalEdgeDisplay.COMPRESS && block.getSuccessors().size() > 0;
for (Block succ : block.getSuccessors()) {
canCompress &= succ.getExceptionHandler() == block.getExceptionHandler();
}
if (!canCompress) {
out.printf(" BB_%d -> BB_%d [tailport=s, headport=n, color=gray]\n", index1, index2);
}
}
}
// print entry edge
out.println(" INIT [shape=plaintext, label=\"\"]");
out.printf(" INIT -> BB_%d [tailport=s, headport=n, label=\"Entry\"]\n", block2index.get(function.getEntry()));
out.println("}");
}
}