com.google.javascript.jscomp.GroupVariableDeclarations.java Source code

Java tutorial

Introduction

Here is the source code for com.google.javascript.jscomp.GroupVariableDeclarations.java

Source

/*
 * 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;
    }

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

    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);
        }
    }

    public void exitScope(NodeTraversal t) {
    }

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

    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) {
            // 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();
    }
}