All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.StatementFusion Maven / Gradle / Ivy

Go to download

Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs.

There is a newer version: v20240317
Show newest version
/*
 * 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 static com.google.common.base.Preconditions.checkArgument;

import com.google.javascript.rhino.Node;

/**
 * Tries to fuse all the statements in a block into a one statement by using COMMAs or statements.
 *
 * 

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 PeepholeMinimizeConditions}. * *

NOTE(user): The current compiler assumes that there are more ;'s than ,'s in a real * program, and so it makes sense to prefer fusing statements with semicolons rather than commas. * This assumption has never been validated on a real program. */ final class StatementFusion extends AbstractPeepholeOptimization { @Override Node optimizeSubtree(Node n) { if (n.getParent().isFunction() || !canFuseIntoOneStatement(n)) { return n; } Node start = n.getFirstChild(); Node end = n.getLastChild(); Node result = fuseIntoOneStatement(start, end); fuseExpressionIntoControlFlowStatement(result, n.getLastChild()); reportChangeToEnclosingScope(n); return n; } private boolean canFuseIntoOneStatement(Node block) { if (!block.isBlock()) { 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 (!c.isExprResult() && c != last) { return false; } } return isFusableControlStatement(last); } private boolean isFusableControlStatement(Node n) { switch (n.getToken()) { case IF: case THROW: case SWITCH: case EXPR_RESULT: return true; case RETURN: // We don't want to add a new return value. return n.hasChildren(); case FOR: // Avoid cases where we have for(var x;_;_) { .... return !NodeUtil.isNameDeclaration(n.getFirstChild()); case FOR_IN: // Avoid cases where we have for(var x = foo() in a) { .... return !mayHaveSideEffects(n.getFirstChild()); case LABEL: return isFusableControlStatement(n.getLastChild()); case BLOCK: return (isASTNormalized() || NodeUtil.canMergeBlock(n)) && !n.isSyntheticBlock() && isFusableControlStatement(n.getFirstChild()); default: break; } return false; } /** * Given a block, fuse a list of statements with comma's. * * @param parent The parent that contains the statements. * @param first The first statement to fuse (inclusive) * @param last The last statement to fuse (exclusive) * @return A single statement that contains all the fused statement as one. */ private static Node fuseIntoOneStatement(Node first, Node last) { // Nothing to fuse if there is only one statement. if (first.getNext() == last) { return first; } // Step one: Create a comma tree that contains all the statements. Node commaTree = first.removeFirstChild(); Node next = null; for (Node cur = first.getNext(); cur != last; cur = next) { commaTree = AstManipulations.fuseExpressions(commaTree, cur.removeFirstChild()); next = cur.getNext(); cur.detach(); } // Step two: The last EXPR_RESULT will now hold the comma tree with all // the fused statements. first.addChildToBack(commaTree); return first; } private static void fuseExpressionIntoControlFlowStatement( Node before, Node control) { checkArgument(before.isExprResult(), "before must be expression result"); // 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 (control.getToken()) { case IF: case RETURN: case THROW: case SWITCH: case EXPR_RESULT: case FOR: before.detach(); fuseExpressionIntoFirstChild(before.removeFirstChild(), control); return; case FOR_IN: before.detach(); fuseExpressionIntoSecondChild(before.removeFirstChild(), control); return; case LABEL: fuseExpressionIntoControlFlowStatement(before, control.getLastChild()); return; case BLOCK: fuseExpressionIntoControlFlowStatement(before, control.getFirstChild()); return; default: throw new IllegalStateException("Statement fusion missing."); } } private static void fuseExpressionIntoFirstChild(Node exp, Node stmt) { Node val = stmt.removeFirstChild(); Node comma = AstManipulations.fuseExpressions(exp, val); stmt.addChildToFront(comma); } private static void fuseExpressionIntoSecondChild(Node exp, Node stmt) { Node val = stmt.getSecondChild().detach(); Node comma = AstManipulations.fuseExpressions(exp, val); comma.insertAfter(stmt.getFirstChild()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy