Java tutorial
/* * 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(); } }