Documentation and comments
This commit is contained in:
parent
5ca4da62dc
commit
9aad602df3
3 changed files with 195 additions and 44 deletions
|
@ -11,6 +11,10 @@ import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
|
|||
import java.util.LinkedList;
|
||||
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> {
|
||||
@Override
|
||||
public void visit(MethodDeclaration methodDeclaration, CFG graph) {
|
||||
|
@ -19,6 +23,10 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
|||
graph.finishMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return statements are connected to the previous statement and to the end node of the graph.
|
||||
* Schema: {@code -> return -> Stop}
|
||||
*/
|
||||
@Override
|
||||
public void visit(ReturnStmt n, CFG arg) {
|
||||
arg.connectTo(n);
|
||||
|
@ -26,24 +34,29 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
|||
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
|
||||
public void visit(IfStmt n, CFG graph) {
|
||||
// Connect to the condition
|
||||
// prev --> cond
|
||||
Node ifStart = n.getCondition();
|
||||
graph.beginBlock(n, ifStart);
|
||||
graph.connectTo(ifStart);
|
||||
// Connect condition to then block
|
||||
// cond --> then (if true)
|
||||
graph.setNextLabel(true);
|
||||
n.getThenStmt().accept(this, graph);
|
||||
// Save end of then block
|
||||
// then --> next (saved for later)
|
||||
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.setNextLabel(false);
|
||||
if (n.getElseStmt().isPresent()) {
|
||||
// cond --> else (if false)
|
||||
n.getElseStmt().get().accept(this, graph);
|
||||
// else --> next
|
||||
newPrev.addAll(graph.getNodeList());
|
||||
} else {
|
||||
// cond --> next (if false)
|
||||
newPrev.add(ifStart);
|
||||
}
|
||||
// Set ends of then/else as the new list
|
||||
|
@ -51,6 +64,7 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
|||
graph.endBlock();
|
||||
}
|
||||
|
||||
/** Registers the label used for the loop statement in the break and continue sections of the CFG */
|
||||
@Override
|
||||
public void visit(LabeledStmt n, CFG graph) {
|
||||
graph.beginBreakSection(n.getLabel());
|
||||
|
@ -60,130 +74,165 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
|||
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
|
||||
public void visit(WhileStmt n, CFG graph) {
|
||||
Node whileStart = n.getCondition();
|
||||
// Begin block/sections
|
||||
graph.beginBlock(n, whileStart);
|
||||
graph.beginBreakSection();
|
||||
graph.beginContinueSection(whileStart);
|
||||
// prev --> cond
|
||||
graph.connectTo(whileStart);
|
||||
// cond --> body (if true)
|
||||
graph.setNextLabel(true);
|
||||
n.getBody().accept(this, graph);
|
||||
// body --> cond
|
||||
graph.connectTo(whileStart);
|
||||
// cond --> next (if false)
|
||||
graph.setNextLabel(false);
|
||||
// End block/sections
|
||||
graph.endContinueSection();
|
||||
graph.endBreakSection();
|
||||
graph.endBlock();
|
||||
}
|
||||
|
||||
/** Do while statement, set up as block. Generates break and continue section. Schema:
|
||||
* {@code -> body -> cond -> body -> cond ->} */
|
||||
@Override
|
||||
public void visit(DoStmt n, CFG graph) {
|
||||
Node condition = n.getCondition();
|
||||
// Begin block/section
|
||||
graph.beginBlock(n, condition);
|
||||
graph.beginBreakSection();
|
||||
graph.beginContinueSection(condition);
|
||||
// prev --> body
|
||||
// cond --> body (if true)
|
||||
graph.appendNode(condition);
|
||||
n.getBody().accept(this, graph);
|
||||
// body --> cond
|
||||
graph.connectTo(condition);
|
||||
// cond --> next (if false)
|
||||
graph.setNextLabel(false);
|
||||
// End block/section
|
||||
graph.endContinueSection();
|
||||
graph.endBreakSection();
|
||||
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
|
||||
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.beginBreakSection();
|
||||
// Initialization expressions
|
||||
if (n.getInitialization() != null) {
|
||||
for (Expression e : n.getInitialization()) {
|
||||
graph.connectTo(e);
|
||||
}
|
||||
}
|
||||
// Comparison expression
|
||||
// TODO: shortcut conditions (||, &&)
|
||||
// prev --> initializer(s), chained in serial
|
||||
for (Expression e : n.getInitialization())
|
||||
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.
|
||||
assert n.getTokenRange().isPresent();
|
||||
if (!n.getCompare().isPresent())
|
||||
n.setCompare(new BooleanLiteralExpr(n.getTokenRange().get(), true));
|
||||
// Begin continue section (with the comparison on hand)
|
||||
graph.beginContinueSection(n.getCompare().get());
|
||||
// initializer(s) --> cond
|
||||
graph.connectTo(n.getCompare().get());
|
||||
// cond --> body (if true)
|
||||
graph.setNextLabel(true);
|
||||
// Loop body
|
||||
n.getBody().accept(this, graph);
|
||||
// Update expressions
|
||||
if (n.getUpdate() != null)
|
||||
for (Expression e : n.getUpdate())
|
||||
graph.connectTo(e);
|
||||
// Connect to comparison expression
|
||||
// Set comparison as last possible statement
|
||||
// body --> update(s), chained in serial
|
||||
for (Expression e : n.getUpdate())
|
||||
graph.connectTo(e);
|
||||
// update(s) --> comp
|
||||
graph.connectTo(n.getCompare().get());
|
||||
// comp --> next (if false)
|
||||
graph.setNextLabel(false);
|
||||
// End block/section (all of them)
|
||||
graph.endContinueSection();
|
||||
graph.endBreakSection();
|
||||
graph.endBlock();
|
||||
}
|
||||
|
||||
/** Foreach statement, set up as a block. Generates break and continue sections. Schema: {@code -> cond -> body -> cond ->} */
|
||||
@Override
|
||||
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());
|
||||
// Begin block/section
|
||||
graph.beginBlock(n, copy);
|
||||
graph.beginBreakSection();
|
||||
graph.beginContinueSection(copy);
|
||||
// prev --> cond
|
||||
graph.connectTo(copy);
|
||||
// cond --> body (if true)
|
||||
graph.setNextLabel(true);
|
||||
n.getBody().accept(this, graph);
|
||||
// body --> cond
|
||||
graph.connectTo(copy);
|
||||
// cond --> next (if false)
|
||||
graph.setNextLabel(false);
|
||||
graph.endContinueSection();
|
||||
graph.endBreakSection();
|
||||
graph.endBlock();
|
||||
}
|
||||
|
||||
/** Switch entry, considered part of the condition of the switch. */
|
||||
@Override
|
||||
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<>());
|
||||
graph.connectTo(caseNode);
|
||||
// Case body
|
||||
// Case body (case EXPR --> body)
|
||||
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
|
||||
public void visit(SwitchStmt n, CFG graph) {
|
||||
// Link previous statement to the switch's selector
|
||||
Node selectorNode = n.getSelector();
|
||||
// Begin block and break section
|
||||
graph.beginBlock(n, selectorNode);
|
||||
graph.beginBreakSection();
|
||||
// prev --> expr
|
||||
graph.connectTo(selectorNode);
|
||||
// Analyze switch's cases
|
||||
// expr --> each case (fallthrough by default, so case --> case too)
|
||||
for (SwitchEntryStmt entry : n.getEntries()) {
|
||||
entry.accept(this, graph);
|
||||
graph.appendNode(selectorNode);
|
||||
entry.accept(this, graph); // expr && prev case --> case --> next case
|
||||
graph.appendNode(selectorNode); // expr --> next case
|
||||
}
|
||||
// The next statement will be linked to:
|
||||
// 1. All break statements that broke from the switch
|
||||
// 2. If the switch doesn't have a default statement, the switch's selector
|
||||
// 3. If the last entry doesn't break, to the last statement
|
||||
graph.removeNode(selectorNode);
|
||||
if (n.getEntries().get(n.getEntries().size() - 1).getLabel().isPresent())
|
||||
graph.appendNode(selectorNode);
|
||||
// 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 (already present)
|
||||
// 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);
|
||||
// End block and break section
|
||||
graph.endBreakSection();
|
||||
graph.endBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BreakStmt n, CFG graph) {
|
||||
graph.connectBreak(n);
|
||||
graph.connectBreak(n); // prev --> break --> end of loop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ContinueStmt n, CFG graph) {
|
||||
graph.connectContinue(n);
|
||||
graph.connectContinue(n); // prev --> continue --> loop condition
|
||||
}
|
||||
|
||||
@Override
|
||||
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