Package ai.cfg

Source Code of ai.cfg.MethodCFGBuilder$ProcessingContext

package ai.cfg;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AssertStatement;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BreakStatement;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.ContinueStatement;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EmptyStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.LabeledStatement;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.SynchronizedStatement;
import org.eclipse.jdt.core.dom.ThrowStatement;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;

import ai.common.Pair;

public class MethodCFGBuilder extends ASTVisitor {
  public static class MethodCFGBuilderException extends RuntimeException {
    private static final long serialVersionUID = 8706002880560036534L;

    public MethodCFGBuilderException(String format, Object... args) {
      super(String.format(format, args));
    }
  }
  class ProcessingContext {
    private final CFGVertice previousVertice;
    private final CFGVertice nextVertice;
    private Pair<CFGVertice, BlockContext> returnDetails;
    public ProcessingContext(CFGVertice previous, CFGVertice next, Pair<CFGVertice, BlockContext> returnDetails){
      previousVertice = previous;
      nextVertice = next;
      this.returnDetails = returnDetails;
    }
    public ProcessingContext setPrevious(CFGVertice newPrevious) {
      return new ProcessingContext(newPrevious, nextVertice, returnDetails);
    }
    public ProcessingContext setNext(CFGVertice newNextVertice) {
      return new ProcessingContext(previousVertice, newNextVertice, returnDetails);
    }
  }
 
  class BreakContext {
    private final String label;
    private final CFGVertice after;
    private final BlockContext blockContext;
    public BreakContext(SimpleName label, CFGVertice after, BlockContext blockContext) {
      this.label = (label == null) ? null : label.getIdentifier();
      this.after = after;
      this.blockContext = blockContext;
    }
  }
 
  class ContinueContext {
    private final CFGVertice loopIteration;
    private final String label;
    private final BlockContext blockContext;
    public ContinueContext(CFGVertice loopIteration, ASTNode loopNode, BlockContext blockContext){
      this.loopIteration = loopIteration;
      if (loopNode.getParent() instanceof LabeledStatement) {
        LabeledStatement labeled = (LabeledStatement) loopNode.getParent();
        this.label = labeled.getLabel().getIdentifier();
      } else {
        this.label = null;
      }
      this.blockContext = blockContext;
    }
  }
 
  class BlockContext {
    private final List<SimpleName> variables;
    private final ASTNode node;
    public BlockContext(ASTNode blockNode){
      this.variables = new LinkedList<SimpleName>();
      this.node = blockNode;
    }
   
    public void add(SimpleName name) {
      this.variables.add(name);
    }
  }
 
  private SimpleName getParentLabel(ASTNode node) {
    if (node.getParent().getNodeType() == ASTNode.LABELED_STATEMENT)
      return ((LabeledStatement)node.getParent()).getLabel();
    else
      return null;
  }
 
  private final InterestingCodeFragment codeFragment;
  private final MethodControlFlowGraph graph;
  private ProcessingContext context;
  private Stack<ContinueContext> loops = new Stack<ContinueContext>();
  private Stack<BreakContext> breakContextStack = new Stack<BreakContext>();
  private Stack<BlockContext> blockStack = new Stack<BlockContext>();

  private MethodCFGBuilder(InterestingCodeFragment codeFragment) {
    this.codeFragment = codeFragment;
    this.graph = new MethodControlFlowGraph(codeFragment);
    blockStack.add(new BlockContext(codeFragment.getNode()));
    this.context = new ProcessingContext(graph.getStartVertice(), graph.getEndVertice(),
        Pair.create(graph.getEndVertice(), blockStack.peek()));
   
  }

  private void acceptChild(ASTNode node, ProcessingContext context) { // mimic ASTNode.acceptChild
    if (node == null){
      return;
    };
    ProcessingContext storedContext = this.context;
    this.context = context;
    try {
      node.accept(this);
    } finally {
      this.context = storedContext;
    }
  }
 
  private interface ChildProcessor {
    void processChild(ASTNode node, ProcessingContext context);
  }

