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

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

/*
 * 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(n, 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 parent, 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(); parent.removeChild(cur); } // 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); stmt.addChildAfter(comma, stmt.getFirstChild()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy