1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-03 07:29:37 +00:00
kickc/src/main/java/dk/camelot64/kickc/passes/reports/ReportCFG.java

111 lines
5.0 KiB
Java

package dk.camelot64.kickc.passes.reports;
import dk.camelot64.kickc.model.Graph;
import dk.camelot64.kickc.model.ProcedureCompilation;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ProcedureRef;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
/**
* Create a report of the Control Block Flow Graph (CFG) of the whole Program.
* This class generates a markdown report with embedded mermaid graphics of
* the whole Program CFGs.
*/
public class ReportCFG extends Report {
private MermaidGraph mermaidGraph;
public ReportCFG(Program program, String pass, String extension) {
super(program, pass, extension);
}
public void mermaidCFGBlock(Graph graph, Graph.Block block, MermaidSubGraph mermaidSubGraph) {
String blockName = block.getLabel().getFullName();
MermaidNode node = mermaidSubGraph.addNode(new MermaidNode(blockName));
node.setStatements(block.getStatements());
List<Graph.Block> predecessors = program.getGraph().getPredecessors(block);
if(!predecessors.isEmpty()) {
for(Graph.Block predecessor : predecessors) {
String predecessorNodeName = predecessor.getLabel().getFullName();
String predecessorLocalNodeName = predecessor.getScope().getLocalName();
if (program.isProcedureEntry(block)) {
MermaidSubGraph procedureSubGraph = mermaidSubGraph.addProcedureSubGraph(predecessorLocalNodeName);
MermaidNode nodePredecessor = procedureSubGraph.addNode(predecessorNodeName);
mermaidSubGraph.addFlow(nodePredecessor, node, "call", MermaidFlow.Type.FULL, MermaidFlow.Direction.UNI);
String blockReturnName = block.getLabel().getLocalName() + "::@return";
MermaidNode nodeReturn = mermaidSubGraph.addNode(blockReturnName);
mermaidSubGraph.addFlow(nodeReturn, nodePredecessor, "return", MermaidFlow.Type.FULL, MermaidFlow.Direction.UNI);
} else {
MermaidNode nodePredecessor = mermaidSubGraph.addNode(predecessorNodeName);
LabelRef successorLabel = predecessor.getConditionalSuccessor();
Graph.Block successor = null;
if(successorLabel != null) {
successor = graph.getBlock(successorLabel);
}
if( successor != null && successor.equals(block)) {
mermaidSubGraph.addFlow(nodePredecessor, node, "true", MermaidFlow.Type.DOTTED, MermaidFlow.Direction.UNI);
} else {
mermaidSubGraph.addFlow(nodePredecessor, node, null, MermaidFlow.Type.DOTTED, MermaidFlow.Direction.UNI);
}
}
}
}
if(!block.getStatements().isEmpty()) {
for (Statement statement : block.getStatements()) {
if(statement instanceof StatementCall) {
String procedureName = ((StatementCall) statement).getProcedureName();
MermaidSubGraph procedureSubGraph = mermaidSubGraph.addProcedureSubGraph(procedureName);
MermaidNode procedureNode = procedureSubGraph.addNode(procedureName);
mermaidSubGraph.addFlow(node, procedureNode,"call", MermaidFlow.Type.FULL, MermaidFlow.Direction.BI);
}
}
}
}
private String mermaidCFGProcedure(Procedure procedure, Graph graph) {
String procedureName = procedure.getLocalName();
String procedureTitle = procedure.toString(program);
MermaidSubGraph subGraphProcedure = new MermaidSubGraph(procedureName, procedureTitle);
mermaidGraph.addProcedureSubGraph(subGraphProcedure);
for(Graph.Block block : graph.getAllBlocks()) {
mermaidCFGBlock(graph, block, subGraphProcedure);
}
return mermaidGraph.getText(program);
}
@Override
public String generateReport(Program program) {
StringBuilder report = new StringBuilder();
for(ProcedureCompilation procedureCompilation : program.getProcedureCompilations()) {
Procedure procedure = program.getScope().getProcedure(procedureCompilation.getProcedureRef());
Graph graph = procedureCompilation.getGraph();
if(graph != null) {
mermaidGraph = new MermaidGraph(MermaidGraph.Direction.TOP_DOWN);
report.append("Procedure " + procedure.getFullName() + "\n");
report.append("```mermaid\n");
report.append(configureReport());
report.append(mermaidCFGProcedure(procedure, graph));
report.append("```\n\n");
}
}
return report.toString();
}
}