package freeboogie.tc; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import freeboogie.ast.*; import freeboogie.util.Err; /** * Constructs a flowgraph of blocks for each implementation. * * @author rgrig * @author reviewed by TODO */ @SuppressWarnings("unused") // unused params public class BlockFlowGraphs extends Transformer { // finds blocks (in the currently processed Body) by name private HashMap<String, Block> blocksByName; // the flow graph currently being built private SimpleGraph<Block> currentFlowGraph; // the block currently being processed private Block currentBlock; // maps implementations to the flow graphs of their bodies private HashMap<Implementation, SimpleGraph<Block>> flowGraphs; // whether inexistent block names were detected private boolean errors; // used fror reachability (DFS) private HashSet<Block> seenBlocks; // === public interface === /** * Constructs flow graphs for {@code ast}. It also prints warnings * if there are syntactically unreachable blocks. * @param ast the AST for which to build flow graphs * @return whether there were missing blocks */ public boolean process(Declaration ast) { currentBlock = null; errors = false; flowGraphs = new HashMap<Implementation, SimpleGraph<Block>>(); ast.eval(this); return errors; } /** * Returns the block flow graph for {@code impl}. * @param impl the implementation * @return the flow graph fro {@code impl} */ public SimpleGraph<Block> getFlowGraph(Implementation impl) { return flowGraphs.get(impl); } // === helpers === private void dfs(Block b) { if (seenBlocks.contains(b)) return; seenBlocks.add(b); Set<Block> children = currentFlowGraph.to(b); for (Block c : children) dfs(c); } // === visiting methods === @Override public void see(Implementation implementation, Signature sig, Body body, Declaration tail) { // initialize graph currentFlowGraph = new SimpleGraph<Block>(); flowGraphs.put(implementation, currentFlowGraph); // get blocks by name blocksByName = new HashMap<String, Block>(); Block b = body.getBlocks(); while (b != null) { blocksByName.put(b.getName(), b); currentFlowGraph.node(b); b = b.getTail(); } // build graph body.eval(this); // check for reachability seenBlocks = new HashSet<Block>(); b = body.getBlocks(); if (b == null) return; dfs(b); while (b != null) { if (!seenBlocks.contains(b)) Err.warning("" + b.loc() + ": Block " + b.getName() + " is unreachable."); b = b.getTail(); } if (tail != null) tail.eval(this); } @Override public void see(Block block, String name, Commands cmds, Identifiers succ, Block tail) { currentBlock = block; if (succ != null) succ.eval(this); currentBlock = null; if (tail != null) tail.eval(this); } @Override public void see(AtomId atomId, String id) { if (currentBlock == null) return; Block target = blocksByName.get(id); if (target == null) { Err.error("" + atomId.loc() + ": Inexistent block " + id + "."); errors = true; } else currentFlowGraph.edge(currentBlock, target); } }