  private void processChildren(List<?> nodes, ProcessingContext context, ChildProcessor processor) {
    if (nodes.size() == 0) {
      graph.createEmptyEdge(context.previousVertice, context.nextVertice);
    } else {
      Object lastItem = nodes.get(nodes.size()-1);
      CFGVertice before = context.previousVertice;
      for(Object obj: nodes){
        ASTNode child = (ASTNode) obj;
        CFGVertice after;
        if (obj==lastItem) {
          after = context.nextVertice;
        } else {
          after = graph.addNewVertice(child);
        }
        before.addDescription(" block before:"+child.toString().split("\n")[0]);
        processor.processChild(child, new ProcessingContext(before, after, context.returnDetails));
        before = after;
      }
    }
  }
 
  private void acceptChildren(List<?> nodes, ProcessingContext context, final boolean newVariablesAsAssignments,
      final BlockContext bc) {
    processChildren(nodes, context, new ChildProcessor() {
      @Override
      public void processChild(ASTNode node, ProcessingContext context) {
        graph.addMapping(node, context.previousVertice, context.nextVertice);
        if (newVariablesAsAssignments && node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT){
          VariableDeclarationStatement vds = (VariableDeclarationStatement) node;
          processVariableDeclarationFragments(vds.fragments(), context, true, bc);
        } else
          acceptChild(node, context);
      }
    });
  }

  public boolean visit(MethodDeclaration node) {
    // don't go inside other methods
    return node == this.codeFragment.getNode();
  }

  public boolean visit(Initializer node) {
    // don't go inside other methods
    return node == this.codeFragment.getNode();
  }
 
//possible statements are:
//   *    {@link TypeDeclarationStatement},

  private static final Set<Integer> HANDLED_NOTE_TYPES = new HashSet<Integer>(Arrays.asList(
      ASTNode.METHOD_DECLARATION,
      //various statements
      ASTNode.ASSERT_STATEMENT,
      ASTNode.BLOCK,
      ASTNode.BREAK_STATEMENT,
      ASTNode.CONSTRUCTOR_INVOCATION,
      ASTNode.CONTINUE_STATEMENT,
      ASTNode.DO_STATEMENT,
      ASTNode.EMPTY_STATEMENT,
      ASTNode.ENHANCED_FOR_STATEMENT,
      ASTNode.EXPRESSION_STATEMENT,
      ASTNode.FOR_STATEMENT,
      ASTNode.IF_STATEMENT,
      ASTNode.LABELED_STATEMENT,
      ASTNode.RETURN_STATEMENT,
      ASTNode.SUPER_CONSTRUCTOR_INVOCATION,
      ASTNode.SWITCH_STATEMENT,
      ASTNode.SWITCH_CASE,
      ASTNode.SYNCHRONIZED_STATEMENT,
      ASTNode.THROW_STATEMENT,
      ASTNode.TRY_STATEMENT,
      ASTNode.TYPE_DECLARATION_STATEMENT,
      ASTNode.VARIABLE_DECLARATION_STATEMENT,
      ASTNode.WHILE_STATEMENT
      ));
 
  @Override
  public boolean preVisit2(ASTNode node) {
    if (!HANDLED_NOTE_TYPES.contains(node.getNodeType()))
      throw new MethodCFGBuilderException("Unhandled node: '%s'", node.getClass());
    return true;
  }
 
  @Override
  public boolean visit(ExpressionStatement node) {
    graph.createExpressionStatementEdge(context.previousVertice, context.nextVertice, node.getExpression());
    return false;
  }
 
  @Override
  public boolean visit(EmptyStatement node) {
    graph.createEmptyEdge(this.context.previousVertice, this.context.nextVertice);
    return false;
  }
 
  @Override
  public boolean visit(LabeledStatement node){
    BreakContext bc = new BreakContext(node.getLabel(), context.nextVertice, blockStack.peek());
    breakContextStack.push(bc);
    acceptChild(node.getBody(), context);
    if (breakContextStack.pop() != bc)
      throw new MethodCFGBuilderException("Different break context!!");
    return false;
  }
 
  private List<SimpleName> getAllVariables(BlockContext bc) {
    List<SimpleName> result = new LinkedList<SimpleName>();
    for(int i = blockStack.size()-1; i >=0 ; i--) {
      BlockContext stackContext = blockStack.get(i);
      if (stackContext == bc)
        return result;
      //FIXME: order of variables??
      result.addAll(stackContext.variables);
    }
    throw new MethodCFGBuilderException("Cannot find variables!!");
  }
 
  private Pair<CFGVertice, List<SimpleName>> findBreakTarget(SimpleName labelNode) {
    if (labelNode == null) {
      if (breakContextStack.size() == 0)
        throw new MethodCFGBuilderException("No break context found!!");
      BreakContext bc = breakContextStack.peek();
      return Pair.create(bc.after, getAllVariables(bc.blockContext));
    }
    String label = labelNode.getIdentifier();
    for(int i=breakContextStack.size() - 1; i >= 0; i--) {
      BreakContext bc = breakContextStack.get(i);
      if (label.equals(bc.label))
        return Pair.create(bc.after, getAllVariables(bc.blockContext));
    }
    throw new MethodCFGBuilderException("Unable to find break target");
  }
 
  private Pair<CFGVertice, List<SimpleName>> findLoopContext(SimpleName labelNode) {
    if (labelNode == null) {
      if (loops.size() == 0)
        throw new MethodCFGBuilderException("No loop context found!!");
      ContinueContext cc = loops.peek();
      return Pair.create(cc.loopIteration, getAllVariables(cc.blockContext));
    }
    String label = labelNode.getIdentifier();
    for (int i=loops.size()-1; i>=0; i--) {
      ContinueContext possibleResult = loops.get(i);
      if (label.equals(possibleResult.label))
        return Pair.create(possibleResult.loopIteration, getAllVariables(possibleResult.blockContext));
    }
    throw new MethodCFGBuilderException("Unhandled case");
  }
 
  @Override
  public boolean visit(BreakStatement node) {
    //create edge outside the loop
    Pair<CFGVertice, List<SimpleName>> target = findBreakTarget(node.getLabel());
    graph.createEmptyEdge(context.previousVertice, target.left, target.right);
    return false;
  }
 
  @Override
  public boolean visit(ContinueStatement node) {
    Pair<CFGVertice, List<SimpleName>> loop = findLoopContext(node.getLabel());
    graph.createEmptyEdge(context.previousVertice, loop.left, loop.right);
    return false;
  }
 
  private int whileCounter = 0;

  @Override
  public boolean visit(WhileStatement node) {
    ProcessingContext context = this.context;
    int id = whileCounter++;
    this.context.previousVertice.addDescription("LoopWhile_"+id);
   
    CFGVertice beforeLoopBody = graph.addNewVertice(node.getBody(), "WhileBody_"+id);
   
    ContinueContext loopContext = new ContinueContext(context.previousVertice, node, blockStack.peek());
    loops.push(loopContext);
    BreakContext bc = null;
    if (getParentLabel(node) == null) {
      bc = new BreakContext(null, context.nextVertice, blockStack.peek());
      breakContextStack.push(bc);
    }
   
    acceptChild(node.getBody(), new ProcessingContext(beforeLoopBody, context.previousVertice, context.returnDetails));
    if (loops.pop() != loopContext)
      throw new MethodCFGBuilderException("Different loop context!!");
    if (bc != null && breakContextStack.pop() != bc)
      throw new MethodCFGBuilderException("Different break context!!");
    graph.addLoopInvariangMapping(node, context.previousVertice);
    graph.createConditionalEdge(context.previousVertice, beforeLoopBody, context.nextVertice, node.getExpression());
    return false;
  }
 
  private int forCounter = 0;

