/vendor/closure-compiler/test/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntaxTest.java
Java | 1065 lines | 757 code | 188 blank | 120 comment | 3 complexity | c800852b0ab03752074a9091f2a4bdae MD5 | raw file
Possible License(s): LGPL-2.0, Apache-2.0, GPL-2.0
- /*
- * Copyright 2004 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 PeepholeSubstituteAlternateSyntax} in isolation.
- * Tests for the interaction of multiple peephole passes are in
- * PeepholeIntegrationTest.
- */
- public class PeepholeSubstituteAlternateSyntaxTest extends CompilerTestCase {
- // Externs for builtin constructors
- // Needed for testFoldLiteralObjectConstructors(),
- // testFoldLiteralArrayConstructors() and testFoldRegExp...()
- private static final String FOLD_CONSTANTS_TEST_EXTERNS =
- "var Object = function f(){};\n" +
- "var RegExp = function f(a){};\n" +
- "var Array = function f(a){};\n";
- private boolean late = true;
- // TODO(user): Remove this when we no longer need to do string comparison.
- private PeepholeSubstituteAlternateSyntaxTest(boolean compareAsTree) {
- super(FOLD_CONSTANTS_TEST_EXTERNS, compareAsTree);
- }
- public PeepholeSubstituteAlternateSyntaxTest() {
- super(FOLD_CONSTANTS_TEST_EXTERNS);
- }
- @Override
- public void setUp() throws Exception {
- late = true;
- super.setUp();
- enableLineNumberCheck(true);
- disableNormalize();
- }
- @Override
- public CompilerPass getProcessor(final Compiler compiler) {
- CompilerPass peepholePass =
- new PeepholeOptimizationsPass(compiler,
- new PeepholeSubstituteAlternateSyntax(late));
- return peepholePass;
- }
- @Override
- protected int getNumRepetitions() {
- // Reduce this to 2 if we get better expression evaluators.
- return 2;
- }
- private void foldSame(String js) {
- testSame(js);
- }
- private void fold(String js, String expected) {
- test(js, expected);
- }
- private void fold(String js, String expected, DiagnosticType warning) {
- test(js, expected, warning);
- }
- void assertResultString(String js, String expected) {
- assertResultString(js, expected, false);
- }
- // TODO(user): This is same as fold() except it uses string comparison. Any
- // test that needs tell us where a folding is constructing an invalid AST.
- void assertResultString(String js, String expected, boolean normalize) {
- PeepholeSubstituteAlternateSyntaxTest scTest
- = new PeepholeSubstituteAlternateSyntaxTest(false);
- if (normalize) {
- scTest.enableNormalize();
- } else {
- scTest.disableNormalize();
- }
- scTest.test(js, expected);
- }
- /** Check that removing blocks with 1 child works */
- public void testFoldOneChildBlocks() {
- late = false;
- fold("function f(){if(x)a();x=3}",
- "function f(){x&&a();x=3}");
- fold("function f(){if(x){a()}x=3}",
- "function f(){x&&a();x=3}");
- fold("function f(){if(x){return 3}}",
- "function f(){if(x)return 3}");
- fold("function f(){if(x){a()}}",
- "function f(){x&&a()}");
- fold("function f(){if(x){throw 1}}", "function f(){if(x)throw 1;}");
- // Try it out with functions
- fold("function f(){if(x){foo()}}", "function f(){x&&foo()}");
- fold("function f(){if(x){foo()}else{bar()}}",
- "function f(){x?foo():bar()}");
- // Try it out with properties and methods
- fold("function f(){if(x){a.b=1}}", "function f(){if(x)a.b=1}");
- fold("function f(){if(x){a.b*=1}}", "function f(){x&&(a.b*=1)}");
- fold("function f(){if(x){a.b+=1}}", "function f(){x&&(a.b+=1)}");
- fold("function f(){if(x){++a.b}}", "function f(){x&&++a.b}");
- fold("function f(){if(x){a.foo()}}", "function f(){x&&a.foo()}");
- // Try it out with throw/catch/finally [which should not change]
- fold("function f(){try{foo()}catch(e){bar(e)}finally{baz()}}",
- "function f(){try{foo()}catch(e){bar(e)}finally{baz()}}");
- // Try it out with switch statements
- fold("function f(){switch(x){case 1:break}}",
- "function f(){switch(x){case 1:break}}");
- // Do while loops stay in a block if that's where they started
- fold("function f(){if(e1){do foo();while(e2)}else foo2()}",
- "function f(){if(e1){do foo();while(e2)}else foo2()}");
- // Test an obscure case with do and while
- fold("if(x){do{foo()}while(y)}else bar()",
- "if(x){do foo();while(y)}else bar()");
- // Play with nested IFs
- fold("function f(){if(x){if(y)foo()}}",
- "function f(){x&&y&&foo()}");
- fold("function f(){if(x){if(y)foo();else bar()}}",
- "function f(){x&&(y?foo():bar())}");
- fold("function f(){if(x){if(y)foo()}else bar()}",
- "function f(){x?y&&foo():bar()}");
- fold("function f(){if(x){if(y)foo();else bar()}else{baz()}}",
- "function f(){x?y?foo():bar():baz()}");
- fold("if(e1){while(e2){if(e3){foo()}}}else{bar()}",
- "if(e1)while(e2)e3&&foo();else bar()");
- fold("if(e1){with(e2){if(e3){foo()}}}else{bar()}",
- "if(e1)with(e2)e3&&foo();else bar()");
- fold("if(a||b){if(c||d){var x;}}", "if(a||b)if(c||d)var x");
- fold("if(x){ if(y){var x;}else{var z;} }",
- "if(x)if(y)var x;else var z");
- // NOTE - technically we can remove the blocks since both the parent
- // and child have elses. But we don't since it causes ambiguities in
- // some cases where not all descendent ifs having elses
- fold("if(x){ if(y){var x;}else{var z;} }else{var w}",
- "if(x)if(y)var x;else var z;else var w");
- fold("if (x) {var x;}else { if (y) { var y;} }",
- "if(x)var x;else if(y)var y");
- // Here's some of the ambiguous cases
- fold("if(a){if(b){f1();f2();}else if(c){f3();}}else {if(d){f4();}}",
- "if(a)if(b){f1();f2()}else c&&f3();else d&&f4()");
- fold("function f(){foo()}", "function f(){foo()}");
- fold("switch(x){case y: foo()}", "switch(x){case y:foo()}");
- fold("try{foo()}catch(ex){bar()}finally{baz()}",
- "try{foo()}catch(ex){bar()}finally{baz()}");
- }
- /** Try to minimize returns */
- public void testFoldReturns() {
- fold("function f(){if(x)return 1;else return 2}",
- "function f(){return x?1:2}");
- fold("function f(){if(x)return 1;return 2}",
- "function f(){return x?1:2}");
- fold("function f(){if(x)return;return 2}",
- "function f(){return x?void 0:2}");
- fold("function f(){if(x)return 1+x;else return 2-x}",
- "function f(){return x?1+x:2-x}");
- fold("function f(){if(x)return 1+x;return 2-x}",
- "function f(){return x?1+x:2-x}");
- fold("function f(){if(x)return y += 1;else return y += 2}",
- "function f(){return x?(y+=1):(y+=2)}");
- fold("function f(){if(x)return;else return 2-x}",
- "function f(){if(x);else return 2-x}");
- fold("function f(){if(x)return;return 2-x}",
- "function f(){return x?void 0:2-x}");
- fold("function f(){if(x)return x;else return}",
- "function f(){if(x)return x;{}}");
- fold("function f(){if(x)return x;return}",
- "function f(){if(x)return x}");
- foldSame("function f(){for(var x in y) { return x.y; } return k}");
- }
- public void testCombineIfs1() {
- fold("function f() {if (x) return 1; if (y) return 1}",
- "function f() {if (x||y) return 1;}");
- fold("function f() {if (x) return 1; if (y) foo(); else return 1}",
- "function f() {if ((!x)&&y) foo(); else return 1;}");
- }
- public void testCombineIfs2() {
- // combinable but not yet done
- foldSame("function f() {if (x) throw 1; if (y) throw 1}");
- // Can't combine, side-effect
- fold("function f(){ if (x) g(); if (y) g() }",
- "function f(){ x&&g(); y&&g() }");
- // Can't combine, side-effect
- fold("function f(){ if (x) y = 0; if (y) y = 0; }",
- "function f(){ x&&(y = 0); y&&(y = 0); }");
- }
- public void testCombineIfs3() {
- foldSame("function f() {if (x) return 1; if (y) {g();f()}}");
- }
- /** Try to minimize assignments */
- public void testFoldAssignments() {
- fold("function f(){if(x)y=3;else y=4;}", "function f(){y=x?3:4}");
- fold("function f(){if(x)y=1+a;else y=2+a;}", "function f(){y=x?1+a:2+a}");
- // and operation assignments
- fold("function f(){if(x)y+=1;else y+=2;}", "function f(){y+=x?1:2}");
- fold("function f(){if(x)y-=1;else y-=2;}", "function f(){y-=x?1:2}");
- fold("function f(){if(x)y%=1;else y%=2;}", "function f(){y%=x?1:2}");
- fold("function f(){if(x)y|=1;else y|=2;}", "function f(){y|=x?1:2}");
- // sanity check, don't fold if the 2 ops don't match
- foldSame("function f(){x ? y-=1 : y+=2}");
- // sanity check, don't fold if the 2 LHS don't match
- foldSame("function f(){x ? y-=1 : z-=1}");
- // sanity check, don't fold if there are potential effects
- foldSame("function f(){x ? y().a=3 : y().a=4}");
- }
- public void testRemoveDuplicateStatements() {
- fold("if (a) { x = 1; x++ } else { x = 2; x++ }",
- "x=(a) ? 1 : 2; x++");
- fold("if (a) { x = 1; x++; y += 1; z = pi; }" +
- " else { x = 2; x++; y += 1; z = pi; }",
- "x=(a) ? 1 : 2; x++; y += 1; z = pi;");
- fold("function z() {" +
- "if (a) { foo(); return !0 } else { goo(); return !0 }" +
- "}",
- "function z() {(a) ? foo() : goo(); return !0}");
- fold("function z() {if (a) { foo(); x = true; return true " +
- "} else { goo(); x = true; return true }}",
- "function z() {(a) ? foo() : goo(); x = !0; return !0}");
- fold("function z() {" +
- " if (a) { bar(); foo(); return true }" +
- " else { bar(); goo(); return true }" +
- "}",
- "function z() {" +
- " if (a) { bar(); foo(); }" +
- " else { bar(); goo(); }" +
- " return !0;" +
- "}");
- }
- public void testNotCond() {
- fold("function f(){if(!x)foo()}", "function f(){x||foo()}");
- fold("function f(){if(!x)b=1}", "function f(){x||(b=1)}");
- fold("if(!x)z=1;else if(y)z=2", "x ? y&&(z=2) : z=1");
- foldSame("function f(){if(!(x=1))a.b=1}");
- }
- public void testAndParenthesesCount() {
- fold("function f(){if(x||y)a.foo()}", "function f(){(x||y)&&a.foo()}");
- fold("function f(){if(x.a)x.a=0}",
- "function f(){x.a&&(x.a=0)}");
- foldSame("function f(){if(x()||y()){x()||y()}}");
- }
- public void testFoldLogicalOpStringCompare() {
- // side-effects
- // There is two way to parse two &&'s and both are correct.
- assertResultString("if(foo() && false) z()", "foo()&&0&&z()");
- }
- public void testFoldNot() {
- fold("while(!(x==y)){a=b;}" , "while(x!=y){a=b;}");
- fold("while(!(x!=y)){a=b;}" , "while(x==y){a=b;}");
- fold("while(!(x===y)){a=b;}", "while(x!==y){a=b;}");
- fold("while(!(x!==y)){a=b;}", "while(x===y){a=b;}");
- // Because !(x<NaN) != x>=NaN don't fold < and > cases.
- foldSame("while(!(x>y)){a=b;}");
- foldSame("while(!(x>=y)){a=b;}");
- foldSame("while(!(x<y)){a=b;}");
- foldSame("while(!(x<=y)){a=b;}");
- foldSame("while(!(x<=NaN)){a=b;}");
- // NOT forces a boolean context
- fold("x = !(y() && true)", "x = !y()");
- // This will be further optimized by PeepholeFoldConstants.
- fold("x = !true", "x = !1");
- }
- public void testFoldRegExpConstructor() {
- enableNormalize();
- // Cannot fold all the way to a literal because there are too few arguments.
- fold("x = new RegExp", "x = RegExp()");
- // Empty regexp should not fold to // since that is a line comment in js
- fold("x = new RegExp(\"\")", "x = RegExp(\"\")");
- fold("x = new RegExp(\"\", \"i\")", "x = RegExp(\"\",\"i\")");
- // Bogus flags should not fold
- fold("x = new RegExp(\"foobar\", \"bogus\")",
- "x = RegExp(\"foobar\",\"bogus\")",
- PeepholeSubstituteAlternateSyntax.INVALID_REGULAR_EXPRESSION_FLAGS);
- // Can Fold
- fold("x = new RegExp(\"foobar\")", "x = /foobar/");
- fold("x = RegExp(\"foobar\")", "x = /foobar/");
- fold("x = new RegExp(\"foobar\", \"i\")", "x = /foobar/i");
- // Make sure that escaping works
- fold("x = new RegExp(\"\\\\.\", \"i\")", "x = /\\./i");
- fold("x = new RegExp(\"/\", \"\")", "x = /\\//");
- fold("x = new RegExp(\"[/]\", \"\")", "x = /[/]/");
- fold("x = new RegExp(\"///\", \"\")", "x = /\\/\\/\\//");
- fold("x = new RegExp(\"\\\\\\/\", \"\")", "x = /\\//");
- fold("x = new RegExp(\"\\n\")", "x = /\\n/");
- fold("x = new RegExp('\\\\\\r')", "x = /\\r/");
- // Don't fold really long regexp literals, because Opera 9.2's
- // regexp parser will explode.
- String longRegexp = "";
- for (int i = 0; i < 200; i++) longRegexp += "x";
- foldSame("x = RegExp(\"" + longRegexp + "\")");
- // Shouldn't fold RegExp unnormalized because
- // we can't be sure that RegExp hasn't been redefined
- disableNormalize();
- foldSame("x = new RegExp(\"foobar\")");
- }
- public void testVersionSpecificRegExpQuirks() {
- enableNormalize();
- // Don't fold if the flags contain 'g'
- enableEcmaScript5(false);
- fold("x = new RegExp(\"foobar\", \"g\")",
- "x = RegExp(\"foobar\",\"g\")");
- fold("x = new RegExp(\"foobar\", \"ig\")",
- "x = RegExp(\"foobar\",\"ig\")");
- // ... unless in EcmaScript 5 mode per section 7.8.5 of EcmaScript 5.
- enableEcmaScript5(true);
- fold("x = new RegExp(\"foobar\", \"ig\")",
- "x = /foobar/ig");
- // Don't fold things that crash older versions of Safari and that don't work
- // as regex literals on other old versions of Safari
- enableEcmaScript5(false);
- fold("x = new RegExp(\"\\u2028\")", "x = RegExp(\"\\u2028\")");
- fold("x = new RegExp(\"\\\\\\\\u2028\")", "x = /\\\\u2028/");
- // Sunset Safari exclusions for EcmaScript 5 and later.
- enableEcmaScript5(true);
- fold("x = new RegExp(\"\\u2028\\u2029\")", "x = /\\u2028\\u2029/");
- fold("x = new RegExp(\"\\\\u2028\")", "x = /\\u2028/");
- fold("x = new RegExp(\"\\\\\\\\u2028\")", "x = /\\\\u2028/");
- }
- public void testFoldRegExpConstructorStringCompare() {
- // Might have something to do with the internal representation of \n and how
- // it is used in node comparison.
- assertResultString("x=new RegExp(\"\\n\", \"i\")", "x=/\\n/i", true);
- }
- public void testContainsUnicodeEscape() throws Exception {
- assertTrue(!PeepholeSubstituteAlternateSyntax.containsUnicodeEscape(""));
- assertTrue(!PeepholeSubstituteAlternateSyntax.containsUnicodeEscape("foo"));
- assertTrue(PeepholeSubstituteAlternateSyntax.containsUnicodeEscape(
- "\u2028"));
- assertTrue(PeepholeSubstituteAlternateSyntax.containsUnicodeEscape(
- "\\u2028"));
- assertTrue(
- PeepholeSubstituteAlternateSyntax.containsUnicodeEscape("foo\\u2028"));
- assertTrue(!PeepholeSubstituteAlternateSyntax.containsUnicodeEscape(
- "foo\\\\u2028"));
- assertTrue(PeepholeSubstituteAlternateSyntax.containsUnicodeEscape(
- "foo\\\\u2028bar\\u2028"));
- }
- public void testFoldLiteralObjectConstructors() {
- enableNormalize();
- // Can fold when normalized
- fold("x = new Object", "x = ({})");
- fold("x = new Object()", "x = ({})");
- fold("x = Object()", "x = ({})");
- disableNormalize();
- // Cannot fold above when not normalized
- foldSame("x = new Object");
- foldSame("x = new Object()");
- foldSame("x = Object()");
- enableNormalize();
- // Cannot fold, the constructor being used is actually a local function
- foldSame("x = " +
- "(function f(){function Object(){this.x=4};return new Object();})();");
- }
- public void testFoldLiteralArrayConstructors() {
- enableNormalize();
- // No arguments - can fold when normalized
- fold("x = new Array", "x = []");
- fold("x = new Array()", "x = []");
- fold("x = Array()", "x = []");
- // One argument - can be fold when normalized
- fold("x = new Array(0)", "x = []");
- fold("x = Array(0)", "x = []");
- fold("x = new Array(\"a\")", "x = [\"a\"]");
- fold("x = Array(\"a\")", "x = [\"a\"]");
- // One argument - cannot be fold when normalized
- fold("x = new Array(7)", "x = Array(7)");
- fold("x = Array(7)", "x = Array(7)");
- fold("x = new Array(y)", "x = Array(y)");
- fold("x = Array(y)", "x = Array(y)");
- fold("x = new Array(foo())", "x = Array(foo())");
- fold("x = Array(foo())", "x = Array(foo())");
- // More than one argument - can be fold when normalized
- fold("x = new Array(1, 2, 3, 4)", "x = [1, 2, 3, 4]");
- fold("x = Array(1, 2, 3, 4)", "x = [1, 2, 3, 4]");
- fold("x = new Array('a', 1, 2, 'bc', 3, {}, 'abc')",
- "x = ['a', 1, 2, 'bc', 3, {}, 'abc']");
- fold("x = Array('a', 1, 2, 'bc', 3, {}, 'abc')",
- "x = ['a', 1, 2, 'bc', 3, {}, 'abc']");
- fold("x = new Array(Array(1, '2', 3, '4'))", "x = [[1, '2', 3, '4']]");
- fold("x = Array(Array(1, '2', 3, '4'))", "x = [[1, '2', 3, '4']]");
- fold("x = new Array(Object(), Array(\"abc\", Object(), Array(Array())))",
- "x = [{}, [\"abc\", {}, [[]]]]");
- fold("x = new Array(Object(), Array(\"abc\", Object(), Array(Array())))",
- "x = [{}, [\"abc\", {}, [[]]]]");
- disableNormalize();
- // Cannot fold above when not normalized
- foldSame("x = new Array");
- foldSame("x = new Array()");
- foldSame("x = Array()");
- foldSame("x = new Array(0)");
- foldSame("x = Array(0)");
- foldSame("x = new Array(\"a\")");
- foldSame("x = Array(\"a\")");
- foldSame("x = new Array(7)");
- foldSame("x = Array(7)");
- foldSame("x = new Array(foo())");
- foldSame("x = Array(foo())");
- foldSame("x = new Array(1, 2, 3, 4)");
- foldSame("x = Array(1, 2, 3, 4)");
- foldSame("x = new Array('a', 1, 2, 'bc', 3, {}, 'abc')");
- foldSame("x = Array('a', 1, 2, 'bc', 3, {}, 'abc')");
- foldSame("x = new Array(Array(1, '2', 3, '4'))");
- foldSame("x = Array(Array(1, '2', 3, '4'))");
- foldSame("x = new Array(" +
- "Object(), Array(\"abc\", Object(), Array(Array())))");
- foldSame("x = new Array(" +
- "Object(), Array(\"abc\", Object(), Array(Array())))");
- }
- public void testMinimizeExprCondition() {
- fold("(x ? true : false) && y()", "x&&y()");
- fold("(x ? false : true) && y()", "(!x)&&y()");
- fold("(x ? true : y) && y()", "(x || y)&&y()");
- fold("(x ? y : false) && y()", "(x && y)&&y()");
- fold("(x && true) && y()", "x && y()");
- fold("(x && false) && y()", "0&&y()");
- fold("(x || true) && y()", "1&&y()");
- fold("(x || false) && y()", "x&&y()");
- }
- public void testMinimizeWhileCondition() {
- // This test uses constant folding logic, so is only here for completeness.
- fold("while(!!true) foo()", "while(1) foo()");
- // These test tryMinimizeCondition
- fold("while(!!x) foo()", "while(x) foo()");
- fold("while(!(!x&&!y)) foo()", "while(x||y) foo()");
- fold("while(x||!!y) foo()", "while(x||y) foo()");
- fold("while(!(!!x&&y)) foo()", "while(!x||!y) foo()");
- fold("while(!(!x&&y)) foo()", "while(x||!y) foo()");
- fold("while(!(x||!y)) foo()", "while(!x&&y) foo()");
- fold("while(!(x||y)) foo()", "while(!x&&!y) foo()");
- fold("while(!(!x||y-z)) foo()", "while(x&&!(y-z)) foo()");
- fold("while(!(!(x/y)||z+w)) foo()", "while(x/y&&!(z+w)) foo()");
- foldSame("while(!(x+y||z)) foo()");
- foldSame("while(!(x&&y*z)) foo()");
- fold("while(!(!!x&&y)) foo()", "while(!x||!y) foo()");
- fold("while(x&&!0) foo()", "while(x) foo()");
- fold("while(x||!1) foo()", "while(x) foo()");
- fold("while(!((x,y)&&z)) foo()", "while(!(x,y)||!z) foo()");
- }
- public void testMinimizeForCondition() {
- // This test uses constant folding logic, so is only here for completeness.
- // These could be simplified to "for(;;) ..."
- fold("for(;!!true;) foo()", "for(;1;) foo()");
- // Don't bother with FOR inits as there are normalized out.
- fold("for(!!true;;) foo()", "for(!0;;) foo()");
- // These test tryMinimizeCondition
- fold("for(;!!x;) foo()", "for(;x;) foo()");
- // sanity check
- foldSame("for(a in b) foo()");
- foldSame("for(a in {}) foo()");
- foldSame("for(a in []) foo()");
- fold("for(a in !!true) foo()", "for(a in !0) foo()");
- }
- public void testMinimizeCondition_example1() {
- // Based on a real failing code sample.
- fold("if(!!(f() > 20)) {foo();foo()}", "if(f() > 20){foo();foo()}");
- }
- public void testFoldLoopBreakLate() {
- late = true;
- fold("for(;;) if (a) break", "for(;!a;);");
- foldSame("for(;;) if (a) { f(); break }");
- fold("for(;;) if (a) break; else f()", "for(;!a;) { { f(); } }");
- fold("for(;a;) if (b) break", "for(;a && !b;);");
- fold("for(;a;) { if (b) break; if (c) break; }", "for(;(a && !b) && !c;);");
- // 'while' is normalized to 'for'
- enableNormalize(true);
- fold("while(true) if (a) break", "for(;1&&!a;);");
- }
- public void testFoldLoopBreakEarly() {
- late = false;
- foldSame("for(;;) if (a) break");
- foldSame("for(;;) if (a) { f(); break }");
- foldSame("for(;;) if (a) break; else f()");
- foldSame("for(;a;) if (b) break");
- foldSame("for(;a;) { if (b) break; if (c) break; }");
- foldSame("while(1) if (a) break");
- enableNormalize(true);
- foldSame("while(1) if (a) break");
- }
- public void testFoldConditionalVarDeclaration() {
- fold("if(x) var y=1;else y=2", "var y=x?1:2");
- fold("if(x) y=1;else var y=2", "var y=x?1:2");
- foldSame("if(x) var y = 1; z = 2");
- foldSame("if(x||y) y = 1; var z = 2");
- foldSame("if(x) { var y = 1; print(y)} else y = 2 ");
- foldSame("if(x) var y = 1; else {y = 2; print(y)}");
- }
- public void testFoldReturnResult() {
- fold("function f(){return false;}", "function f(){return !1}");
- foldSame("function f(){return null;}");
- fold("function f(){return void 0;}",
- "function f(){}");
- foldSame("function f(){return void foo();}");
- fold("function f(){return undefined;}",
- "function f(){}");
- fold("function f(){if(a()){return undefined;}}",
- "function f(){if(a()){}}");
- }
- public void testFoldStandardConstructors() {
- foldSame("new Foo('a')");
- foldSame("var x = new goog.Foo(1)");
- foldSame("var x = new String(1)");
- foldSame("var x = new Number(1)");
- foldSame("var x = new Boolean(1)");
- enableNormalize();
- fold("var x = new Object('a')", "var x = Object('a')");
- fold("var x = new RegExp('')", "var x = RegExp('')");
- fold("var x = new Error('20')", "var x = Error(\"20\")");
- fold("var x = new Array(20)", "var x = Array(20)");
- }
- public void testSubsituteReturn() {
- fold("function f() { while(x) { return }}",
- "function f() { while(x) { break }}");
- foldSame("function f() { while(x) { return 5 } }");
- foldSame("function f() { a: { return 5 } }");
- fold("function f() { while(x) { return 5} return 5}",
- "function f() { while(x) { break } return 5}");
- fold("function f() { while(x) { return x} return x}",
- "function f() { while(x) { break } return x}");
- fold("function f() { while(x) { if (y) { return }}}",
- "function f() { while(x) { if (y) { break }}}");
- fold("function f() { while(x) { if (y) { return }} return}",
- "function f() { while(x) { if (y) { break }}}");
- fold("function f() { while(x) { if (y) { return 5 }} return 5}",
- "function f() { while(x) { if (y) { break }} return 5}");
- // It doesn't matter if x is changed between them. We are still returning
- // x at whatever x value current holds. The whole x = 1 is skipped.
- fold("function f() { while(x) { if (y) { return x } x = 1} return x}",
- "function f() { while(x) { if (y) { break } x = 1} return x}");
- // RemoveUnreachableCode would take care of the useless breaks.
- fold("function f() { while(x) { if (y) { return x } return x} return x}",
- "function f() { while(x) { if (y) {} break }return x}");
- // A break here only breaks out of the inner loop.
- foldSame("function f() { while(x) { while (y) { return } } }");
- foldSame("function f() { while(1) { return 7} return 5}");
- foldSame("function f() {" +
- " try { while(x) {return f()}} catch (e) { } return f()}");
- foldSame("function f() {" +
- " try { while(x) {return f()}} finally {alert(1)} return f()}");
- // Both returns has the same handler
- fold("function f() {" +
- " try { while(x) { return f() } return f() } catch (e) { } }",
- "function f() {" +
- " try { while(x) { break } return f() } catch (e) { } }");
- // We can't fold this because it'll change the order of when foo is called.
- foldSame("function f() {" +
- " try { while(x) { return foo() } } finally { alert(1) } " +
- " return foo()}");
- // This is fine, we have no side effect in the return value.
- fold("function f() {" +
- " try { while(x) { return 1 } } finally { alert(1) } return 1}",
- "function f() {" +
- " try { while(x) { break } } finally { alert(1) } return 1}"
- );
- foldSame("function f() { try{ return a } finally { a = 2 } return a; }");
- fold(
- "function f() { switch(a){ case 1: return a; default: g();} return a;}",
- "function f() { switch(a){ case 1: break; default: g();} return a; }");
- }
- public void testSubsituteBreakForThrow() {
- foldSame("function f() { while(x) { throw Error }}");
- fold("function f() { while(x) { throw Error } throw Error }",
- "function f() { while(x) { break } throw Error}");
- foldSame("function f() { while(x) { throw Error(1) } throw Error(2)}");
- foldSame("function f() { while(x) { throw Error(1) } return Error(2)}");
- foldSame("function f() { while(x) { throw 5 } }");
- foldSame("function f() { a: { throw 5 } }");
- fold("function f() { while(x) { throw 5} throw 5}",
- "function f() { while(x) { break } throw 5}");
- fold("function f() { while(x) { throw x} throw x}",
- "function f() { while(x) { break } throw x}");
- foldSame("function f() { while(x) { if (y) { throw Error }}}");
- fold("function f() { while(x) { if (y) { throw Error }} throw Error}",
- "function f() { while(x) { if (y) { break }} throw Error}");
- fold("function f() { while(x) { if (y) { throw 5 }} throw 5}",
- "function f() { while(x) { if (y) { break }} throw 5}");
- // It doesn't matter if x is changed between them. We are still throwing
- // x at whatever x value current holds. The whole x = 1 is skipped.
- fold("function f() { while(x) { if (y) { throw x } x = 1} throw x}",
- "function f() { while(x) { if (y) { break } x = 1} throw x}");
- // RemoveUnreachableCode would take care of the useless breaks.
- fold("function f() { while(x) { if (y) { throw x } throw x} throw x}",
- "function f() { while(x) { if (y) {} break }throw x}");
- // A break here only breaks out of the inner loop.
- foldSame("function f() { while(x) { while (y) { throw Error } } }");
- foldSame("function f() { while(1) { throw 7} throw 5}");
- foldSame("function f() {" +
- " try { while(x) {throw f()}} catch (e) { } throw f()}");
- foldSame("function f() {" +
- " try { while(x) {throw f()}} finally {alert(1)} throw f()}");
- // Both throws has the same handler
- fold("function f() {" +
- " try { while(x) { throw f() } throw f() } catch (e) { } }",
- "function f() {" +
- " try { while(x) { break } throw f() } catch (e) { } }");
- // We can't fold this because it'll change the order of when foo is called.
- foldSame("function f() {" +
- " try { while(x) { throw foo() } } finally { alert(1) } " +
- " throw foo()}");
- // This is fine, we have no side effect in the throw value.
- fold("function f() {" +
- " try { while(x) { throw 1 } } finally { alert(1) } throw 1}",
- "function f() {" +
- " try { while(x) { break } } finally { alert(1) } throw 1}"
- );
- foldSame("function f() { try{ throw a } finally { a = 2 } throw a; }");
- fold(
- "function f() { switch(a){ case 1: throw a; default: g();} throw a;}",
- "function f() { switch(a){ case 1: break; default: g();} throw a; }");
- }
- public void testRemoveDuplicateReturn() {
- fold("function f() { return; }",
- "function f(){}");
- foldSame("function f() { return a; }");
- fold("function f() { if (x) { return a } return a; }",
- "function f() { if (x) {} return a; }");
- foldSame(
- "function f() { try { if (x) { return a } } catch(e) {} return a; }");
- foldSame(
- "function f() { try { if (x) {} } catch(e) {} return 1; }");
- // finally clauses may have side effects
- foldSame(
- "function f() { try { if (x) { return a } } finally { a++ } return a; }");
- // but they don't matter if the result doesn't have side effects and can't
- // be affect by side-effects.
- fold("function f() { try { if (x) { return 1 } } finally {} return 1; }",
- "function f() { try { if (x) {} } finally {} return 1; }");
- fold("function f() { switch(a){ case 1: return a; } return a; }",
- "function f() { switch(a){ case 1: } return a; }");
- fold("function f() { switch(a){ " +
- " case 1: return a; case 2: return a; } return a; }",
- "function f() { switch(a){ " +
- " case 1: break; case 2: } return a; }");
- }
- public void testRemoveDuplicateThrow() {
- foldSame("function f() { throw a; }");
- fold("function f() { if (x) { throw a } throw a; }",
- "function f() { if (x) {} throw a; }");
- foldSame(
- "function f() { try { if (x) {throw a} } catch(e) {} throw a; }");
- foldSame(
- "function f() { try { if (x) {throw 1} } catch(e) {f()} throw 1; }");
- foldSame(
- "function f() { try { if (x) {throw 1} } catch(e) {f()} throw 1; }");
- foldSame(
- "function f() { try { if (x) {throw 1} } catch(e) {throw 1}}");
- fold(
- "function f() { try { if (x) {throw 1} } catch(e) {throw 1} throw 1; }",
- "function f() { try { if (x) {throw 1} } catch(e) {} throw 1; }");
- // finally clauses may have side effects
- foldSame(
- "function f() { try { if (x) { throw a } } finally { a++ } throw a; }");
- // but they don't matter if the result doesn't have side effects and can't
- // be affect by side-effects.
- fold("function f() { try { if (x) { throw 1 } } finally {} throw 1; }",
- "function f() { try { if (x) {} } finally {} throw 1; }");
- fold("function f() { switch(a){ case 1: throw a; } throw a; }",
- "function f() { switch(a){ case 1: } throw a; }");
- fold("function f() { switch(a){ " +
- "case 1: throw a; case 2: throw a; } throw a; }",
- "function f() { switch(a){ case 1: break; case 2: } throw a; }");
- }
- public void testNestedIfCombine() {
- fold("if(x)if(y){while(1){}}", "if(x&&y){while(1){}}");
- fold("if(x||z)if(y){while(1){}}", "if((x||z)&&y){while(1){}}");
- fold("if(x)if(y||z){while(1){}}", "if((x)&&(y||z)){while(1){}}");
- foldSame("if(x||z)if(y||z){while(1){}}");
- fold("if(x)if(y){if(z){while(1){}}}", "if(x&&y&&z){while(1){}}");
- }
- public void testFoldTrueFalse() {
- fold("x = true", "x = !0");
- fold("x = false", "x = !1");
- }
- public void testIssue291() {
- fold("if (true) { f.onchange(); }", "if (1) f.onchange();");
- foldSame("if (f) { f.onchange(); }");
- foldSame("if (f) { f.bar(); } else { f.onchange(); }");
- fold("if (f) { f.bonchange(); }", "f && f.bonchange();");
- foldSame("if (f) { f['x'](); }");
- }
- public void testUndefined() {
- foldSame("var x = undefined");
- foldSame("function f(f) {var undefined=2;var x = undefined;}");
- this.enableNormalize();
- fold("var x = undefined", "var x=void 0");
- foldSame(
- "var undefined = 1;" +
- "function f() {var undefined=2;var x = undefined;}");
- foldSame("function f(undefined) {}");
- foldSame("try {} catch(undefined) {}");
- foldSame("for (undefined in {}) {}");
- foldSame("undefined++;");
- fold("undefined += undefined;", "undefined += void 0;");
- }
- public void testSplitCommaExpressions() {
- late = false;
- // Don't try to split in expressions.
- foldSame("while (foo(), !0) boo()");
- foldSame("var a = (foo(), !0);");
- foldSame("a = (foo(), !0);");
- // Don't try to split COMMA under LABELs.
- foldSame("a:a(),b()");
- fold("(x=2), foo()", "x=2; foo()");
- fold("foo(), boo();", "foo(); boo()");
- fold("(a(), b()), (c(), d());", "a(); b(); c(); d();");
- fold("foo(), true", "foo();1");
- fold("function x(){foo(), !0}", "function x(){foo(); 1}");
- }
- public void testComma1() {
- late = false;
- fold("1, 2", "1; 1");
- late = true;
- foldSame("1, 2");
- }
- public void testComma2() {
- late = false;
- test("1, a()", "1; a()");
- late = true;
- foldSame("1, a()");
- }
- public void testComma3() {
- late = false;
- test("1, a(), b()", "1; a(); b()");
- late = true;
- foldSame("1, a(), b()");
- }
- public void testComma4() {
- late = false;
- test("a(), b()", "a();b()");
- late = true;
- foldSame("a(), b()");
- }
- public void testComma5() {
- late = false;
- test("a(), b(), 1", "a();b();1");
- late = true;
- foldSame("a(), b(), 1");
- }
- public void testObjectLiteral() {
- test("({})", "1");
- test("({a:1})", "1");
- testSame("({a:foo()})");
- testSame("({'a':foo()})");
- }
- public void testArrayLiteral() {
- test("([])", "1");
- test("([1])", "1");
- test("([a])", "1");
- testSame("([foo()])");
- }
- public void testStringArraySplitting() {
- testSame("var x=['1','2','3','4']");
- testSame("var x=['1','2','3','4','5']");
- test("var x=['1','2','3','4','5','6']",
- "var x='1,2,3,4,5,6'.split(',')");
- test("var x=['1','2','3','4','5','6','7']",
- "var x='1,2,3,4,5,6,7'.split(',')");
- test("var x=[',',',',',',',',',',',']",
- "var x=', , , , , ,'.split(' ')");
- test("var x=[',',' ',',',',',',',',']",
- "var x=',; ;,;,;,;,'.split(';')");
- test("var x=[',',' ',',',',',',',',']",
- "var x=',; ;,;,;,;,'.split(';')");
- }
- public void testRemoveElseCause() {
- test("function f() {" +
- " if(x) return 1;" +
- " else if(x) return 2;" +
- " else if(x) return 3 }",
- "function f() {" +
- " if(x) return 1;" +
- "{ if(x) return 2;" +
- "{ if(x) return 3 } } }");
- }
- public void testRemoveElseCause1() {
- test("function f() { if (x) throw 1; else f() }",
- "function f() { if (x) throw 1; { f() } }");
- }
- public void testRemoveElseCause2() {
- test("function f() { if (x) return 1; else f() }",
- "function f() { if (x) return 1; { f() } }");
- test("function f() { if (x) return; else f() }",
- "function f() { if (x) {} else { f() } }");
- // This case is handled by minimize exit points.
- testSame("function f() { if (x) return; f() }");
- }
- public void testRemoveElseCause3() {
- testSame("function f() { a:{if (x) break a; else f() } }");
- testSame("function f() { if (x) { a:{ break a } } else f() }");
- testSame("function f() { if (x) a:{ break a } else f() }");
- }
- public void testRemoveElseCause4() {
- testSame("function f() { if (x) { if (y) { return 1; } } else f() }");
- }
- public void testBindToCall1() {
- test("(goog.bind(f))()", "f()");
- test("(goog.bind(f,a))()", "f.call(a)");
- test("(goog.bind(f,a,b))()", "f.call(a,b)");
- test("(goog.bind(f))(a)", "f(a)");
- test("(goog.bind(f,a))(b)", "f.call(a,b)");
- test("(goog.bind(f,a,b))(c)", "f.call(a,b,c)");
- test("(goog.partial(f))()", "f()");
- test("(goog.partial(f,a))()", "f(a)");
- test("(goog.partial(f,a,b))()", "f(a,b)");
- test("(goog.partial(f))(a)", "f(a)");
- test("(goog.partial(f,a))(b)", "f(a,b)");
- test("(goog.partial(f,a,b))(c)", "f(a,b,c)");
- test("((function(){}).bind())()", "((function(){}))()");
- test("((function(){}).bind(a))()", "((function(){})).call(a)");
- test("((function(){}).bind(a,b))()", "((function(){})).call(a,b)");
- test("((function(){}).bind())(a)", "((function(){}))(a)");
- test("((function(){}).bind(a))(b)", "((function(){})).call(a,b)");
- test("((function(){}).bind(a,b))(c)", "((function(){})).call(a,b,c)");
- // Without using type information we don't know "f" is a function.
- testSame("(f.bind())()");
- testSame("(f.bind(a))()");
- testSame("(f.bind())(a)");
- testSame("(f.bind(a))(b)");
- // Don't rewrite if the bind isn't the immediate call target
- testSame("(goog.bind(f)).call(g)");
- }
- public void testBindToCall2() {
- test("(goog$bind(f))()", "f()");
- test("(goog$bind(f,a))()", "f.call(a)");
- test("(goog$bind(f,a,b))()", "f.call(a,b)");
- test("(goog$bind(f))(a)", "f(a)");
- test("(goog$bind(f,a))(b)", "f.call(a,b)");
- test("(goog$bind(f,a,b))(c)", "f.call(a,b,c)");
- test("(goog$partial(f))()", "f()");
- test("(goog$partial(f,a))()", "f(a)");
- test("(goog$partial(f,a,b))()", "f(a,b)");
- test("(goog$partial(f))(a)", "f(a)");
- test("(goog$partial(f,a))(b)", "f(a,b)");
- test("(goog$partial(f,a,b))(c)", "f(a,b,c)");
- // Don't rewrite if the bind isn't the immediate call target
- testSame("(goog$bind(f)).call(g)");
- }
- public void testBindToCall3() {
- // TODO(johnlenz): The code generator wraps free calls with (0,...) to
- // prevent leaking "this", but the parser doesn't unfold it, making a
- // AST comparison fail. For now do a string comparison to validate the
- // correct code is in fact generated.
- // The FREE call wrapping should be moved out of the code generator
- // and into a denormalizing pass.
- new StringCompareTestCase().testBindToCall3();
- }
- public void testSimpleFunctionCall() {
- test("var a = String(23)", "var a = '' + 23");
- test("var a = String('hello')", "var a = '' + 'hello'");
- }
- private static class StringCompareTestCase extends CompilerTestCase {
- StringCompareTestCase() {
- super("", false);
- }
- @Override
- protected CompilerPass getProcessor(Compiler compiler) {
- CompilerPass peepholePass =
- new PeepholeOptimizationsPass(compiler,
- new PeepholeSubstituteAlternateSyntax(false));
- return peepholePass;
- }
- public void testBindToCall3() {
- test("(goog.bind(f.m))()", "(0,f.m)()");
- test("(goog.bind(f.m,a))()", "f.m.call(a)");
- test("(goog.bind(f.m))(a)", "(0,f.m)(a)");
- test("(goog.bind(f.m,a))(b)", "f.m.call(a,b)");
- test("(goog.partial(f.m))()", "(0,f.m)()");
- test("(goog.partial(f.m,a))()", "(0,f.m)(a)");
- test("(goog.partial(f.m))(a)", "(0,f.m)(a)");
- test("(goog.partial(f.m,a))(b)", "(0,f.m)(a,b)");
- // Without using type information we don't know "f" is a function.
- testSame("f.m.bind()()");
- testSame("f.m.bind(a)()");
- testSame("f.m.bind()(a)");
- testSame("f.m.bind(a)(b)");
- // Don't rewrite if the bind isn't the immediate call target
- testSame("goog.bind(f.m).call(g)");
- }
- }
- }