private Node tryReplaceExitWithBreak(Node n) {
    Node result = n.getFirstChild();

    // Find the enclosing control structure, if any, that a "break" would exit
    // from.
    Node breakTarget = n;
    for (; !ControlFlowAnalysis.isBreakTarget(breakTarget, null /* no label */);
        breakTarget = breakTarget.getParent()) {
      if (breakTarget.isFunction() || breakTarget.isScript()) {
        // No break target.
        return n;
      }
    }

    Node follow = ControlFlowAnalysis.computeFollowNode(breakTarget);

    // Skip pass all the finally blocks because both the break and return will
    // also trigger all the finally blocks. However, the order of execution is
    // slightly changed. Consider:
    //
    // return a() -> finally { b() } -> return a()
    //
    // which would call a() first. However, changing the first return to a
    // break will result in calling b().

    Node prefinallyFollows = follow;
    follow = skipFinallyNodes(follow);

    if (prefinallyFollows != follow) {
      // There were finally clauses
      if (!isPure(result)) {
        // Can't defer the exit
        return n;
      }
    }

    if (follow == null && (n.isThrow() || result != null)) {
      // Can't complete remove a throw here or a return with a result.
      return n;
    }

    // When follow is null, this mean the follow of a break target is the
    // end of a function. This means a break is same as return.
    if (follow == null || areMatchingExits(n, follow)) {
      Node replacement = IR.breakNode();
      n.replaceWith(replacement);
      reportChangeToEnclosingScope(replacement);
      return replacement;
    }

    return n;
  }
private Node tryReplaceExitWithBreak ( Node n ) { Node result = n . getFirstChild ( ) ; Node breakTarget = n ; for ( ; ! ControlFlowAnalysis . isBreakTarget ( breakTarget , null ) ; breakTarget = breakTarget . getParent ( ) ) { if ( breakTarget . isFunction ( ) || breakTarget . isScript ( ) ) { return n ; } } Node follow = ControlFlowAnalysis . computeFollowNode ( breakTarget ) ; Node prefinallyFollows = follow ; follow = skipFinallyNodes ( follow ) ; if ( prefinallyFollows != follow ) { if ( ! isPure ( result ) ) { return n ; } } if ( follow == null && ( n . isThrow ( ) || result != null ) ) { return n ; } if ( follow == null || areMatchingExits ( n , follow ) ) { Node replacement = IR . breakNode ( ) ; n . replaceWith ( replacement ) ; reportChangeToEnclosingScope ( replacement ) ; return replacement ; } return n ; }
Replace duplicate exits in control structures.  If the node following
the exit node expression has the same effect as exit node, the node can
be replaced or removed.
For example:
"while (a) {return f()} return f();" ==> "while (a) {break} return f();"
"while (a) {throw 'ow'} throw 'ow';" ==> "while (a) {break} throw 'ow';"

@param n An follow control exit expression (a THROW or RETURN node)
@return The replacement for n, or the original if no change was made.