  private void processVariableDeclarationFragments(List<?> fragments, ProcessingContext context,
      final boolean newVariablesAsAssignemnts, final BlockContext blockContext) {
    processChildren(fragments, context, new ChildProcessor() {
      @Override
      public void processChild(ASTNode fragment, ProcessingContext context) {
        VariableDeclarationFragment vdf = (VariableDeclarationFragment) fragment;
        if (!newVariablesAsAssignemnts)
          blockContext.add(vdf.getName());
        graph.createVariableDeclarationEdge(context.previousVertice, context.nextVertice,
            vdf.getName(), vdf.getInitializer(), newVariablesAsAssignemnts);
      }
    });
  }
 
  private void processForExpressions(List<?> initializers, ProcessingContext context, final BlockContext bc) {
    processChildren(initializers, context, new ChildProcessor() {
      @Override
      public void processChild(ASTNode node, ProcessingContext context) {
        Expression initializer = (Expression) node;
        if (initializer.getNodeType() == ASTNode.VARIABLE_DECLARATION_EXPRESSION)
          processVariableDeclarationFragments(((VariableDeclarationExpression)initializer).fragments(), context, false,
              bc);
        else
          graph.createExpressionStatementEdge(context.previousVertice, context.nextVertice, initializer);
      }
    });
  }
 
  @Override
  public boolean visit(ForStatement node) {
    ProcessingContext context = this.context;
    int idx = forCounter++;
    this.context.previousVertice.addDescription("ForInitialisers_"+idx);
    //initialisers
    BlockContext currentBlock = blockStack.peek();
    BlockContext forBlockContext = pushNewBlockContext(node);
    CFGVertice loopVertice = graph.addNewVertice(node, "LoopFor_" + idx);
    graph.addLoopInvariangMapping(node, loopVertice);
    processForExpressions(node.initializers(), new ProcessingContext(context.previousVertice, loopVertice, context.returnDetails), forBlockContext);
   
    //loop body
    CFGVertice beforeLoopBody = graph.addNewVertice(node.getBody(), "ForBody_" + idx);
    CFGVertice afterLoopBody = graph.addNewVertice(null, "ForEnd_" + idx);
   
    ContinueContext loopContext = new ContinueContext(afterLoopBody, node, forBlockContext);
    loops.push(loopContext);
    BreakContext bc = null;
    if (getParentLabel(node) == null) {
      bc = new BreakContext(null, context.nextVertice, currentBlock);
      breakContextStack.push(bc);
    }
   
    acceptChild(node.getBody(), new ProcessingContext(beforeLoopBody, afterLoopBody, context.returnDetails));
    if (loops.pop() != loopContext)
      throw new MethodCFGBuilderException("Different loop context!!");
    if (bc != null && breakContextStack.pop() != bc)
      throw new MethodCFGBuilderException("Different break context!!");
    //updaters
    processForExpressions(node.updaters(), new ProcessingContext(afterLoopBody, loopVertice, context.returnDetails), forBlockContext);
   
    if (forBlockContext != blockStack.pop())
      throw new MethodCFGBuilderException("Different block context!!");
   
    CFGVertice nextVertice = context.nextVertice;
    if (forBlockContext.variables.size() > 0) {
      CFGVertice removeInitializers = graph.addNewVertice(node, "Remove initializers");
      graph.createEmptyEdge(removeInitializers, nextVertice, forBlockContext.variables);
      nextVertice = removeInitializers;
    }
    graph.createConditionalEdge(loopVertice, beforeLoopBody, nextVertice, node.getExpression());
    return false;
  }
 
  @Override
  public boolean visit(VariableDeclarationStatement node) {
    processVariableDeclarationFragments(node.fragments(), context, false, blockStack.peek());
    return false;
  }
 
  int blockCounter = 0;
  private void processBlock(Block node, ProcessingContext context, BlockContext bc) {
    CFGVertice removeVars = graph.addNewVertice(null, "Remove block variables:"+blockCounter++);
    acceptChildren(node.statements(), context.setNext(removeVars), false, bc);
    graph.createEmptyEdge(removeVars, context.nextVertice, bc.variables);
  }

 
  @Override
  public boolean visit(Block node) {
    graph.addMapping(node, context.previousVertice, context.nextVertice);
    BlockContext bc = pushNewBlockContext(node);
    processBlock(node, this.context, bc);
    if (blockStack.pop() != bc)
      throw new MethodCFGBuilderException("Different block context!!");
    return false;
  }
 
