/* * Copyright 2009 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; /** * Tests for {@link OptimizeParameters} * */ public class OptimizeParametersTest extends CompilerTestCase { @Override public CompilerPass getProcessor(Compiler compiler) { return new OptimizeParameters(compiler); } @Override public void setUp() { super.enableNormalize(); super.enableLineNumberCheck(false); } public void testNoRemoval() { testSame("function foo(p1) { } foo(1); foo(2)"); testSame("function foo(p1) { } foo(1,2); foo(3,4)"); } public void testSimpleRemoval() { test("function foo(p1) { } foo(); foo()", "function foo() {var p1;} foo(); foo()"); test("function foo(p1) { } foo(1); foo(1)", "function foo() {var p1 = 1;} foo(); foo()"); test("function foo(p1) { } foo(1,2); foo(1,4)", "function foo() {var p1 = 1;} foo(2); foo(4)"); } public void testNotAFunction() { testSame("var x = 1; x; x = 2"); } public void testRemoveOneOptionalNamedFunction() { test("function foo(p1) { } foo()", "function foo() {var p1} foo()"); } public void testDifferentScopes() { test("function f(a, b) {} f(1, 2); f(1, 3); " + "function h() {function g(a) {} g(4); g(5);} f(1, 2);", "function f(b) {var a = 1} f(2); f(3); " + "function h() {function g(a) {} g(4); g(5);} f(2);"); } public void testOptimizeOnlyImmutableValues() { test("function foo(a) {}; foo(undefined);", "function foo() {var a = undefined}; foo()"); test("function foo(a) {}; foo(null);", "function foo() {var a = null}; foo()"); test("function foo(a) {}; foo(1);", "function foo() {var a = 1}; foo()"); test("function foo(a) {}; foo('abc');", "function foo() {var a = 'abc'}; foo()"); test("var foo = function(a) {}; foo(undefined);", "var foo = function() {var a = undefined}; foo()"); test("var foo = function(a) {}; foo(null);", "var foo = function() {var a = null}; foo()"); test("var foo = function(a) {}; foo(1);", "var foo = function() {var a = 1}; foo()"); test("var foo = function(a) {}; foo('abc');", "var foo = function() {var a = 'abc'}; foo()"); } public void testRemoveOneOptionalVarAssignment() { test("var foo = function (p1) { }; foo()", "var foo = function () {var p1}; foo()"); } public void testDoOptimizeCall() { testSame("var foo = function () {}; foo(); foo.call();"); // TODO(johnlenz): support foo.call testSame("var foo = function () {}; foo(); foo.call(this);"); testSame("var foo = function (a, b) {}; foo(1); foo.call(this, 1);"); testSame("var foo = function () {}; foo(); foo.call(null);"); testSame("var foo = function (a, b) {}; foo(1); foo.call(null, 1);"); testSame("var foo = function () {}; foo.call();"); // TODO(johnlenz): support foo.call testSame("var foo = function () {}; foo.call(this);"); testSame("var foo = function (a, b) {}; foo.call(this, 1);"); testSame("var foo = function () {}; foo.call(null);"); testSame("var foo = function (a, b) {}; foo.call(null, 1);"); } public void testDoOptimizeApply() { testSame("var foo = function () {}; foo(); foo.apply();"); testSame("var foo = function () {}; foo(); foo.apply(this);"); testSame("var foo = function (a, b) {}; foo(1); foo.apply(this, 1);"); testSame("var foo = function () {}; foo(); foo.apply(null);"); testSame("var foo = function (a, b) {}; foo(1); foo.apply(null, []);"); testSame("var foo = function () {}; foo.apply();"); testSame("var foo = function () {}; foo.apply(this);"); testSame("var foo = function (a, b) {}; foo.apply(this, 1);"); testSame("var foo = function () {}; foo.apply(null);"); testSame("var foo = function (a, b) {}; foo.apply(null, []);"); } public void testRemoveOneOptionalExpressionAssign() { // TODO(johnlenz): There are two definitions of "foo" here, ignore the // one that can't be called. testSame("var foo; foo = function (p1) { }; foo()"); } public void testRemoveOneOptionalOneRequired() { test("function foo(p1, p2) { } foo(1); foo(2)", "function foo(p1) {var p2} foo(1); foo(2)"); } public void testRemoveOneOptionalMultipleCalls() { test( "function foo(p1, p2) { } foo(1); foo(2); foo()", "function foo(p1) {var p2} foo(1); foo(2); foo()"); } public void testRemoveOneOptionalMultiplePossibleDefinition() { String src = "var goog = {};" + "goog.foo = function (p1, p2) { };" + "goog.foo = function (q1, q2) { };" + "goog.foo = function (r1, r2) { };" + "goog.foo(1); goog.foo(2); goog.foo()"; String expected = "var goog = {};" + "goog.foo = function (p1) { var p2 };" + "goog.foo = function (q1) { var q2 };" + "goog.foo = function (r1) { var r2 };" + "goog.foo(1); goog.foo(2); goog.foo()"; // TODO(johnlenz): Support multiple valid definitions. testSame(src); } public void testRemoveTwoOptionalMultiplePossibleDefinition() { String src = "var goog = {};" + "goog.foo = function (p1, p2, p3, p4) { };" + "goog.foo = function (q1, q2, q3, q4) { };" + "goog.foo = function (r1, r2, r3, r4) { };" + "goog.foo(1,0); goog.foo(2,1); goog.foo()"; String expected = "var goog = {};" + "goog.foo = function(p1, p2) { var p4; var p3};" + "goog.foo = function(q1, q2) { var q4; var q3};" + "goog.foo = function(r1, r2) { var r4; var r3};" + "goog.foo(1,0); goog.foo(2,1); goog.foo()"; // TODO(johnlenz): Support multiple valid definitions. testSame(src); } public void testConstructorOptArgsNotRemoved() { String src = "/** @constructor */" + "var goog = function(){};" + "goog.prototype.foo = function(a,b) {};" + "goog.prototype.bar = function(a) {};" + "goog.bar.inherits(goog.foo);" + "new goog.foo(2,3);" + "new goog.foo(1,2);"; testSame(src); } public void testMultipleUnknown() { String src = "var goog1 = {};" + "goog1.foo = function () { };" + "var goog2 = {};" + "goog2.foo = function (p1) { };" + "var x = getGoog();" + "x.foo()"; String expected = "var goog1 = {};" + "goog1.foo = function () { };" + "var goog2 = {};" + "goog2.foo = function () { var p1 };" + "var x = getGoog();" + "x.foo()"; // TODO(johnlenz): Support multiple definitions. testSame(src); } public void testSingleUnknown() { String src = "var goog2 = {};" + "goog2.foo = function (p1) { };" + "var x = getGoog();" + "x.foo()"; String expected = "var goog2 = {};" + "goog2.foo = function () { var p1 };" + "var x = getGoog();" + "x.foo()"; test(src, expected); } public void testRemoveVarArg() { test("function foo(p1, var_args) { } foo(1); foo(2)", "function foo(p1) { var var_args } foo(1); foo(2)"); } public void testAliasMethodsDontGetOptimize() { String src = "var foo = function(a, b) {};" + "var goog = {};" + "goog.foo = foo;" + "goog.prototype.bar = goog.foo;" + "new goog().bar(1,2);" + "foo(2);"; testSame(src); } public void testAliasMethodsDontGetOptimize2() { String src = "var foo = function(a, b) {};" + "var bar = foo;" + "foo(1);" + "bar(2,3);"; testSame(src); } public void testAliasMethodsDontGetOptimize3() { String src = "var array = {};" + "array[0] = function(a, b) {};" + "var foo = array[0];" + // foo should be marked as aliased. "foo(1);"; testSame(src); } public void testAliasMethodsDontGetOptimize4() { // Don't change the call to baz as it has been aliased. test( "function foo(bar) {};" + "baz = function(a) {};" + "baz(1);" + "foo(baz);", "function foo() {var bar = baz};" + "baz = function(a) {};" + "baz(1);" + "foo();"); } public void testMethodsDefinedInArraysDontGetOptimized() { String src = "var array = [true, function (a) {}];" + "array[1](1)"; testSame(src); } public void testMethodsDefinedInObjectDontGetOptimized() { String src = "var object = { foo: function bar() {} };" + "object.foo(1)"; testSame(src); src = "var object = { foo: function bar() {} };" + "object['foo'](1)"; testSame(src); } public void testRemoveConstantArgument() { // Remove only one parameter test("function foo(p1, p2) {}; foo(1,2); foo(2,2);", "function foo(p1) {var p2 = 2}; foo(1); foo(2)"); // Remove nothing testSame("function foo(p1, p2) {}; foo(1); foo(2,3);"); // Remove middle parameter test("function foo(a,b,c){}; foo(1, 2, 3); foo(1, 2, 4); foo(2, 2, 3)", "function foo(a,c){var b=2}; foo(1, 3); foo(1, 4); foo(2, 3)"); // Number are equals test("function foo(a) {}; foo(1); foo(1.0);", "function foo() {var a = 1;}; foo(); foo();"); // A more OO test String src = "/** @constructor */" + "function Person(){}; Person.prototype.run = function(a, b) {};" + "Person.run(1, 'a'); Person.run(2, 'a')"; String expected = "function Person(){}; Person.prototype.run = " + "function(a) {var b = 'a'};" + "Person.run(1); Person.run(2)"; test(src, expected); } public void testCanDeleteArgumentsAtAnyPosition() { // Argument removed in middle and end String src = "function foo(a,b,c,d,e) {};" + "foo(1,2,3,4,5);" + "foo(2,2,4,4,5);"; String expected = "function foo(a,c) {var b=2; var d=4; var e=5;};" + "foo(1,3);" + "foo(2,4);"; test(src, expected); } public void testNoOptimizationForExternsFunctions() { testSame("function _foo(x, y, z){}; _foo(1);"); } public void testNoOptimizationForGoogExportSymbol() { testSame("goog.exportSymbol('foo', foo);" + "function foo(x, y, z){}; foo(1);"); } public void testNoArgumentRemovalNonEqualNodes() { testSame("function foo(a){}; foo('bar'); foo('baz');"); testSame("function foo(a){}; foo(1.0); foo(2.0);"); testSame("function foo(a){}; foo(true); foo(false);"); testSame("var a = 1, b = 2; function foo(a){}; foo(a); foo(b);"); testSame("function foo(a){}; foo(/&/g); foo(/