/* * Copyright 2013 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.javascript.rhino.Node; import java.util.LinkedList; import java.util.List; /** * This file contains the only tests that use the infrastructure in * CompilerTestCase to run multiple passes and do sanity checks. The other files * that use CompilerTestCase unit test a single pass. * * @author dimvar@google.com (Dimitris Vardoulakis) */ public final class MultiPassTest extends CompilerTestCase { private List<PassFactory> passes; public MultiPassTest() { enableNormalize(); enableGatherExternProperties(); } @Override protected CompilerPass getProcessor(Compiler compiler) { PhaseOptimizer phaseopt = new PhaseOptimizer(compiler, null); phaseopt.consume(passes); phaseopt.setSanityCheck( new PassFactory("sanityCheck", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new SanityCheck(compiler); } }); compiler.setPhaseOptimizer(phaseopt); return phaseopt; } @Override protected CompilerOptions getOptions() { CompilerOptions options = super.getOptions(); options.setPrintSourceAfterEachPass(true); return options; } public void testInlineVarsAndPeephole() { passes = new LinkedList<>(); addInlineVariables(); addPeephole(); test("function f() { var x = 1; return x + 5; }", "function f() { return 6; }"); } public void testInlineFunctionsAndPeephole() { passes = new LinkedList<>(); addInlineFunctions(); addPeephole(); test("function f() { return 1; }" + "function g() { return f(); }" + "function h() { return g(); } var n = h();", "var n = 1"); } public void testInlineVarsAndDeadCodeElim() { passes = new LinkedList<>(); addDeadCodeElimination(); addInlineVariables(); test("function f() { var x = 1; return x; x = 3; }", "function f() { return 1; }"); } public void testCollapseObjectLiteralsScopeChange() { passes = new LinkedList<>(); addCollapseObjectLiterals(); test("function f() {" + " var obj = { x: 1 };" + " var z = function() { return obj.x; }" + "}", "function f(){" + " var JSCompiler_object_inline_x_0 = 1;" + " var z = function(){" + " return JSCompiler_object_inline_x_0;" + " }" + "}"); } public void testRemoveUnusedClassPropertiesScopeChange() { passes = new LinkedList<>(); addRemoveUnusedClassProperties(); test("/** @constructor */" + "function Foo() { this.a = 1; }" + "Foo.baz = function() {};", "/** @constructor */" + "function Foo() { 1; }" + "Foo.baz = function() {};"); } public void testRemoveUnusedVariablesScopeChange() { passes = new LinkedList<>(); addRemoveUnusedVars(); test("function f() { var x; }", "function f() {}"); test("function g() { function f(x, y) { return 1; } }", "function g() {}"); test("function f() { var x = 123; }", "function f() {}"); } public void testTopScopeChange() { passes = new LinkedList<>(); addInlineVariables(); addPeephole(); test("var x = 1, y = x, z = x + y;", "var z = 2;"); } public void testTwoOptimLoopsNoCrash() { passes = new LinkedList<>(); addInlineVariables(); addSmartNamePass(); addInlineVariables(); test("var x = '';", ""); } private void addCollapseObjectLiterals() { passes.add( new PassFactory("collapseObjectLiterals", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineObjectLiterals( compiler, compiler.getUniqueNameIdSupplier()); } }); } private void addDeadCodeElimination() { passes.add( new PassFactory("removeUnreachableCode", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new UnreachableCodeElimination(compiler, true); } }); } private void addInlineFunctions() { passes.add( new PassFactory("inlineFunctions", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineFunctions( compiler, compiler.getUniqueNameIdSupplier(), true, true, true, true, true, CompilerOptions.UNLIMITED_FUN_SIZE_AFTER_INLINING); } }); } private void addInlineVariables() { passes.add( new PassFactory("inlineVariables", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineVariables( compiler, InlineVariables.Mode.ALL, true); } }); } private void addPeephole() { passes.add( new PassFactory("peepholeOptimizations", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { final boolean late = false; return new PeepholeOptimizationsPass(compiler, new PeepholeMinimizeConditions(late, false /* useTypes */), new PeepholeSubstituteAlternateSyntax(late), new PeepholeReplaceKnownMethods(late, false), new PeepholeRemoveDeadCode(), new PeepholeFoldConstants(late, false), new PeepholeCollectPropertyAssignments()); } }); } private void addRemoveUnusedClassProperties() { passes.add( new PassFactory("removeUnusedClassProperties", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RemoveUnusedClassProperties(compiler, false); } }); } private void addRemoveUnusedVars() { passes.add( new PassFactory("removeUnusedVars", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RemoveUnusedVars(compiler, false, false, false); } }); } private void addSmartNamePass() { passes.add( new PassFactory("smartNamePass", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { NameAnalyzer na = new NameAnalyzer(compiler, false, null); na.process(externs, root); na.removeUnreferenced(); } }; } }); } }