  private int ifCounter = 0;
 
  private BlockContext pushNewBlockContext(ASTNode node) {
    BlockContext bc = new BlockContext(node);
    blockStack.push(bc);
    return bc;
  }
 
  @Override
  public boolean visit(IfStatement node) {
    ProcessingContext context = this.context;
    int id = ifCounter++;
    this.context.previousVertice.addDescription("If_"+id);
   
    //then
    ASTNode thenNode = node.getThenStatement();
    assert thenNode != null;
    CFGVertice positiveStart;
    positiveStart = graph.addNewVertice(thenNode, "Then_" + id);
    acceptChild(thenNode, context.setPrevious(positiveStart));

    //else
    ASTNode elseNode = node.getElseStatement();
    CFGVertice negativeStart = null;
    if (elseNode != null) {
      negativeStart = graph.addNewVertice(elseNode, "Else_" + id);
      acceptChild(elseNode, context.setPrevious(negativeStart));
    } else
      negativeStart = context.nextVertice;
    graph.createConditionalEdge(context.previousVertice, positiveStart, negativeStart, node.getExpression());
    return false;
  }
 
  @Override
  public boolean visit(ReturnStatement node) {
    List<SimpleName> removeVars = getAllVariables(context.returnDetails.right);
    graph.createEmptyEdge(context.previousVertice, context.returnDetails.left, removeVars);
    return false;
  }
 
  @Override
  public boolean visit(TryStatement node) {
    BlockContext bc = blockStack.peek();
    if (node.resources() != null && node.resources().size() > 0)
      throw new MethodCFGBuilderException("Not handled case,  non empt resources: '%s'", node.resources());
    ProcessingContext context = this.context;
    //first handle finally and prepare context for the rest
    if (node.getFinally() != null) {
      CFGVertice finallyStart = graph.addNewVertice(node.getFinally(), "Finally START");
      graph.createExceptionEdge(context.previousVertice, finallyStart, null);
      acceptChild(node.getFinally(), context.setPrevious(finallyStart));
      context = new ProcessingContext(context.previousVertice, finallyStart, Pair.create(finallyStart, bc));
    }
    //exceptions next
    if (node.catchClauses() != null && node.catchClauses().size() > 0) {
      for(Object catchClauseObj: node.catchClauses()) {
        CatchClause catchClause = (CatchClause) catchClauseObj;
        CFGVertice catchStart = graph.addNewVertice(catchClause, "Catch " + catchClause.getException());
        graph.createExceptionEdge(context.previousVertice, catchStart, catchClause.getException());
        BlockContext cbc = pushNewBlockContext(node);
        SimpleName excVariable = catchClause.getException().getName();
        if (excVariable!=null)
          cbc.add(excVariable);
        processBlock(catchClause.getBody(), context.setPrevious(catchStart), cbc);
        if (blockStack.pop() != cbc)
          throw new MethodCFGBuilderException("Different block context!!");
      }
    }
    acceptChild(node.getBody(), context);
    return false;
  }
 
  @Override
  public boolean visit(ConstructorInvocation node){
    graph.createConstructorInvocationEdge(context.previousVertice, context.nextVertice, node);
    return false;
  }

  @Override
  public boolean visit(DoStatement node){
    ProcessingContext context = this.context;
    BlockContext blockContext = blockStack.peek();
    int id = whileCounter++;
    this.context.previousVertice.addDescription("LoopDoWhile_"+id);
   
    CFGVertice loopCondition = graph.addNewVertice(node, "ConditionDoWhile_"+id);
    ContinueContext loopContext = new ContinueContext(context.previousVertice, node, blockContext);
    loops.push(loopContext);
    BreakContext bc = null;
    if (getParentLabel(node) == null) {
      bc = new BreakContext(null, context.nextVertice, blockContext);
      breakContextStack.push(bc);
    }

    acceptChild(node.getBody(), context.setNext(loopCondition));
    if (loops.pop() != loopContext)
      throw new MethodCFGBuilderException("Different loop context!!");
    if (bc != null && breakContextStack.pop() != bc)
      throw new MethodCFGBuilderException("Different break context!!!");
    graph.addLoopInvariangMapping(node, loopCondition);
    graph.createConditionalEdge(loopCondition, context.previousVertice, context.nextVertice, node.getExpression());
    return false;
  }

