Documentation and comments
This commit is contained in:
		
					parent
					
						
							
								5ca4da62dc
							
						
					
				
			
			
				commit
				
					
						9aad602df3
					
				
			
		
					 3 changed files with 195 additions and 44 deletions
				
			
		| 
						 | 
					@ -7,21 +7,58 @@ import com.github.javaparser.ast.stmt.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Represents a control flow graph, offering methods to build it easily with a visitor and an output ready to be
 | 
				
			||||||
 | 
					 * converted to a graph by GraphViz ({@link #toStringList(GraphViz)}).
 | 
				
			||||||
 | 
					 * <br/>
 | 
				
			||||||
 | 
					 * The process should begin with {@link #beginMethod()} and end with {@link #finishMethod()}. Similarly, every opened
 | 
				
			||||||
 | 
					 * {@link Block} and section must be closed. The class offers special support for {@link ContinueStmt} and {@link BreakStmt},
 | 
				
			||||||
 | 
					 * including both the labeled and unlabeled versions.
 | 
				
			||||||
 | 
					 * <br/>
 | 
				
			||||||
 | 
					 * The inner class {@link Block} is used to represent a block statement (not only {@link BlockStmt} but also {@link IfStmt}
 | 
				
			||||||
 | 
					 * and {@link WhileStmt}, for example). {@link Block} should not be used directly, only through the API provided by CFG.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
public class CFG {
 | 
					public class CFG {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * List of nodes that exist in this CFG (can either be {@link com.github.javaparser.ast.expr.Expression} or {@link Statement})
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	private final List<Node> nodes = new LinkedList<>();
 | 
						private final List<Node> nodes = new LinkedList<>();
 | 
				
			||||||
 | 
						/** List of blocks (statements that contain other statements) in this CFG.
 | 
				
			||||||
 | 
						 * Only includes the "surface" blocks, not the ones inside other blocks.
 | 
				
			||||||
 | 
						 * @see Block */
 | 
				
			||||||
	private final LinkedList<Block> blocks = new LinkedList<>();
 | 
						private final LinkedList<Block> blocks = new LinkedList<>();
 | 
				
			||||||
 | 
						/** Stack of blocks, representing the position in the method. The current position is inside the topmost block,
 | 
				
			||||||
 | 
						 * which is contained inside the previous one. If empty, the position is outside all blocks, in the main level
 | 
				
			||||||
 | 
						 * of the method. */
 | 
				
			||||||
	private final Stack<Block> blockStack = new Stack<>();
 | 
						private final Stack<Block> blockStack = new Stack<>();
 | 
				
			||||||
 | 
						/** List of {@link Edge}, or connections between nodes. */
 | 
				
			||||||
	private final List<Edge> edges = new LinkedList<>();
 | 
						private final List<Edge> edges = new LinkedList<>();
 | 
				
			||||||
 | 
						/** First and last node of a method, represented by an {@link EmptyStmt} instance. */
 | 
				
			||||||
	private final Node beginNode = new EmptyStmt(), endNode = new EmptyStmt();
 | 
						private final Node beginNode = new EmptyStmt(), endNode = new EmptyStmt();
 | 
				
			||||||
 | 
						/** List of nodes that are immediately before the next instruction, and therefore will be linked to it.
 | 
				
			||||||
 | 
						 * May be empty after a break/return/continue, or contain more than one element after a control-flow instruction. */
 | 
				
			||||||
	private final LinkedList<Node> nodeList = new LinkedList<>();
 | 
						private final LinkedList<Node> nodeList = new LinkedList<>();
 | 
				
			||||||
 | 
						/** Map of nodes to the name that needs to be applied to the edge when that node is connected to the next one.
 | 
				
			||||||
 | 
						 * Used to distinguish the {@code true} and {@code false} branches in conditions. */
 | 
				
			||||||
	private final Map<Node, String> labelMap = new HashMap<>();
 | 
						private final Map<Node, String> labelMap = new HashMap<>();
 | 
				
			||||||
 | 
						/** Map of loop labels to lists of break statements (all labeled). Each list begins empty and {@link BreakStmt} are
 | 
				
			||||||
 | 
						 * added as found. When the end of the labeled loop is reached, the list is removed and dumped into {@link #nodeList}. */
 | 
				
			||||||
	private final Map<SimpleName, List<BreakStmt>> breakMap = new HashMap<>();
 | 
						private final Map<SimpleName, List<BreakStmt>> breakMap = new HashMap<>();
 | 
				
			||||||
 | 
						/** Stack of lists of break statements (without label). A new list is added when entering any structure that may be broken out of,
 | 
				
			||||||
 | 
						 * and the list is dumped into the {@link #nodeList} when exiting the loop. */
 | 
				
			||||||
	private final Stack<List<BreakStmt>> breakStack = new Stack<>();
 | 
						private final Stack<List<BreakStmt>> breakStack = new Stack<>();
 | 
				
			||||||
 | 
						/** Map of loop labels to the node that the loop "continues" to, i.e., the node to which a continue stmt will be
 | 
				
			||||||
 | 
						 * connected. */
 | 
				
			||||||
	private final Map<SimpleName, Node> continueMap = new HashMap<>();
 | 
						private final Map<SimpleName, Node> continueMap = new HashMap<>();
 | 
				
			||||||
 | 
						/** Stack of the node that the loop "continues" to.
 | 
				
			||||||
 | 
						 * @see #continueMap */
 | 
				
			||||||
	private final Stack<Node> continueStack = new Stack<>();
 | 
						private final Stack<Node> continueStack = new Stack<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Last labeled visited, if a {@link LabeledStmt} has been visited, but the corresponding loop hasn't been yet.
 | 
				
			||||||
 | 
						 * Otherwise, {@code null}. */
 | 
				
			||||||
	private SimpleName lastLabel;
 | 
						private SimpleName lastLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Adds a node to the list of nodes known by this tree */
 | 
				
			||||||
	private void registerNode(Node stmt) {
 | 
						private void registerNode(Node stmt) {
 | 
				
			||||||
		nodes.add(stmt);
 | 
							nodes.add(stmt);
 | 
				
			||||||
		if (!blockStack.isEmpty())
 | 
							if (!blockStack.isEmpty())
 | 
				
			||||||
| 
						 | 
					@ -29,19 +66,24 @@ public class CFG {
 | 
				
			||||||
		System.out.println("NODO: " + node2str(stmt));
 | 
							System.out.println("NODO: " + node2str(stmt));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Clears the list of nodes and adds the given node. */
 | 
				
			||||||
	public void setNode(Node node) {
 | 
						public void setNode(Node node) {
 | 
				
			||||||
		clearNode();
 | 
							clearNode();
 | 
				
			||||||
		appendNode(node);
 | 
							appendNode(node);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Clears the list of nodes. */
 | 
				
			||||||
	public void clearNode() {
 | 
						public void clearNode() {
 | 
				
			||||||
		nodeList.clear();
 | 
							nodeList.clear();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Connect each node in the list of nodes to the current node, then set the node as the only element in the list. */
 | 
				
			||||||
	public void connectTo(Node node) {
 | 
						public void connectTo(Node node) {
 | 
				
			||||||
		connectTo(node, true);
 | 
							connectTo(node, true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Connect each node in the list of nodes to the current node, then optionally set the node as the only
 | 
				
			||||||
 | 
						 * element in the list. */
 | 
				
			||||||
	public void connectTo(Node node, boolean overwrite) {
 | 
						public void connectTo(Node node, boolean overwrite) {
 | 
				
			||||||
		if (!nodesContains(node))
 | 
							if (!nodesContains(node))
 | 
				
			||||||
			registerNode(node);
 | 
								registerNode(node);
 | 
				
			||||||
| 
						 | 
					@ -51,6 +93,8 @@ public class CFG {
 | 
				
			||||||
			setNode(node);
 | 
								setNode(node);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Checks if an object is in the node registration list. Implemented to use {@code ==} instead of
 | 
				
			||||||
 | 
						 * {@link List#contains(Object)}, which uses {@link Object#equals(Object)}. */
 | 
				
			||||||
	private boolean nodesContains(Object object) {
 | 
						private boolean nodesContains(Object object) {
 | 
				
			||||||
		for (Node n : nodes)
 | 
							for (Node n : nodes)
 | 
				
			||||||
			if (n == object)
 | 
								if (n == object)
 | 
				
			||||||
| 
						 | 
					@ -58,38 +102,45 @@ public class CFG {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Removes a node from the list of nodes */
 | 
				
			||||||
	public void removeNode(Node node) {
 | 
						public void removeNode(Node node) {
 | 
				
			||||||
		nodeList.remove(node);
 | 
							nodeList.remove(node);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Adds a node to the list of nodes */
 | 
				
			||||||
	public void appendNode(Node node) {
 | 
						public void appendNode(Node node) {
 | 
				
			||||||
		nodeList.add(node);
 | 
							nodeList.add(node);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void appendNodes(Collection<? extends Node> nodes) {
 | 
						/** Returns an unmodifiable copy of the list of nodes */
 | 
				
			||||||
		nodeList.addAll(nodes);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	public List<Node> getNodeList() {
 | 
						public List<Node> getNodeList() {
 | 
				
			||||||
		return Collections.unmodifiableList(nodeList);
 | 
							return Collections.unmodifiableList(nodeList);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Replace the list of nodes with this list. The argument may be reused without affecting the list of nodes. */
 | 
				
			||||||
	public void setNodeList(List<Node> nodes) {
 | 
						public void setNodeList(List<Node> nodes) {
 | 
				
			||||||
		nodeList.clear();
 | 
							nodeList.clear();
 | 
				
			||||||
		nodeList.addAll(nodes);
 | 
							nodeList.addAll(nodes);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Method operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Begin a method: clear the list of nodes and add the "Start" node */
 | 
				
			||||||
	public void beginMethod() {
 | 
						public void beginMethod() {
 | 
				
			||||||
		nodeList.clear();
 | 
							nodeList.clear();
 | 
				
			||||||
		nodeList.add(beginNode);
 | 
							nodeList.add(beginNode);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Finish a method: connect each element of the list of nodes to the end (unless they are the end already) */
 | 
				
			||||||
	public void finishMethod() {
 | 
						public void finishMethod() {
 | 
				
			||||||
		for (Node begin : nodeList)
 | 
							for (Node begin : nodeList)
 | 
				
			||||||
			if (begin != endNode)
 | 
								if (begin != endNode)
 | 
				
			||||||
				connect(begin, endNode);
 | 
									connect(begin, endNode);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Block operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Start a new block, with a node that it represents and its condition. */
 | 
				
			||||||
	public void beginBlock(Node node, Node condition) {
 | 
						public void beginBlock(Node node, Node condition) {
 | 
				
			||||||
		Block b = new Block(node, condition);
 | 
							Block b = new Block(node, condition);
 | 
				
			||||||
		if (condition != null)
 | 
							if (condition != null)
 | 
				
			||||||
| 
						 | 
					@ -101,14 +152,18 @@ public class CFG {
 | 
				
			||||||
		blockStack.push(b);
 | 
							blockStack.push(b);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** End a block */
 | 
				
			||||||
	public void endBlock() {
 | 
						public void endBlock() {
 | 
				
			||||||
		blockStack.pop();
 | 
							blockStack.pop();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Set the label to be used when the last element of the list of nodes is connected to the next node. Run this
 | 
				
			||||||
 | 
						 * AFTER adding the node with {@link #appendNode(Node)}. */
 | 
				
			||||||
	public void setNextLabel(boolean b) {
 | 
						public void setNextLabel(boolean b) {
 | 
				
			||||||
		labelMap.put(nodeList.getLast(), String.valueOf(b));
 | 
							labelMap.put(nodeList.getLast(), String.valueOf(b));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Connects one node with another, checking the map of labels and clearing the appropriate entry */
 | 
				
			||||||
	private void connect(Node begin, Node end) {
 | 
						private void connect(Node begin, Node end) {
 | 
				
			||||||
		edges.add(new Edge(begin, end, labelMap.get(begin)));
 | 
							edges.add(new Edge(begin, end, labelMap.get(begin)));
 | 
				
			||||||
		labelMap.remove(begin);
 | 
							labelMap.remove(begin);
 | 
				
			||||||
| 
						 | 
					@ -118,14 +173,20 @@ public class CFG {
 | 
				
			||||||
		return endNode;
 | 
							return endNode;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Break operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Begin a new list of break statements associated with the given label */
 | 
				
			||||||
	public void beginBreakSection(SimpleName name) {
 | 
						public void beginBreakSection(SimpleName name) {
 | 
				
			||||||
		breakMap.put(name, new LinkedList<>());
 | 
							breakMap.put(name, new LinkedList<>());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Begin a new list of unlabeled break statements */
 | 
				
			||||||
	public void beginBreakSection() {
 | 
						public void beginBreakSection() {
 | 
				
			||||||
		breakStack.push(new LinkedList<>());
 | 
							breakStack.push(new LinkedList<>());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Connect a break statement to the chain, then add it to the appropriate list, so that when the switch/loop ends
 | 
				
			||||||
 | 
						 * it can be connected to the proper node, which is currently unknown. */
 | 
				
			||||||
	public void connectBreak(BreakStmt stmt) {
 | 
						public void connectBreak(BreakStmt stmt) {
 | 
				
			||||||
		connectTo(stmt);
 | 
							connectTo(stmt);
 | 
				
			||||||
		if (stmt.getLabel().isPresent()) {
 | 
							if (stmt.getLabel().isPresent()) {
 | 
				
			||||||
| 
						 | 
					@ -136,19 +197,29 @@ public class CFG {
 | 
				
			||||||
		clearNode();
 | 
							clearNode();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Ends a list of labeled break statements, adding all the break statements collected to the list of nodes. */
 | 
				
			||||||
	public void endBreakSection(SimpleName name) {
 | 
						public void endBreakSection(SimpleName name) {
 | 
				
			||||||
		nodeList.addAll(breakMap.remove(name));
 | 
							nodeList.addAll(breakMap.remove(name));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Ends a list of unlabeled break statements, adding all the break statements collected to the list of nodes.
 | 
				
			||||||
 | 
						 * Restores the previous list (if it exists), so that nested loops/switch statements work properly */
 | 
				
			||||||
	public void endBreakSection() {
 | 
						public void endBreakSection() {
 | 
				
			||||||
		nodeList.addAll(breakStack.pop());
 | 
							nodeList.addAll(breakStack.pop());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Continue operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Begin a labeled continue section (loop). Inside the loop, {@link #beginContinueSection(Node)} must be called for
 | 
				
			||||||
 | 
						 * this method to work properly. */
 | 
				
			||||||
	public void beginContinueSection(SimpleName name) {
 | 
						public void beginContinueSection(SimpleName name) {
 | 
				
			||||||
		assert lastLabel == null;
 | 
							assert lastLabel == null;
 | 
				
			||||||
		lastLabel = name;
 | 
							lastLabel = name;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Begin a continue section (loop). Will register the given node as the to-go node when a continue statement is
 | 
				
			||||||
 | 
						 * found, and optionally register it with the proper label if {@link #beginContinueSection(SimpleName)} has been
 | 
				
			||||||
 | 
						 * called immediately before. */
 | 
				
			||||||
	public void beginContinueSection(Node node) {
 | 
						public void beginContinueSection(Node node) {
 | 
				
			||||||
		continueStack.push(node);
 | 
							continueStack.push(node);
 | 
				
			||||||
		if (lastLabel != null) {
 | 
							if (lastLabel != null) {
 | 
				
			||||||
| 
						 | 
					@ -158,6 +229,8 @@ public class CFG {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Connect each statement in the list of nodes to the continue statement, then connect the continue to the proper
 | 
				
			||||||
 | 
						 * statement, depending on whether it has a label or not. */
 | 
				
			||||||
	public void connectContinue(ContinueStmt stmt) {
 | 
						public void connectContinue(ContinueStmt stmt) {
 | 
				
			||||||
		connectTo(stmt);
 | 
							connectTo(stmt);
 | 
				
			||||||
		if (stmt.getLabel().isPresent()) {
 | 
							if (stmt.getLabel().isPresent()) {
 | 
				
			||||||
| 
						 | 
					@ -168,14 +241,17 @@ public class CFG {
 | 
				
			||||||
		clearNode();
 | 
							clearNode();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Ends a labeled continue section */
 | 
				
			||||||
	public void endContinueSection(SimpleName name) {
 | 
						public void endContinueSection(SimpleName name) {
 | 
				
			||||||
		continueMap.remove(name);
 | 
							continueMap.remove(name);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Ends an unlabeled continue section (loop). Works with nested loops */
 | 
				
			||||||
	public void endContinueSection() {
 | 
						public void endContinueSection() {
 | 
				
			||||||
		continueStack.pop();
 | 
							continueStack.pop();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Outputs the contents of this object as a list of strings, ready to be parsed by dot and turned into a graph. */
 | 
				
			||||||
	public List<String> toStringList(GraphViz gv) {
 | 
						public List<String> toStringList(GraphViz gv) {
 | 
				
			||||||
		List<String> res = new LinkedList<>();
 | 
							List<String> res = new LinkedList<>();
 | 
				
			||||||
		res.add(gv.start_graph());
 | 
							res.add(gv.start_graph());
 | 
				
			||||||
| 
						 | 
					@ -188,12 +264,15 @@ public class CFG {
 | 
				
			||||||
		return res;
 | 
							return res;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Converts a node to string, prepending an index and line number, and escaping any quotations. */
 | 
				
			||||||
	private String node2str(Node n) {
 | 
						private String node2str(Node n) {
 | 
				
			||||||
		if (n == beginNode) return "Start";
 | 
							if (n == beginNode) return "Start";
 | 
				
			||||||
		if (n == endNode) return "Stop";
 | 
							if (n == endNode) return "Stop";
 | 
				
			||||||
		return String.format("\"(%d, %d) %s\"", indexOfNode(n) + 1, n.getRange().get().begin.line, escape(n.toString()));
 | 
							return String.format("\"(%d, %d) %s\"", indexOfNode(n) + 1, n.getRange().get().begin.line, escape(n.toString()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Finds the index of a node in the node registration list. Throws an exception if unsuccessful.
 | 
				
			||||||
 | 
						 * This implementation is necessary to use {@code ==} instead of {@link Object#equals(Object)}. */
 | 
				
			||||||
	private int indexOfNode(Node n) {
 | 
						private int indexOfNode(Node n) {
 | 
				
			||||||
		for (int i = 0; i < nodes.size(); i++)
 | 
							for (int i = 0; i < nodes.size(); i++)
 | 
				
			||||||
			if (nodes.get(i) == n)
 | 
								if (nodes.get(i) == n)
 | 
				
			||||||
| 
						 | 
					@ -201,14 +280,25 @@ public class CFG {
 | 
				
			||||||
		throw new RuntimeException("Internal error, can't find node in list");
 | 
							throw new RuntimeException("Internal error, can't find node in list");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Escapes a string. Currently only escapes quotation marks. */
 | 
				
			||||||
	private String escape(String s) {
 | 
						private String escape(String s) {
 | 
				
			||||||
		return s.replace("\"", "\\\"");
 | 
							return s.replace("\"", "\\\"");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Id for each subgraph/block (incremented with each new block) */
 | 
				
			||||||
	private static int clusterId = 0;
 | 
						private static int clusterId = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Representation of a statement containing {@link Node}, such as an if, loop or switch statement */
 | 
				
			||||||
	class Block extends LinkedList<Node> {
 | 
						class Block extends LinkedList<Node> {
 | 
				
			||||||
		private final Node container, condition;
 | 
							/**
 | 
				
			||||||
 | 
							 * The node that is being represented by this block
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							private final Node container;
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * The condition to enter the current block, or the condition of the if
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							private final Node condition;
 | 
				
			||||||
 | 
							/** The sub-blocks present in this Block */
 | 
				
			||||||
		private final List<Block> inners = new LinkedList<>();
 | 
							private final List<Block> inners = new LinkedList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Block(Node container, Node condition) {
 | 
							Block(Node container, Node condition) {
 | 
				
			||||||
| 
						 | 
					@ -216,29 +306,36 @@ public class CFG {
 | 
				
			||||||
			this.condition = condition;
 | 
								this.condition = condition;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/** Add a sub-block to this block */
 | 
				
			||||||
		void addSubBlock(Block block) {
 | 
							void addSubBlock(Block block) {
 | 
				
			||||||
			inners.add(block);
 | 
								inners.add(block);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/** Get a representation ready to be used in dot (of the subgraph) */
 | 
				
			||||||
		List<String> toStringList(GraphViz gv) {
 | 
							List<String> toStringList(GraphViz gv) {
 | 
				
			||||||
			List<String> res = new LinkedList<>();
 | 
								List<String> res = new LinkedList<>();
 | 
				
			||||||
			res.add(gv.start_subgraph(clusterId++));
 | 
								res.add(gv.start_subgraph(clusterId++)); // start
 | 
				
			||||||
			res.add("label = \"" + getTitle() + "\"");
 | 
								res.add("label = \"" + getTitle() + "\""); // title of the sub-graph
 | 
				
			||||||
 | 
								// Add the condition node with a special shape, then reset the shape back to the default
 | 
				
			||||||
			if (condition != null) {
 | 
								if (condition != null) {
 | 
				
			||||||
				res.add("node [ shape = rectangle ]; " + node2str(condition));
 | 
									res.add("node [ shape = rectangle ]; " + node2str(condition));
 | 
				
			||||||
				res.add("node [ shape = ellipse ]");
 | 
									res.add("node [ shape = ellipse ]");
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								// Add all inner blocks
 | 
				
			||||||
			for (Block b : inners) {
 | 
								for (Block b : inners) {
 | 
				
			||||||
				res.addAll(b.toStringList(gv));
 | 
									res.addAll(b.toStringList(gv));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								// Add all nodes as part of the sub-graph
 | 
				
			||||||
			for (Node n : this) {
 | 
								for (Node n : this) {
 | 
				
			||||||
				res.add(node2str(n));
 | 
									res.add(node2str(n));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			res.add(gv.end_subgraph());
 | 
								res.add(gv.end_subgraph()); // end
 | 
				
			||||||
			return res;
 | 
								return res;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/** Generate a title for the sub-graph based on the block instruction it represents. */
 | 
				
			||||||
		String getTitle() {
 | 
							String getTitle() {
 | 
				
			||||||
 | 
								// Variables to be filled for String#format(String, Object[])
 | 
				
			||||||
			String template;
 | 
								String template;
 | 
				
			||||||
			Object[] filler;
 | 
								Object[] filler;
 | 
				
			||||||
			if (container instanceof IfStmt) {
 | 
								if (container instanceof IfStmt) {
 | 
				
			||||||
| 
						 | 
					@ -269,22 +366,23 @@ public class CFG {
 | 
				
			||||||
				filler = new Object[]{((SwitchStmt) container).getSelector()};
 | 
									filler = new Object[]{((SwitchStmt) container).getSelector()};
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				throw new RuntimeException("Unimplemented!!!");
 | 
									throw new RuntimeException("Unimplemented!!!");
 | 
				
			||||||
				// TODO: remove
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return String.format(template, filler);
 | 
								return String.format(template, filler);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	class Edge {
 | 
						/** Representation of a connection between two nodes, with an optional label */
 | 
				
			||||||
 | 
						private class Edge {
 | 
				
			||||||
		private final Node origin, destination;
 | 
							private final Node origin, destination;
 | 
				
			||||||
		private final String label;
 | 
							private final String label;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Edge(Node origin, Node destination, String label) {
 | 
							private Edge(Node origin, Node destination, String label) {
 | 
				
			||||||
			this.origin = origin;
 | 
								this.origin = origin;
 | 
				
			||||||
			this.destination = destination;
 | 
								this.destination = destination;
 | 
				
			||||||
			this.label = label;
 | 
								this.label = label;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Override
 | 
				
			||||||
		public String toString() {
 | 
							public String toString() {
 | 
				
			||||||
			String s = node2str(origin) + " -> " + node2str(destination);
 | 
								String s = node2str(origin) + " -> " + node2str(destination);
 | 
				
			||||||
			if (label != null) {
 | 
								if (label != null) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,9 @@ public class Transformador {
 | 
				
			||||||
			analyzeFile(file);
 | 
								analyzeFile(file);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Runs {@link #analyzeFile(File)} for each Java file found in this directory, recursively.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public static void analyzeDir(File dir) throws FileNotFoundException {
 | 
						public static void analyzeDir(File dir) throws FileNotFoundException {
 | 
				
			||||||
		for (File f : dir.listFiles()) {
 | 
							for (File f : dir.listFiles()) {
 | 
				
			||||||
			if (f.isDirectory())
 | 
								if (f.isDirectory())
 | 
				
			||||||
| 
						 | 
					@ -28,6 +31,7 @@ public class Transformador {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Analyzes and generates a CFG in a PDF from a Java file. */
 | 
				
			||||||
	public static void analyzeFile(File file) throws FileNotFoundException {
 | 
						public static void analyzeFile(File file) throws FileNotFoundException {
 | 
				
			||||||
		System.out.println("BEGIN FILE " + file.getPath());
 | 
							System.out.println("BEGIN FILE " + file.getPath());
 | 
				
			||||||
		// Ruta del fichero con el programa que vamos a transformar
 | 
							// Ruta del fichero con el programa que vamos a transformar
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,10 @@ import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
 | 
				
			||||||
import java.util.LinkedList;
 | 
					import java.util.LinkedList;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Visits a compilation unit and extracts the control flow graph, registering all the data in the argument to all
 | 
				
			||||||
 | 
					 * functions: {@link CFG}. This class contains no information, all data is saved in the control flow graph.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
public class Visitador extends VoidVisitorAdapter<CFG> {
 | 
					public class Visitador extends VoidVisitorAdapter<CFG> {
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(MethodDeclaration methodDeclaration, CFG graph) {
 | 
						public void visit(MethodDeclaration methodDeclaration, CFG graph) {
 | 
				
			||||||
| 
						 | 
					@ -19,6 +23,10 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
 | 
				
			||||||
		graph.finishMethod();
 | 
							graph.finishMethod();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Return statements are connected to the previous statement and to the end node of the graph.
 | 
				
			||||||
 | 
						 * Schema: {@code -> return -> Stop}
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(ReturnStmt n, CFG arg) {
 | 
						public void visit(ReturnStmt n, CFG arg) {
 | 
				
			||||||
		arg.connectTo(n);
 | 
							arg.connectTo(n);
 | 
				
			||||||
| 
						 | 
					@ -26,24 +34,29 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
 | 
				
			||||||
		arg.clearNode();
 | 
							arg.clearNode();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** If statements are set up as a block, with a condition and supports any configuration, including missing else,
 | 
				
			||||||
 | 
						 * empty then/else blocks. */
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(IfStmt n, CFG graph) {
 | 
						public void visit(IfStmt n, CFG graph) {
 | 
				
			||||||
		// Connect to the condition
 | 
							// prev --> cond
 | 
				
			||||||
		Node ifStart = n.getCondition();
 | 
							Node ifStart = n.getCondition();
 | 
				
			||||||
		graph.beginBlock(n, ifStart);
 | 
							graph.beginBlock(n, ifStart);
 | 
				
			||||||
		graph.connectTo(ifStart);
 | 
							graph.connectTo(ifStart);
 | 
				
			||||||
		// Connect condition to then block
 | 
							// cond --> then (if true)
 | 
				
			||||||
		graph.setNextLabel(true);
 | 
							graph.setNextLabel(true);
 | 
				
			||||||
		n.getThenStmt().accept(this, graph);
 | 
							n.getThenStmt().accept(this, graph);
 | 
				
			||||||
		// Save end of then block
 | 
							// then --> next (saved for later)
 | 
				
			||||||
		List<Node> newPrev = new LinkedList<>(graph.getNodeList());
 | 
							List<Node> newPrev = new LinkedList<>(graph.getNodeList());
 | 
				
			||||||
		// Connect condition to else block
 | 
							// Else processing (either connect cond --> else --> next or cond --> next)
 | 
				
			||||||
		graph.setNode(ifStart);
 | 
							graph.setNode(ifStart);
 | 
				
			||||||
		graph.setNextLabel(false);
 | 
							graph.setNextLabel(false);
 | 
				
			||||||
		if (n.getElseStmt().isPresent()) {
 | 
							if (n.getElseStmt().isPresent()) {
 | 
				
			||||||
 | 
								// cond --> else (if false)
 | 
				
			||||||
			n.getElseStmt().get().accept(this, graph);
 | 
								n.getElseStmt().get().accept(this, graph);
 | 
				
			||||||
 | 
								// else --> next
 | 
				
			||||||
			newPrev.addAll(graph.getNodeList());
 | 
								newPrev.addAll(graph.getNodeList());
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 | 
								// cond --> next (if false)
 | 
				
			||||||
			newPrev.add(ifStart);
 | 
								newPrev.add(ifStart);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// Set ends of then/else as the new list
 | 
							// Set ends of then/else as the new list
 | 
				
			||||||
| 
						 | 
					@ -51,6 +64,7 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
 | 
				
			||||||
		graph.endBlock();
 | 
							graph.endBlock();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Registers the label used for the loop statement in the break and continue sections of the CFG */
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(LabeledStmt n, CFG graph) {
 | 
						public void visit(LabeledStmt n, CFG graph) {
 | 
				
			||||||
		graph.beginBreakSection(n.getLabel());
 | 
							graph.beginBreakSection(n.getLabel());
 | 
				
			||||||
| 
						 | 
					@ -60,130 +74,165 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
 | 
				
			||||||
		graph.endBreakSection(n.getLabel());
 | 
							graph.endBreakSection(n.getLabel());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** While statements are set up as a block. They also generate an unlabeled break and continue section.
 | 
				
			||||||
 | 
						 * The schema followed is {@code -> cond -> body -> cond ->} */
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(WhileStmt n, CFG graph) {
 | 
						public void visit(WhileStmt n, CFG graph) {
 | 
				
			||||||
		Node whileStart = n.getCondition();
 | 
							Node whileStart = n.getCondition();
 | 
				
			||||||
 | 
							// Begin block/sections
 | 
				
			||||||
		graph.beginBlock(n, whileStart);
 | 
							graph.beginBlock(n, whileStart);
 | 
				
			||||||
		graph.beginBreakSection();
 | 
							graph.beginBreakSection();
 | 
				
			||||||
		graph.beginContinueSection(whileStart);
 | 
							graph.beginContinueSection(whileStart);
 | 
				
			||||||
 | 
							// prev --> cond
 | 
				
			||||||
		graph.connectTo(whileStart);
 | 
							graph.connectTo(whileStart);
 | 
				
			||||||
 | 
							// cond --> body (if true)
 | 
				
			||||||
		graph.setNextLabel(true);
 | 
							graph.setNextLabel(true);
 | 
				
			||||||
		n.getBody().accept(this, graph);
 | 
							n.getBody().accept(this, graph);
 | 
				
			||||||
 | 
							// body --> cond
 | 
				
			||||||
		graph.connectTo(whileStart);
 | 
							graph.connectTo(whileStart);
 | 
				
			||||||
 | 
							// cond --> next (if false)
 | 
				
			||||||
		graph.setNextLabel(false);
 | 
							graph.setNextLabel(false);
 | 
				
			||||||
 | 
							// End block/sections
 | 
				
			||||||
		graph.endContinueSection();
 | 
							graph.endContinueSection();
 | 
				
			||||||
		graph.endBreakSection();
 | 
							graph.endBreakSection();
 | 
				
			||||||
		graph.endBlock();
 | 
							graph.endBlock();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Do while statement, set up as block. Generates break and continue section. Schema:
 | 
				
			||||||
 | 
						 * {@code -> body -> cond -> body -> cond ->} */
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(DoStmt n, CFG graph) {
 | 
						public void visit(DoStmt n, CFG graph) {
 | 
				
			||||||
		Node condition = n.getCondition();
 | 
							Node condition = n.getCondition();
 | 
				
			||||||
 | 
							// Begin block/section
 | 
				
			||||||
		graph.beginBlock(n, condition);
 | 
							graph.beginBlock(n, condition);
 | 
				
			||||||
		graph.beginBreakSection();
 | 
							graph.beginBreakSection();
 | 
				
			||||||
		graph.beginContinueSection(condition);
 | 
							graph.beginContinueSection(condition);
 | 
				
			||||||
 | 
							// prev --> body
 | 
				
			||||||
 | 
							// cond --> body (if true)
 | 
				
			||||||
		graph.appendNode(condition);
 | 
							graph.appendNode(condition);
 | 
				
			||||||
		n.getBody().accept(this, graph);
 | 
							n.getBody().accept(this, graph);
 | 
				
			||||||
 | 
							// body --> cond
 | 
				
			||||||
		graph.connectTo(condition);
 | 
							graph.connectTo(condition);
 | 
				
			||||||
 | 
							// cond --> next (if false)
 | 
				
			||||||
		graph.setNextLabel(false);
 | 
							graph.setNextLabel(false);
 | 
				
			||||||
 | 
							// End block/section
 | 
				
			||||||
		graph.endContinueSection();
 | 
							graph.endContinueSection();
 | 
				
			||||||
		graph.endBreakSection();
 | 
							graph.endBreakSection();
 | 
				
			||||||
		graph.endBlock();
 | 
							graph.endBlock();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** For statement, set up as a block. Generates break and continue section. If the condition is empty, it will be
 | 
				
			||||||
 | 
						 * set as a {@code true} literal expression. Schema: {@code -> initializer(s) -> cond -> body -> update(s) -> cond -> } */
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(ForStmt n, CFG graph) {
 | 
						public void visit(ForStmt n, CFG graph) {
 | 
				
			||||||
 | 
							// Begin block and break sections (continue section needs the comparison, see below)
 | 
				
			||||||
		graph.beginBlock(n, n.getCompare().orElse(null));
 | 
							graph.beginBlock(n, n.getCompare().orElse(null));
 | 
				
			||||||
		graph.beginBreakSection();
 | 
							graph.beginBreakSection();
 | 
				
			||||||
		// Initialization expressions
 | 
							// prev --> initializer(s), chained in serial
 | 
				
			||||||
		if (n.getInitialization() != null) {
 | 
							for (Expression e : n.getInitialization())
 | 
				
			||||||
			for (Expression e : n.getInitialization()) {
 | 
					 | 
				
			||||||
			graph.connectTo(e);
 | 
								graph.connectTo(e);
 | 
				
			||||||
			}
 | 
							// Set the comparison expression to "true" if the expression was empty, as a safeguard and to ease the treatment
 | 
				
			||||||
		}
 | 
							// of cases where the loop is of the form "for (;;) ;", which would require special processing.
 | 
				
			||||||
		// Comparison expression
 | 
					 | 
				
			||||||
		// TODO: shortcut conditions (||, &&)
 | 
					 | 
				
			||||||
		assert n.getTokenRange().isPresent();
 | 
							assert n.getTokenRange().isPresent();
 | 
				
			||||||
		if (!n.getCompare().isPresent())
 | 
							if (!n.getCompare().isPresent())
 | 
				
			||||||
			n.setCompare(new BooleanLiteralExpr(n.getTokenRange().get(), true));
 | 
								n.setCompare(new BooleanLiteralExpr(n.getTokenRange().get(), true));
 | 
				
			||||||
 | 
							// Begin continue section (with the comparison on hand)
 | 
				
			||||||
		graph.beginContinueSection(n.getCompare().get());
 | 
							graph.beginContinueSection(n.getCompare().get());
 | 
				
			||||||
 | 
							// initializer(s) --> cond
 | 
				
			||||||
		graph.connectTo(n.getCompare().get());
 | 
							graph.connectTo(n.getCompare().get());
 | 
				
			||||||
 | 
							// cond --> body (if true)
 | 
				
			||||||
		graph.setNextLabel(true);
 | 
							graph.setNextLabel(true);
 | 
				
			||||||
		// Loop body
 | 
					 | 
				
			||||||
		n.getBody().accept(this, graph);
 | 
							n.getBody().accept(this, graph);
 | 
				
			||||||
		// Update expressions
 | 
							// body --> update(s), chained in serial
 | 
				
			||||||
		if (n.getUpdate() != null)
 | 
					 | 
				
			||||||
		for (Expression e : n.getUpdate())
 | 
							for (Expression e : n.getUpdate())
 | 
				
			||||||
			graph.connectTo(e);
 | 
								graph.connectTo(e);
 | 
				
			||||||
		// Connect to comparison expression
 | 
							// update(s) --> comp
 | 
				
			||||||
		// Set comparison as last possible statement
 | 
					 | 
				
			||||||
		graph.connectTo(n.getCompare().get());
 | 
							graph.connectTo(n.getCompare().get());
 | 
				
			||||||
 | 
							// comp --> next (if false)
 | 
				
			||||||
		graph.setNextLabel(false);
 | 
							graph.setNextLabel(false);
 | 
				
			||||||
 | 
							// End block/section (all of them)
 | 
				
			||||||
		graph.endContinueSection();
 | 
							graph.endContinueSection();
 | 
				
			||||||
		graph.endBreakSection();
 | 
							graph.endBreakSection();
 | 
				
			||||||
		graph.endBlock();
 | 
							graph.endBlock();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Foreach statement, set up as a block. Generates break and continue sections. Schema: {@code -> cond -> body -> cond ->} */
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(ForeachStmt n, CFG graph) {
 | 
						public void visit(ForeachStmt n, CFG graph) {
 | 
				
			||||||
 | 
							// Create a copy of the foreach statement to use as the condition node. The alternative would be to "emulate" the
 | 
				
			||||||
 | 
							// foreach statement with a for statement.
 | 
				
			||||||
		ForeachStmt copy = new ForeachStmt(n.getTokenRange().orElse(null), n.getVariable(), n.getIterable(), new EmptyStmt());
 | 
							ForeachStmt copy = new ForeachStmt(n.getTokenRange().orElse(null), n.getVariable(), n.getIterable(), new EmptyStmt());
 | 
				
			||||||
 | 
							// Begin block/section
 | 
				
			||||||
		graph.beginBlock(n, copy);
 | 
							graph.beginBlock(n, copy);
 | 
				
			||||||
		graph.beginBreakSection();
 | 
							graph.beginBreakSection();
 | 
				
			||||||
		graph.beginContinueSection(copy);
 | 
							graph.beginContinueSection(copy);
 | 
				
			||||||
 | 
							// prev --> cond
 | 
				
			||||||
		graph.connectTo(copy);
 | 
							graph.connectTo(copy);
 | 
				
			||||||
 | 
							// cond --> body (if true)
 | 
				
			||||||
		graph.setNextLabel(true);
 | 
							graph.setNextLabel(true);
 | 
				
			||||||
		n.getBody().accept(this, graph);
 | 
							n.getBody().accept(this, graph);
 | 
				
			||||||
 | 
							// body --> cond
 | 
				
			||||||
		graph.connectTo(copy);
 | 
							graph.connectTo(copy);
 | 
				
			||||||
 | 
							// cond --> next (if false)
 | 
				
			||||||
		graph.setNextLabel(false);
 | 
							graph.setNextLabel(false);
 | 
				
			||||||
		graph.endContinueSection();
 | 
							graph.endContinueSection();
 | 
				
			||||||
		graph.endBreakSection();
 | 
							graph.endBreakSection();
 | 
				
			||||||
		graph.endBlock();
 | 
							graph.endBlock();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Switch entry, considered part of the condition of the switch. */
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(SwitchEntryStmt n, CFG graph) {
 | 
						public void visit(SwitchEntryStmt n, CFG graph) {
 | 
				
			||||||
		// Case header
 | 
							// Case header (prev -> case EXPR)
 | 
				
			||||||
		Node caseNode = new SwitchEntryStmt(n.getTokenRange().orElse(null), n.getLabel().orElse(null), new NodeList<>());
 | 
							Node caseNode = new SwitchEntryStmt(n.getTokenRange().orElse(null), n.getLabel().orElse(null), new NodeList<>());
 | 
				
			||||||
		graph.connectTo(caseNode);
 | 
							graph.connectTo(caseNode);
 | 
				
			||||||
		// Case body
 | 
							// Case body (case EXPR --> body)
 | 
				
			||||||
		n.getStatements().accept(this, graph);
 | 
							n.getStatements().accept(this, graph);
 | 
				
			||||||
 | 
							// body --> next
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Switch statement, set up as a block. Generates an unlabeled break section. The statement begins with an expression,
 | 
				
			||||||
 | 
						 * which is compared to each of the cases, entering through the first one that matches. Each case may end in a break
 | 
				
			||||||
 | 
						 * or return statement, or not. When it doesn't the control falls through the next cases. If the switch doesn't have
 | 
				
			||||||
 | 
						 * a default entry, the */
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(SwitchStmt n, CFG graph) {
 | 
						public void visit(SwitchStmt n, CFG graph) {
 | 
				
			||||||
		// Link previous statement to the switch's selector
 | 
							// Link previous statement to the switch's selector
 | 
				
			||||||
		Node selectorNode = n.getSelector();
 | 
							Node selectorNode = n.getSelector();
 | 
				
			||||||
 | 
							// Begin block and break section
 | 
				
			||||||
		graph.beginBlock(n, selectorNode);
 | 
							graph.beginBlock(n, selectorNode);
 | 
				
			||||||
		graph.beginBreakSection();
 | 
							graph.beginBreakSection();
 | 
				
			||||||
 | 
							// prev --> expr
 | 
				
			||||||
		graph.connectTo(selectorNode);
 | 
							graph.connectTo(selectorNode);
 | 
				
			||||||
		// Analyze switch's cases
 | 
							// expr --> each case (fallthrough by default, so case --> case too)
 | 
				
			||||||
		for (SwitchEntryStmt entry : n.getEntries()) {
 | 
							for (SwitchEntryStmt entry : n.getEntries()) {
 | 
				
			||||||
			entry.accept(this, graph);
 | 
								entry.accept(this, graph); // expr && prev case --> case --> next case
 | 
				
			||||||
			graph.appendNode(selectorNode);
 | 
								graph.appendNode(selectorNode); // expr --> next case
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// The next statement will be linked to:
 | 
							// The next statement will be linked to:
 | 
				
			||||||
		//		1. All break statements that broke from the switch
 | 
							//		1. All break statements that broke from the switch (done with break section)
 | 
				
			||||||
		// 		2. If the switch doesn't have a default statement, the switch's selector
 | 
							// 		2. If the switch doesn't have a default statement, the switch's selector (already present)
 | 
				
			||||||
		// 		3. If the last entry doesn't break, to the last statement
 | 
							// 		3. If the last entry doesn't break, to the last statement (present already)
 | 
				
			||||||
 | 
							// If the last case is a default case, remove the selector node from the list of nodes (see 2)
 | 
				
			||||||
 | 
							if (!n.getEntries().get(n.getEntries().size() - 1).getLabel().isPresent())
 | 
				
			||||||
			graph.removeNode(selectorNode);
 | 
								graph.removeNode(selectorNode);
 | 
				
			||||||
		if (n.getEntries().get(n.getEntries().size() - 1).getLabel().isPresent())
 | 
							// End block and break section
 | 
				
			||||||
			graph.appendNode(selectorNode);
 | 
					 | 
				
			||||||
		graph.endBreakSection();
 | 
							graph.endBreakSection();
 | 
				
			||||||
		graph.endBlock();
 | 
							graph.endBlock();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(BreakStmt n, CFG graph) {
 | 
						public void visit(BreakStmt n, CFG graph) {
 | 
				
			||||||
		graph.connectBreak(n);
 | 
							graph.connectBreak(n); // prev --> break --> end of loop
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(ContinueStmt n, CFG graph) {
 | 
						public void visit(ContinueStmt n, CFG graph) {
 | 
				
			||||||
		graph.connectContinue(n);
 | 
							graph.connectContinue(n); // prev --> continue --> loop condition
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void visit(ExpressionStmt es, CFG graph) {
 | 
						public void visit(ExpressionStmt es, CFG graph) {
 | 
				
			||||||
		graph.connectTo(es);
 | 
							graph.connectTo(es); // prev --> es --> next
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue