private void handleContinue(Node node) {
    String label = null;
    if (node.hasChildren()) {
      label = node.getFirstChild().getString();
    }
    Node cur;
    Node lastJump;
    // Similar to handBreak's logic with a few minor variation.
    Node parent = node.getParent();
    for (cur = node, lastJump = node;
        !isContinueTarget(cur, parent, label);
        cur = parent, parent = parent.getParent()) {
      if (cur.getType() == Token.TRY && NodeUtil.hasFinally(cur)) {
        if (lastJump == node) {
          createEdge(lastJump, Branch.UNCOND, cur.getLastChild());
        } else {
          finallyMap.put(lastJump, computeFallThrough(cur.getLastChild()));
        }
        lastJump = cur;
      }
      Preconditions.checkState(parent != null, "Cannot find continue target.");
    }
    Node iter = cur;
    if (cur.getChildCount() == 4) {
      iter = cur.getFirstChild().getNext().getNext();
    }

    if (lastJump == node) {
      createEdge(node, Branch.UNCOND, iter);
    } else {
      finallyMap.put(lastJump, iter);
    }
  }
  private void handleReturn(Node node) {
    Node lastJump = null;
    for (Iterator<Node> iter = exceptionHandler.iterator(); iter.hasNext(); ) {
      Node curHandler = iter.next();
      if (NodeUtil.isFunction(curHandler)) {
        break;
      }
      if (NodeUtil.hasFinally(curHandler)) {
        if (lastJump == null) {
          createEdge(node, Branch.UNCOND, curHandler.getLastChild());
        } else {
          finallyMap.put(lastJump, computeFallThrough(curHandler.getLastChild()));
        }
        lastJump = curHandler;
      }
    }

    if (node.hasChildren()) {
      connectToPossibleExceptionHandler(node, node.getFirstChild());
    }

    if (lastJump == null) {
      createEdge(node, Branch.UNCOND, null);
    } else {
      finallyMap.put(lastJump, null);
    }
  }
 private void handleBreak(Node node) {
   String label = null;
   // See if it is a break with label.
   if (node.hasChildren()) {
     label = node.getFirstChild().getString();
   }
   Node cur;
   Node lastJump;
   Node parent = node.getParent();
   /*
    * Continuously look up the ancestor tree for the BREAK target or the target
    * with the corresponding label and connect to it. If along the path we
    * discover a FINALLY, we will connect the BREAK to that FINALLY. From then
    * on, we will just record the control flow changes in the finallyMap. This
    * is due to the fact that we need to connect any node that leaves its own
    * FINALLY block to the outer FINALLY or the BREAK's target but those nodes
    * are not known yet due to the way we traverse the nodes.
    */
   for (cur = node, lastJump = node;
       !isBreakTarget(cur, parent, label);
       cur = parent, parent = parent.getParent()) {
     if (cur.getType() == Token.TRY && NodeUtil.hasFinally(cur)) {
       if (lastJump == node) {
         createEdge(lastJump, Branch.UNCOND, computeFallThrough(cur.getLastChild()));
       } else {
         finallyMap.put(lastJump, computeFallThrough(cur.getLastChild()));
       }
       lastJump = cur;
     }
     Preconditions.checkState(parent != null, "Cannot find break target.");
   }
   if (lastJump == node) {
     createEdge(lastJump, Branch.UNCOND, computeFollowNode(cur));
   } else {
     finallyMap.put(lastJump, computeFollowNode(cur));
   }
 }
  /**
   * Computes the follow() node of a given node and its parent. There is a side effect when calling
   * this function. If this function computed an edge that exists a FINALLY, it'll attempt to
   * connect the fromNode to the outer FINALLY according to the finallyMap.
   *
   * @param fromNode The original source node since {@code node} is changed during recursion.
   * @param node The node that follow() should compute.
   */
  private Node computeFollowNode(Node fromNode, Node node) {
    /*
     * This is the case where:
     *
     * 1. Parent is null implies that we are transferring control to the end of
     * the script.
     *
     * 2. Parent is a function implies that we are transferring control back to
     * the caller of the function.
     *
     * 3. If the node is a return statement, we should also transfer control
     * back to the caller of the function.
     *
     * 4. If the node is root then we have reached the end of what we have been
     * asked to traverse.
     *
     * In all cases we should transfer control to a "symbolic return" node.
     * This will make life easier for DFAs.
     */
    Node parent = node.getParent();
    if (parent == null || parent.getType() == Token.FUNCTION || node == root) {
      return null;
    }

    // If we are just before a IF/WHILE/DO/FOR:
    switch (parent.getType()) {
        // The follow() of any of the path from IF would be what follows IF.
      case Token.IF:
        return computeFollowNode(fromNode, parent);
      case Token.CASE:
      case Token.DEFAULT:
        // After the body of a CASE, the control goes to the body of the next
        // case, without having to go to the case condition.
        if (parent.getNext() != null) {
          if (parent.getNext().getType() == Token.CASE) {
            return parent.getNext().getFirstChild().getNext();
          } else if (parent.getNext().getType() == Token.DEFAULT) {
            return parent.getNext().getFirstChild();
          } else {
            Preconditions.checkState(false, "Not reachable");
          }
        } else {
          return computeFollowNode(fromNode, parent);
        }
        break;
      case Token.FOR:
        if (NodeUtil.isForIn(parent)) {
          return parent;
        } else {
          return parent.getFirstChild().getNext().getNext();
        }
      case Token.WHILE:
      case Token.DO:
        return parent;
      case Token.TRY:
        // If we are coming out of the TRY block...
        if (parent.getFirstChild() == node) {
          if (NodeUtil.hasFinally(parent)) { // and have FINALLY block.
            return computeFallThrough(parent.getLastChild());
          } else { // and have no FINALLY.
            return computeFollowNode(fromNode, parent);
          }
          // CATCH block.
        } else if (NodeUtil.getCatchBlock(parent) == node) {
          if (NodeUtil.hasFinally(parent)) { // and have FINALLY block.
            return computeFallThrough(node.getNext());
          } else {
            return computeFollowNode(fromNode, parent);
          }
          // If we are coming out of the FINALLY block...
        } else if (parent.getLastChild() == node) {
          for (Node finallyNode : finallyMap.get(parent)) {
            createEdge(fromNode, Branch.UNCOND, finallyNode);
          }
          return computeFollowNode(fromNode, parent);
        }
    }

    // Now that we are done with the special cases follow should be its
    // immediate sibling, unless its sibling is a function
    Node nextSibling = node.getNext();

    // Skip function declarations because control doesn't get pass into it.
    while (nextSibling != null && nextSibling.getType() == Token.FUNCTION) {
      nextSibling = nextSibling.getNext();
    }

    if (nextSibling != null) {
      return computeFallThrough(nextSibling);
    } else {
      // If there are no more siblings, control is transfered up the AST.
      return computeFollowNode(fromNode, parent);
    }
  }