  @Override
  public boolean visit(SuperConstructorInvocation node){
    graph.createSuperConstructorInvocationEdge(context.previousVertice, context.nextVertice, node);
    return false;
  }
 
  private List<SimpleName> findBlockVariables(List<?> statements) {
    List<SimpleName> result = new LinkedList<SimpleName>();
    for(Object obj: statements) {
      ASTNode node = (ASTNode) obj;
      if (node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT) {
        VariableDeclarationStatement vds = (VariableDeclarationStatement) node;
        for(Object fragmentObj: vds.fragments())
          result.add(((VariableDeclarationFragment)fragmentObj).getName());
      }
    }
    return result;
  }
 
  private void addNewVariableDefinitions(List<SimpleName> variables, ProcessingContext context, final BlockContext bc) {
    processChildren(variables, context, new ChildProcessor() {
      @Override
      public void processChild(ASTNode node, ProcessingContext context) {
        SimpleName name = (SimpleName) node;
        graph.createVariableDeclarationEdge(context.previousVertice, context.nextVertice, name, null, false);
        bc.add(name);
      }
    });
  }
 
  @Override
  public boolean visit(SwitchStatement node) {
    ProcessingContext context = this.context;
    context.previousVertice.addDescription("Switch");
    BlockContext currentBlock = blockStack.peek();
    //switch is a block
    BlockContext switchBlock = pushNewBlockContext(node);

    BreakContext bc = null;
    if (getParentLabel(node) == null) {
      bc = new BreakContext(null, context.nextVertice, currentBlock);
      breakContextStack.push(bc);
    }
    CFGVertice previousVertice = context.previousVertice;
   
    CFGVertice defaultTarget = null;
    List<Statement> lastBlock = null;
    List<Pair<CFGVertice, Expression>> cases = new LinkedList<Pair<CFGVertice,Expression>>();
    List<SimpleName> blockVariables = findBlockVariables(node.statements());
    if (blockVariables.size() > 0) {
      CFGVertice afterVariables = graph.addNewVertice(null, "After switch variables");
      addNewVariableDefinitions(blockVariables, context.setNext(afterVariables), switchBlock);
      CFGVertice cleanVariables = graph.addNewVertice(null, "Remove switch block variables");
      graph.createEmptyEdge(cleanVariables, context.nextVertice, switchBlock.variables);
      context = new ProcessingContext(afterVariables, cleanVariables, context.returnDetails);
    }
   
    for(Object obj: node.statements()) {
      Statement stmt = (Statement) obj;
      if (stmt.getNodeType() == ASTNode.SWITCH_CASE) {
        CFGVertice caseVertice = graph.addNewVertice(stmt, "Switch case: " + stmt);
        SwitchCase aCase = (SwitchCase) stmt;
        if (aCase.isDefault()) {
          if (defaultTarget !=null)
            throw new MethodCFGBuilderException("Two defaults??");
          defaultTarget = caseVertice;
        } else
          cases.add(Pair.create(caseVertice, aCase.getExpression()));
        if (lastBlock !=null)
          acceptChildren(lastBlock, new ProcessingContext(previousVertice, caseVertice, context.returnDetails), true, switchBlock);
        lastBlock = new LinkedList<Statement>();
        previousVertice = caseVertice;
      } else {
        lastBlock.add(stmt);
      }
    }
    if (lastBlock !=null)
      acceptChildren(lastBlock, context.setPrevious(previousVertice), true, switchBlock);
    if (bc != null && breakContextStack.pop() != bc)
      throw new MethodCFGBuilderException("Different loop context!!");
    //finally we create the case edge
    if (defaultTarget == null) defaultTarget = context.nextVertice;
    graph.createSwitchEdge(context.previousVertice, defaultTarget, node.getExpression(), cases);
    if (switchBlock != blockStack.pop())
      throw new MethodCFGBuilderException("Different block context!!");   
    return false;
  }
 
