Package com.google.javascript.jscomp

Source Code of com.google.javascript.jscomp.StatementFusion

/*
* Copyright 2011 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.javascript.jscomp;

import com.google.common.base.Preconditions;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

/**
* Tries to fuse all the statements in a block into a one statement by using
* COMMAs.
*
* Because COMMAs has the lowest precedence, we never need to insert
* extra () around. Once we have only one statement in a block, we can then
* eliminate a pair of {}'s. Further more, we can also fold a single
* statement IF into && or create further opportunities for all the other
* goodies in {@link PeepholeSubstituteAlternateSyntax}.
*
*/
public class StatementFusion extends AbstractPeepholeOptimization {

  @Override
  Node optimizeSubtree(Node n) {
    // The block of a function body always need { }.
    if (!NodeUtil.isFunction(n.getParent()) && canFuseIntoOneStatement(n)) {
      fuseIntoOneStatement(n);
      reportCodeChange();
    }
    return n;
  }

  private static boolean canFuseIntoOneStatement(Node block) {
    // Fold only statement block. NOT scripts block.
    if (block.getType() != Token.BLOCK) {
      return false;
    }

    // Nothing to do here.
    if (!block.hasChildren() || block.hasOneChild()) {
      return false;
    }

    Node last = block.getLastChild();

    for (Node c = block.getFirstChild(); c != null; c = c.getNext()) {
      if (!NodeUtil.isExpressionNode(c) && c != last) {
        return false;
      }
    }

    // TODO(user): Support more control statement for fusion.
    // FOR
    switch(last.getType()) {
      case Token.IF:
      case Token.THROW:
      case Token.SWITCH:
      case Token.EXPR_RESULT:
        return true;
      case Token.RETURN:
        // We don't want to add a new return value.
        return last.hasChildren();
      case Token.FOR:
        return NodeUtil.isForIn(last) &&
            // Avoid cases where we have for(var x = foo() in a) { ....
            !NodeUtil.mayHaveSideEffects(last.getFirstChild());
    }

    return false;
  }

  private void fuseIntoOneStatement(Node block) {
    Node cur = block.removeFirstChild();

    // Starts building a tree.
    Node commaTree = cur.removeFirstChild();


    while (block.hasMoreThanOneChild()) {
      Node next = block.removeFirstChild().removeFirstChild();
      commaTree = fuseExpressionIntoExpression(commaTree, next);
    }

    Preconditions.checkState(block.hasOneChild());
    Node last = block.getLastChild();

    // Now we are just left with two statements. The comma tree of the first
    // n - 1 statements (which can be used in an expression) and the last
    // statement. We perform specific fusion based on the last statement's type.
    switch(last.getType()) {
      case Token.IF:
      case Token.RETURN:
      case Token.THROW:
      case Token.SWITCH:
      case Token.EXPR_RESULT:
        fuseExpresssonIntoFirstChild(commaTree, last);
        return;
      case Token.FOR:
        if (NodeUtil.isForIn(last)) {
          fuseExpresssonIntoSecondChild(commaTree, last);
        }
        return ;
      default:
        throw new IllegalStateException("Statement fusion missing.");
    }
  }

  // exp1, exp1
  private static Node fuseExpressionIntoExpression(Node exp1, Node exp2) {
    Node comma = new Node(Token.COMMA, exp1);
    comma.copyInformationFrom(exp2);

    // We can just join the new comma expression with another comma but
    // lets keep all the comma's in a straight line. That way we can use
    // tree comparison.
    if (exp2.getType() == Token.COMMA) {
      Node leftMostChild = exp2;
      while(leftMostChild.getType() == Token.COMMA) {
        leftMostChild = leftMostChild.getFirstChild();
      }
      Node parent = leftMostChild.getParent();
      comma.addChildToBack(leftMostChild.detachFromParent());
      parent.addChildToFront(comma);
      return exp2;
    } else {
      comma.addChildToBack(exp2);
      return comma;
    }
  }

  private static void fuseExpresssonIntoFirstChild(Node exp, Node stmt) {
    Node val = stmt.removeFirstChild();
    Node comma = fuseExpressionIntoExpression(exp, val);
    stmt.addChildToFront(comma);
  }

  private static void fuseExpresssonIntoSecondChild(Node exp, Node stmt) {
    Node val = stmt.removeChildAfter(stmt.getFirstChild());
    Node comma = fuseExpressionIntoExpression(exp, val);
    stmt.addChildAfter(comma, stmt.getFirstChild());
  }
}
TOP

Related Classes of com.google.javascript.jscomp.StatementFusion

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.