Package com.google.javascript.jscomp

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

/*
* Copyright 2010 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.collect.Sets;
import com.google.javascript.jscomp.NodeTraversal.ScopedCallback;
import com.google.javascript.jscomp.Scope.Var;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

import java.util.Iterator;
import java.util.Set;

/**
* Groups multiple variable declarations (that may or may not be contiguous)
* in the same scope into a single one. i.e.
*
* <pre>
* var a, b = 10;
* f1();
* var c, d;
* ... some other code ...
* var x, y, z = 100;
* ... some other code ...
* var p = 200, q = 300;
* </pre>
*
* becomes:
*
* <pre>
* var a, b = 10, c, d, x, y, z;
* f1();
* ... some other code ...
* z = 100;
* ... some other code ...
* var p = 200, q = 300;
* </pre>
*
* This reduces the generated uncompressed code size.
*
* For any scope, we use the first VAR statement as the statement to collapse
* the other declarations into. For other VAR statements in the scope, we only
* consider ones that either (a) have no variable that is initialized in the
* the statement, or (b) have exactly one variable that is initialized (e.g.
* the "var x, y, z = 100;" statement in the example above. VAR statements
* with more than one variable initialization are not collapsed. This is
* because doing so would increase uncompressed code size.
*
*/
class GroupVariableDeclarations implements CompilerPass, ScopedCallback {
  private final AbstractCompiler compiler;

  GroupVariableDeclarations(AbstractCompiler compiler) {
    this.compiler = compiler;
  }

  @Override
  public void process(Node externs, Node root) {
    NodeTraversal.traverse(compiler, root, this);
  }

  @Override
  public void enterScope(NodeTraversal t) {
    Set<Node> varNodes = Sets.newLinkedHashSet();
    Iterator<Var> scopeVarIter = t.getScope().getVars();
    while (scopeVarIter.hasNext()) {
      Node parentNode = scopeVarIter.next().getParentNode();
      if (parentNode.getType() == Token.VAR) {
        varNodes.add(parentNode);
      }
    }
    if (varNodes.size() <= 1) {
      return;
    }
    Iterator<Node> varNodeIter = varNodes.iterator();
    Node firstVarNode = varNodeIter.next();
    while (varNodeIter.hasNext()) {
      Node varNode = varNodeIter.next();
      applyGroupingToVar(firstVarNode, varNode);
    }
  }

  @Override
  public void exitScope(NodeTraversal t) {
  }

  @Override
  public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
                                Node parent) {
    return true;
  }

  @Override
  public void visit(NodeTraversal t, Node n, Node parent) {
  }

  /**
   * Attempts to collapse groupVar. This can only happen if groupVar has at most
   * one variable initialization (it may have multiple variable declarations).
   * If successful, then detaches groupVar's children and appends them to
   * firstVar
   *
   * @param firstVar The first VAR {@code Node} in that scope. This is the node
   *                 that we want to collapse groupVar into
   * @param groupVar The VAR {@code Node} that we want to try collapsing
   *                 into the first VAR node of that scope
   */
  private void applyGroupingToVar(Node firstVar, Node groupVar) {
    Node child = groupVar.getFirstChild();
    // if some variable is initialized, then the corresponding NAME node will be
    // stored here
    Node initializedName = null;
    while (child != null) {
      if (child.hasChildren()) {
        // check that no more than one var is initialized
        if (initializedName != null) {
          return;
        }
        initializedName = child;
      }
      child = child.getNext();
    }

    // we will be modifying the groupVar subtree so get its parent
    Node groupVarParent = groupVar.getParent();


    if (initializedName != null) {
      if (NodeUtil.isForIn(groupVarParent)) {
        // The target of the for-in expression must be an assignable expression.
        return;
      }

      // we have an initialized var in the VAR node. We will replace the
      // VAR node with an assignment.

      // first create a detached childless clone of initializedName.
      Node clone = initializedName.cloneNode();
      // replace
      groupVar.replaceChild(initializedName, clone);
      // add the assignment now.
      Node initializedVal = initializedName.getFirstChild();
      initializedName.removeChild(initializedVal);
      Node assignmentNode = new Node(Token.ASSIGN, initializedName);
      assignmentNode.addChildAfter(initializedVal, initializedName);
      if (groupVarParent.getType() == Token.FOR) {
        // Handle For and For-In Loops specially. For these, we do not need
        // to construct an EXPR_RESULT node.
        groupVarParent.replaceChild(groupVar, assignmentNode);
      } else {
        Node exprNode = NodeUtil.newExpr(assignmentNode);
        groupVarParent.replaceChild(groupVar, exprNode);
      }
    } else {
      // There is no initialized var. But we need to handle FOR and
      // FOR-IN loops specially
      if (groupVarParent.getType() == Token.FOR) {
        if (NodeUtil.isForIn(groupVarParent)) {
          // In For-In loop, we replace the VAR node with a NAME node
          Node nameNodeClone = groupVar.getFirstChild().cloneNode();
          groupVarParent.replaceChild(groupVar, nameNodeClone);
        } else {
          // In For loop, we replace the VAR node with an EMPTY node
          Node emptyNode = new Node(Token.EMPTY);
          groupVarParent.replaceChild(groupVar, emptyNode);
        }
      } else {
        // we can safely remove the VAR node
        groupVarParent.removeChild(groupVar);
      }
    }

    Node children = groupVar.removeChildren();
    firstVar.addChildrenToBack(children);

    compiler.reportCodeChange();
  }
}
TOP

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

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.