  @Override
  public boolean visit(AssertStatement node) {
    graph.createEmptyEdge(context.previousVertice, context.nextVertice);
    return false; //NOTE: we assume assertions are not executed!!
  }

  @Override
  public boolean visit(SynchronizedStatement node) {
    ProcessingContext context = this.context;
    context.previousVertice.addDescription("Synchronized expression");
    CFGVertice blockStart = graph.addNewVertice(node, "Synchronized block");
    graph.createExpressionStatementEdge(context.previousVertice, blockStart, node.getExpression());
    acceptChild(node.getBody(), context.setPrevious(blockStart));
    return false;
  }

  @Override
  public boolean visit(TypeDeclarationStatement node) {
    graph.createEmptyEdge(context.previousVertice, context.nextVertice);
    return false;
  }

  @Override
  public boolean visit(ThrowStatement node) {
    //FIXME: we do not handle exceptions at all
    //current implementation looses the path -does not add connection (previous -> next)
    //since we handle exceptions in finallyOrCatch edges
    return false;
  }
 
  @Override
  public boolean visit(EnhancedForStatement node) {
    ProcessingContext context = this.context;
    int idx = forCounter++;
    context.previousVertice.addDescription("BeforeEnhancedFor_"+idx);
    BlockContext currentBlock = blockStack.peek();
    //initializer expression
    CFGVertice loopVertice = graph.addNewVertice(node, "LoopEnhancedFor_"+idx);
    graph.createExpressionStatementEdge(context.previousVertice, loopVertice, node.getExpression());   
    CFGVertice loopVariable = graph.addNewVertice(node, "EnhancedForVariable_"+idx);
    //dummy conditions
    graph.addLoopInvariangMapping(node, loopVertice);
    graph.createConditionalEdge(loopVertice, loopVariable, context.nextVertice, null); // no need to remove
   
    BlockContext forBlockContext = pushNewBlockContext(node);
    forBlockContext.add(node.getParameter().getName());
    CFGVertice removeInitializers = graph.addNewVertice(node, "Remove initializers");
    graph.createEmptyEdge(removeInitializers, loopVertice, forBlockContext.variables);
    //dummy conditions
    CFGVertice beforeLoopBody = graph.addNewVertice(node, "EnhancedForBody_"+idx);   
    graph.createVariableDeclarationEdge(loopVariable, beforeLoopBody, node.getParameter().getName(), null, false);
   
    ContinueContext loopContext = new ContinueContext(removeInitializers, node, forBlockContext);
    loops.push(loopContext);
    BreakContext bc = null;
    if (getParentLabel(node) == null) {
      bc = new BreakContext(null, context.nextVertice, currentBlock);
      breakContextStack.push(bc);
    }

    acceptChild(node.getBody(), new ProcessingContext(beforeLoopBody, removeInitializers, context.returnDetails));
    if (loops.pop() != loopContext)
      throw new MethodCFGBuilderException("Different loop context!!");
    if (bc != null && breakContextStack.pop() != bc)
      throw new MethodCFGBuilderException("Different break context!!!");
    if (forBlockContext != blockStack.pop())
      throw new MethodCFGBuilderException("Different block context!!!");
    return false;
  }

  /**
   * If method doesn't have body (abstract?) null is returned
   *
   * @param method
   * @return
   */
  public static MethodControlFlowGraph buildCFG(InterestingCodeFragment cf) {
    //It may be an abstract method or interface method??
    if (cf.getBody() == null)
      return null;
    MethodCFGBuilder builder = new MethodCFGBuilder(cf);
    cf.getBody().accept(builder);
    return builder.graph;
  }
}
TOP

Related Classes of ai.cfg.MethodCFGBuilder$ProcessingContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.