PageRenderTime 57ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/closure-compiler/test/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntaxTest.java

https://bitbucket.org/simpler/squeeze
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
  1. /*
  2. * Copyright 2004 The Closure Compiler Authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.google.javascript.jscomp;
  17. /**
  18. * Tests for {@link PeepholeSubstituteAlternateSyntax} in isolation.
  19. * Tests for the interaction of multiple peephole passes are in
  20. * PeepholeIntegrationTest.
  21. */
  22. public class PeepholeSubstituteAlternateSyntaxTest extends CompilerTestCase {
  23. // Externs for builtin constructors
  24. // Needed for testFoldLiteralObjectConstructors(),
  25. // testFoldLiteralArrayConstructors() and testFoldRegExp...()
  26. private static final String FOLD_CONSTANTS_TEST_EXTERNS =
  27. "var Object = function f(){};\n" +
  28. "var RegExp = function f(a){};\n" +
  29. "var Array = function f(a){};\n";
  30. private boolean late = true;
  31. // TODO(user): Remove this when we no longer need to do string comparison.
  32. private PeepholeSubstituteAlternateSyntaxTest(boolean compareAsTree) {
  33. super(FOLD_CONSTANTS_TEST_EXTERNS, compareAsTree);
  34. }
  35. public PeepholeSubstituteAlternateSyntaxTest() {
  36. super(FOLD_CONSTANTS_TEST_EXTERNS);
  37. }
  38. @Override
  39. public void setUp() throws Exception {
  40. late = true;
  41. super.setUp();
  42. enableLineNumberCheck(true);
  43. disableNormalize();
  44. }
  45. @Override
  46. public CompilerPass getProcessor(final Compiler compiler) {
  47. CompilerPass peepholePass =
  48. new PeepholeOptimizationsPass(compiler,
  49. new PeepholeSubstituteAlternateSyntax(late));
  50. return peepholePass;
  51. }
  52. @Override
  53. protected int getNumRepetitions() {
  54. // Reduce this to 2 if we get better expression evaluators.
  55. return 2;
  56. }
  57. private void foldSame(String js) {
  58. testSame(js);
  59. }
  60. private void fold(String js, String expected) {
  61. test(js, expected);
  62. }
  63. private void fold(String js, String expected, DiagnosticType warning) {
  64. test(js, expected, warning);
  65. }
  66. void assertResultString(String js, String expected) {
  67. assertResultString(js, expected, false);
  68. }
  69. // TODO(user): This is same as fold() except it uses string comparison. Any
  70. // test that needs tell us where a folding is constructing an invalid AST.
  71. void assertResultString(String js, String expected, boolean normalize) {
  72. PeepholeSubstituteAlternateSyntaxTest scTest
  73. = new PeepholeSubstituteAlternateSyntaxTest(false);
  74. if (normalize) {
  75. scTest.enableNormalize();
  76. } else {
  77. scTest.disableNormalize();
  78. }
  79. scTest.test(js, expected);
  80. }
  81. /** Check that removing blocks with 1 child works */
  82. public void testFoldOneChildBlocks() {
  83. late = false;
  84. fold("function f(){if(x)a();x=3}",
  85. "function f(){x&&a();x=3}");
  86. fold("function f(){if(x){a()}x=3}",
  87. "function f(){x&&a();x=3}");
  88. fold("function f(){if(x){return 3}}",
  89. "function f(){if(x)return 3}");
  90. fold("function f(){if(x){a()}}",
  91. "function f(){x&&a()}");
  92. fold("function f(){if(x){throw 1}}", "function f(){if(x)throw 1;}");
  93. // Try it out with functions
  94. fold("function f(){if(x){foo()}}", "function f(){x&&foo()}");
  95. fold("function f(){if(x){foo()}else{bar()}}",
  96. "function f(){x?foo():bar()}");
  97. // Try it out with properties and methods
  98. fold("function f(){if(x){a.b=1}}", "function f(){if(x)a.b=1}");
  99. fold("function f(){if(x){a.b*=1}}", "function f(){x&&(a.b*=1)}");
  100. fold("function f(){if(x){a.b+=1}}", "function f(){x&&(a.b+=1)}");
  101. fold("function f(){if(x){++a.b}}", "function f(){x&&++a.b}");
  102. fold("function f(){if(x){a.foo()}}", "function f(){x&&a.foo()}");
  103. // Try it out with throw/catch/finally [which should not change]
  104. fold("function f(){try{foo()}catch(e){bar(e)}finally{baz()}}",
  105. "function f(){try{foo()}catch(e){bar(e)}finally{baz()}}");
  106. // Try it out with switch statements
  107. fold("function f(){switch(x){case 1:break}}",
  108. "function f(){switch(x){case 1:break}}");
  109. // Do while loops stay in a block if that's where they started
  110. fold("function f(){if(e1){do foo();while(e2)}else foo2()}",
  111. "function f(){if(e1){do foo();while(e2)}else foo2()}");
  112. // Test an obscure case with do and while
  113. fold("if(x){do{foo()}while(y)}else bar()",
  114. "if(x){do foo();while(y)}else bar()");
  115. // Play with nested IFs
  116. fold("function f(){if(x){if(y)foo()}}",
  117. "function f(){x&&y&&foo()}");
  118. fold("function f(){if(x){if(y)foo();else bar()}}",
  119. "function f(){x&&(y?foo():bar())}");
  120. fold("function f(){if(x){if(y)foo()}else bar()}",
  121. "function f(){x?y&&foo():bar()}");
  122. fold("function f(){if(x){if(y)foo();else bar()}else{baz()}}",
  123. "function f(){x?y?foo():bar():baz()}");
  124. fold("if(e1){while(e2){if(e3){foo()}}}else{bar()}",
  125. "if(e1)while(e2)e3&&foo();else bar()");
  126. fold("if(e1){with(e2){if(e3){foo()}}}else{bar()}",
  127. "if(e1)with(e2)e3&&foo();else bar()");
  128. fold("if(a||b){if(c||d){var x;}}", "if(a||b)if(c||d)var x");
  129. fold("if(x){ if(y){var x;}else{var z;} }",
  130. "if(x)if(y)var x;else var z");
  131. // NOTE - technically we can remove the blocks since both the parent
  132. // and child have elses. But we don't since it causes ambiguities in
  133. // some cases where not all descendent ifs having elses
  134. fold("if(x){ if(y){var x;}else{var z;} }else{var w}",
  135. "if(x)if(y)var x;else var z;else var w");
  136. fold("if (x) {var x;}else { if (y) { var y;} }",
  137. "if(x)var x;else if(y)var y");
  138. // Here's some of the ambiguous cases
  139. fold("if(a){if(b){f1();f2();}else if(c){f3();}}else {if(d){f4();}}",
  140. "if(a)if(b){f1();f2()}else c&&f3();else d&&f4()");
  141. fold("function f(){foo()}", "function f(){foo()}");
  142. fold("switch(x){case y: foo()}", "switch(x){case y:foo()}");
  143. fold("try{foo()}catch(ex){bar()}finally{baz()}",
  144. "try{foo()}catch(ex){bar()}finally{baz()}");
  145. }
  146. /** Try to minimize returns */
  147. public void testFoldReturns() {
  148. fold("function f(){if(x)return 1;else return 2}",
  149. "function f(){return x?1:2}");
  150. fold("function f(){if(x)return 1;return 2}",
  151. "function f(){return x?1:2}");
  152. fold("function f(){if(x)return;return 2}",
  153. "function f(){return x?void 0:2}");
  154. fold("function f(){if(x)return 1+x;else return 2-x}",
  155. "function f(){return x?1+x:2-x}");
  156. fold("function f(){if(x)return 1+x;return 2-x}",
  157. "function f(){return x?1+x:2-x}");
  158. fold("function f(){if(x)return y += 1;else return y += 2}",
  159. "function f(){return x?(y+=1):(y+=2)}");
  160. fold("function f(){if(x)return;else return 2-x}",
  161. "function f(){if(x);else return 2-x}");
  162. fold("function f(){if(x)return;return 2-x}",
  163. "function f(){return x?void 0:2-x}");
  164. fold("function f(){if(x)return x;else return}",
  165. "function f(){if(x)return x;{}}");
  166. fold("function f(){if(x)return x;return}",
  167. "function f(){if(x)return x}");
  168. foldSame("function f(){for(var x in y) { return x.y; } return k}");
  169. }
  170. public void testCombineIfs1() {
  171. fold("function f() {if (x) return 1; if (y) return 1}",
  172. "function f() {if (x||y) return 1;}");
  173. fold("function f() {if (x) return 1; if (y) foo(); else return 1}",
  174. "function f() {if ((!x)&&y) foo(); else return 1;}");
  175. }
  176. public void testCombineIfs2() {
  177. // combinable but not yet done
  178. foldSame("function f() {if (x) throw 1; if (y) throw 1}");
  179. // Can't combine, side-effect
  180. fold("function f(){ if (x) g(); if (y) g() }",
  181. "function f(){ x&&g(); y&&g() }");
  182. // Can't combine, side-effect
  183. fold("function f(){ if (x) y = 0; if (y) y = 0; }",
  184. "function f(){ x&&(y = 0); y&&(y = 0); }");
  185. }
  186. public void testCombineIfs3() {
  187. foldSame("function f() {if (x) return 1; if (y) {g();f()}}");
  188. }
  189. /** Try to minimize assignments */
  190. public void testFoldAssignments() {
  191. fold("function f(){if(x)y=3;else y=4;}", "function f(){y=x?3:4}");
  192. fold("function f(){if(x)y=1+a;else y=2+a;}", "function f(){y=x?1+a:2+a}");
  193. // and operation assignments
  194. fold("function f(){if(x)y+=1;else y+=2;}", "function f(){y+=x?1:2}");
  195. fold("function f(){if(x)y-=1;else y-=2;}", "function f(){y-=x?1:2}");
  196. fold("function f(){if(x)y%=1;else y%=2;}", "function f(){y%=x?1:2}");
  197. fold("function f(){if(x)y|=1;else y|=2;}", "function f(){y|=x?1:2}");
  198. // sanity check, don't fold if the 2 ops don't match
  199. foldSame("function f(){x ? y-=1 : y+=2}");
  200. // sanity check, don't fold if the 2 LHS don't match
  201. foldSame("function f(){x ? y-=1 : z-=1}");
  202. // sanity check, don't fold if there are potential effects
  203. foldSame("function f(){x ? y().a=3 : y().a=4}");
  204. }
  205. public void testRemoveDuplicateStatements() {
  206. fold("if (a) { x = 1; x++ } else { x = 2; x++ }",
  207. "x=(a) ? 1 : 2; x++");
  208. fold("if (a) { x = 1; x++; y += 1; z = pi; }" +
  209. " else { x = 2; x++; y += 1; z = pi; }",
  210. "x=(a) ? 1 : 2; x++; y += 1; z = pi;");
  211. fold("function z() {" +
  212. "if (a) { foo(); return !0 } else { goo(); return !0 }" +
  213. "}",
  214. "function z() {(a) ? foo() : goo(); return !0}");
  215. fold("function z() {if (a) { foo(); x = true; return true " +
  216. "} else { goo(); x = true; return true }}",
  217. "function z() {(a) ? foo() : goo(); x = !0; return !0}");
  218. fold("function z() {" +
  219. " if (a) { bar(); foo(); return true }" +
  220. " else { bar(); goo(); return true }" +
  221. "}",
  222. "function z() {" +
  223. " if (a) { bar(); foo(); }" +
  224. " else { bar(); goo(); }" +
  225. " return !0;" +
  226. "}");
  227. }
  228. public void testNotCond() {
  229. fold("function f(){if(!x)foo()}", "function f(){x||foo()}");
  230. fold("function f(){if(!x)b=1}", "function f(){x||(b=1)}");
  231. fold("if(!x)z=1;else if(y)z=2", "x ? y&&(z=2) : z=1");
  232. foldSame("function f(){if(!(x=1))a.b=1}");
  233. }
  234. public void testAndParenthesesCount() {
  235. fold("function f(){if(x||y)a.foo()}", "function f(){(x||y)&&a.foo()}");
  236. fold("function f(){if(x.a)x.a=0}",
  237. "function f(){x.a&&(x.a=0)}");
  238. foldSame("function f(){if(x()||y()){x()||y()}}");
  239. }
  240. public void testFoldLogicalOpStringCompare() {
  241. // side-effects
  242. // There is two way to parse two &&'s and both are correct.
  243. assertResultString("if(foo() && false) z()", "foo()&&0&&z()");
  244. }
  245. public void testFoldNot() {
  246. fold("while(!(x==y)){a=b;}" , "while(x!=y){a=b;}");
  247. fold("while(!(x!=y)){a=b;}" , "while(x==y){a=b;}");
  248. fold("while(!(x===y)){a=b;}", "while(x!==y){a=b;}");
  249. fold("while(!(x!==y)){a=b;}", "while(x===y){a=b;}");
  250. // Because !(x<NaN) != x>=NaN don't fold < and > cases.
  251. foldSame("while(!(x>y)){a=b;}");
  252. foldSame("while(!(x>=y)){a=b;}");
  253. foldSame("while(!(x<y)){a=b;}");
  254. foldSame("while(!(x<=y)){a=b;}");
  255. foldSame("while(!(x<=NaN)){a=b;}");
  256. // NOT forces a boolean context
  257. fold("x = !(y() && true)", "x = !y()");
  258. // This will be further optimized by PeepholeFoldConstants.
  259. fold("x = !true", "x = !1");
  260. }
  261. public void testFoldRegExpConstructor() {
  262. enableNormalize();
  263. // Cannot fold all the way to a literal because there are too few arguments.
  264. fold("x = new RegExp", "x = RegExp()");
  265. // Empty regexp should not fold to // since that is a line comment in js
  266. fold("x = new RegExp(\"\")", "x = RegExp(\"\")");
  267. fold("x = new RegExp(\"\", \"i\")", "x = RegExp(\"\",\"i\")");
  268. // Bogus flags should not fold
  269. fold("x = new RegExp(\"foobar\", \"bogus\")",
  270. "x = RegExp(\"foobar\",\"bogus\")",
  271. PeepholeSubstituteAlternateSyntax.INVALID_REGULAR_EXPRESSION_FLAGS);
  272. // Can Fold
  273. fold("x = new RegExp(\"foobar\")", "x = /foobar/");
  274. fold("x = RegExp(\"foobar\")", "x = /foobar/");
  275. fold("x = new RegExp(\"foobar\", \"i\")", "x = /foobar/i");
  276. // Make sure that escaping works
  277. fold("x = new RegExp(\"\\\\.\", \"i\")", "x = /\\./i");
  278. fold("x = new RegExp(\"/\", \"\")", "x = /\\//");
  279. fold("x = new RegExp(\"[/]\", \"\")", "x = /[/]/");
  280. fold("x = new RegExp(\"///\", \"\")", "x = /\\/\\/\\//");
  281. fold("x = new RegExp(\"\\\\\\/\", \"\")", "x = /\\//");
  282. fold("x = new RegExp(\"\\n\")", "x = /\\n/");
  283. fold("x = new RegExp('\\\\\\r')", "x = /\\r/");
  284. // Don't fold really long regexp literals, because Opera 9.2's
  285. // regexp parser will explode.
  286. String longRegexp = "";
  287. for (int i = 0; i < 200; i++) longRegexp += "x";
  288. foldSame("x = RegExp(\"" + longRegexp + "\")");
  289. // Shouldn't fold RegExp unnormalized because
  290. // we can't be sure that RegExp hasn't been redefined
  291. disableNormalize();
  292. foldSame("x = new RegExp(\"foobar\")");
  293. }
  294. public void testVersionSpecificRegExpQuirks() {
  295. enableNormalize();
  296. // Don't fold if the flags contain 'g'
  297. enableEcmaScript5(false);
  298. fold("x = new RegExp(\"foobar\", \"g\")",
  299. "x = RegExp(\"foobar\",\"g\")");
  300. fold("x = new RegExp(\"foobar\", \"ig\")",
  301. "x = RegExp(\"foobar\",\"ig\")");
  302. // ... unless in EcmaScript 5 mode per section 7.8.5 of EcmaScript 5.
  303. enableEcmaScript5(true);
  304. fold("x = new RegExp(\"foobar\", \"ig\")",
  305. "x = /foobar/ig");
  306. // Don't fold things that crash older versions of Safari and that don't work
  307. // as regex literals on other old versions of Safari
  308. enableEcmaScript5(false);
  309. fold("x = new RegExp(\"\\u2028\")", "x = RegExp(\"\\u2028\")");
  310. fold("x = new RegExp(\"\\\\\\\\u2028\")", "x = /\\\\u2028/");
  311. // Sunset Safari exclusions for EcmaScript 5 and later.
  312. enableEcmaScript5(true);
  313. fold("x = new RegExp(\"\\u2028\\u2029\")", "x = /\\u2028\\u2029/");
  314. fold("x = new RegExp(\"\\\\u2028\")", "x = /\\u2028/");
  315. fold("x = new RegExp(\"\\\\\\\\u2028\")", "x = /\\\\u2028/");
  316. }
  317. public void testFoldRegExpConstructorStringCompare() {
  318. // Might have something to do with the internal representation of \n and how
  319. // it is used in node comparison.
  320. assertResultString("x=new RegExp(\"\\n\", \"i\")", "x=/\\n/i", true);
  321. }
  322. public void testContainsUnicodeEscape() throws Exception {
  323. assertTrue(!PeepholeSubstituteAlternateSyntax.containsUnicodeEscape(""));
  324. assertTrue(!PeepholeSubstituteAlternateSyntax.containsUnicodeEscape("foo"));
  325. assertTrue(PeepholeSubstituteAlternateSyntax.containsUnicodeEscape(
  326. "\u2028"));
  327. assertTrue(PeepholeSubstituteAlternateSyntax.containsUnicodeEscape(
  328. "\\u2028"));
  329. assertTrue(
  330. PeepholeSubstituteAlternateSyntax.containsUnicodeEscape("foo\\u2028"));
  331. assertTrue(!PeepholeSubstituteAlternateSyntax.containsUnicodeEscape(
  332. "foo\\\\u2028"));
  333. assertTrue(PeepholeSubstituteAlternateSyntax.containsUnicodeEscape(
  334. "foo\\\\u2028bar\\u2028"));
  335. }
  336. public void testFoldLiteralObjectConstructors() {
  337. enableNormalize();
  338. // Can fold when normalized
  339. fold("x = new Object", "x = ({})");
  340. fold("x = new Object()", "x = ({})");
  341. fold("x = Object()", "x = ({})");
  342. disableNormalize();
  343. // Cannot fold above when not normalized
  344. foldSame("x = new Object");
  345. foldSame("x = new Object()");
  346. foldSame("x = Object()");
  347. enableNormalize();
  348. // Cannot fold, the constructor being used is actually a local function
  349. foldSame("x = " +
  350. "(function f(){function Object(){this.x=4};return new Object();})();");
  351. }
  352. public void testFoldLiteralArrayConstructors() {
  353. enableNormalize();
  354. // No arguments - can fold when normalized
  355. fold("x = new Array", "x = []");
  356. fold("x = new Array()", "x = []");
  357. fold("x = Array()", "x = []");
  358. // One argument - can be fold when normalized
  359. fold("x = new Array(0)", "x = []");
  360. fold("x = Array(0)", "x = []");
  361. fold("x = new Array(\"a\")", "x = [\"a\"]");
  362. fold("x = Array(\"a\")", "x = [\"a\"]");
  363. // One argument - cannot be fold when normalized
  364. fold("x = new Array(7)", "x = Array(7)");
  365. fold("x = Array(7)", "x = Array(7)");
  366. fold("x = new Array(y)", "x = Array(y)");
  367. fold("x = Array(y)", "x = Array(y)");
  368. fold("x = new Array(foo())", "x = Array(foo())");
  369. fold("x = Array(foo())", "x = Array(foo())");
  370. // More than one argument - can be fold when normalized
  371. fold("x = new Array(1, 2, 3, 4)", "x = [1, 2, 3, 4]");
  372. fold("x = Array(1, 2, 3, 4)", "x = [1, 2, 3, 4]");
  373. fold("x = new Array('a', 1, 2, 'bc', 3, {}, 'abc')",
  374. "x = ['a', 1, 2, 'bc', 3, {}, 'abc']");
  375. fold("x = Array('a', 1, 2, 'bc', 3, {}, 'abc')",
  376. "x = ['a', 1, 2, 'bc', 3, {}, 'abc']");
  377. fold("x = new Array(Array(1, '2', 3, '4'))", "x = [[1, '2', 3, '4']]");
  378. fold("x = Array(Array(1, '2', 3, '4'))", "x = [[1, '2', 3, '4']]");
  379. fold("x = new Array(Object(), Array(\"abc\", Object(), Array(Array())))",
  380. "x = [{}, [\"abc\", {}, [[]]]]");
  381. fold("x = new Array(Object(), Array(\"abc\", Object(), Array(Array())))",
  382. "x = [{}, [\"abc\", {}, [[]]]]");
  383. disableNormalize();
  384. // Cannot fold above when not normalized
  385. foldSame("x = new Array");
  386. foldSame("x = new Array()");
  387. foldSame("x = Array()");
  388. foldSame("x = new Array(0)");
  389. foldSame("x = Array(0)");
  390. foldSame("x = new Array(\"a\")");
  391. foldSame("x = Array(\"a\")");
  392. foldSame("x = new Array(7)");
  393. foldSame("x = Array(7)");
  394. foldSame("x = new Array(foo())");
  395. foldSame("x = Array(foo())");
  396. foldSame("x = new Array(1, 2, 3, 4)");
  397. foldSame("x = Array(1, 2, 3, 4)");
  398. foldSame("x = new Array('a', 1, 2, 'bc', 3, {}, 'abc')");
  399. foldSame("x = Array('a', 1, 2, 'bc', 3, {}, 'abc')");
  400. foldSame("x = new Array(Array(1, '2', 3, '4'))");
  401. foldSame("x = Array(Array(1, '2', 3, '4'))");
  402. foldSame("x = new Array(" +
  403. "Object(), Array(\"abc\", Object(), Array(Array())))");
  404. foldSame("x = new Array(" +
  405. "Object(), Array(\"abc\", Object(), Array(Array())))");
  406. }
  407. public void testMinimizeExprCondition() {
  408. fold("(x ? true : false) && y()", "x&&y()");
  409. fold("(x ? false : true) && y()", "(!x)&&y()");
  410. fold("(x ? true : y) && y()", "(x || y)&&y()");
  411. fold("(x ? y : false) && y()", "(x && y)&&y()");
  412. fold("(x && true) && y()", "x && y()");
  413. fold("(x && false) && y()", "0&&y()");
  414. fold("(x || true) && y()", "1&&y()");
  415. fold("(x || false) && y()", "x&&y()");
  416. }
  417. public void testMinimizeWhileCondition() {
  418. // This test uses constant folding logic, so is only here for completeness.
  419. fold("while(!!true) foo()", "while(1) foo()");
  420. // These test tryMinimizeCondition
  421. fold("while(!!x) foo()", "while(x) foo()");
  422. fold("while(!(!x&&!y)) foo()", "while(x||y) foo()");
  423. fold("while(x||!!y) foo()", "while(x||y) foo()");
  424. fold("while(!(!!x&&y)) foo()", "while(!x||!y) foo()");
  425. fold("while(!(!x&&y)) foo()", "while(x||!y) foo()");
  426. fold("while(!(x||!y)) foo()", "while(!x&&y) foo()");
  427. fold("while(!(x||y)) foo()", "while(!x&&!y) foo()");
  428. fold("while(!(!x||y-z)) foo()", "while(x&&!(y-z)) foo()");
  429. fold("while(!(!(x/y)||z+w)) foo()", "while(x/y&&!(z+w)) foo()");
  430. foldSame("while(!(x+y||z)) foo()");
  431. foldSame("while(!(x&&y*z)) foo()");
  432. fold("while(!(!!x&&y)) foo()", "while(!x||!y) foo()");
  433. fold("while(x&&!0) foo()", "while(x) foo()");
  434. fold("while(x||!1) foo()", "while(x) foo()");
  435. fold("while(!((x,y)&&z)) foo()", "while(!(x,y)||!z) foo()");
  436. }
  437. public void testMinimizeForCondition() {
  438. // This test uses constant folding logic, so is only here for completeness.
  439. // These could be simplified to "for(;;) ..."
  440. fold("for(;!!true;) foo()", "for(;1;) foo()");
  441. // Don't bother with FOR inits as there are normalized out.
  442. fold("for(!!true;;) foo()", "for(!0;;) foo()");
  443. // These test tryMinimizeCondition
  444. fold("for(;!!x;) foo()", "for(;x;) foo()");
  445. // sanity check
  446. foldSame("for(a in b) foo()");
  447. foldSame("for(a in {}) foo()");
  448. foldSame("for(a in []) foo()");
  449. fold("for(a in !!true) foo()", "for(a in !0) foo()");
  450. }
  451. public void testMinimizeCondition_example1() {
  452. // Based on a real failing code sample.
  453. fold("if(!!(f() > 20)) {foo();foo()}", "if(f() > 20){foo();foo()}");
  454. }
  455. public void testFoldLoopBreakLate() {
  456. late = true;
  457. fold("for(;;) if (a) break", "for(;!a;);");
  458. foldSame("for(;;) if (a) { f(); break }");
  459. fold("for(;;) if (a) break; else f()", "for(;!a;) { { f(); } }");
  460. fold("for(;a;) if (b) break", "for(;a && !b;);");
  461. fold("for(;a;) { if (b) break; if (c) break; }", "for(;(a && !b) && !c;);");
  462. // 'while' is normalized to 'for'
  463. enableNormalize(true);
  464. fold("while(true) if (a) break", "for(;1&&!a;);");
  465. }
  466. public void testFoldLoopBreakEarly() {
  467. late = false;
  468. foldSame("for(;;) if (a) break");
  469. foldSame("for(;;) if (a) { f(); break }");
  470. foldSame("for(;;) if (a) break; else f()");
  471. foldSame("for(;a;) if (b) break");
  472. foldSame("for(;a;) { if (b) break; if (c) break; }");
  473. foldSame("while(1) if (a) break");
  474. enableNormalize(true);
  475. foldSame("while(1) if (a) break");
  476. }
  477. public void testFoldConditionalVarDeclaration() {
  478. fold("if(x) var y=1;else y=2", "var y=x?1:2");
  479. fold("if(x) y=1;else var y=2", "var y=x?1:2");
  480. foldSame("if(x) var y = 1; z = 2");
  481. foldSame("if(x||y) y = 1; var z = 2");
  482. foldSame("if(x) { var y = 1; print(y)} else y = 2 ");
  483. foldSame("if(x) var y = 1; else {y = 2; print(y)}");
  484. }
  485. public void testFoldReturnResult() {
  486. fold("function f(){return false;}", "function f(){return !1}");
  487. foldSame("function f(){return null;}");
  488. fold("function f(){return void 0;}",
  489. "function f(){}");
  490. foldSame("function f(){return void foo();}");
  491. fold("function f(){return undefined;}",
  492. "function f(){}");
  493. fold("function f(){if(a()){return undefined;}}",
  494. "function f(){if(a()){}}");
  495. }
  496. public void testFoldStandardConstructors() {
  497. foldSame("new Foo('a')");
  498. foldSame("var x = new goog.Foo(1)");
  499. foldSame("var x = new String(1)");
  500. foldSame("var x = new Number(1)");
  501. foldSame("var x = new Boolean(1)");
  502. enableNormalize();
  503. fold("var x = new Object('a')", "var x = Object('a')");
  504. fold("var x = new RegExp('')", "var x = RegExp('')");
  505. fold("var x = new Error('20')", "var x = Error(\"20\")");
  506. fold("var x = new Array(20)", "var x = Array(20)");
  507. }
  508. public void testSubsituteReturn() {
  509. fold("function f() { while(x) { return }}",
  510. "function f() { while(x) { break }}");
  511. foldSame("function f() { while(x) { return 5 } }");
  512. foldSame("function f() { a: { return 5 } }");
  513. fold("function f() { while(x) { return 5} return 5}",
  514. "function f() { while(x) { break } return 5}");
  515. fold("function f() { while(x) { return x} return x}",
  516. "function f() { while(x) { break } return x}");
  517. fold("function f() { while(x) { if (y) { return }}}",
  518. "function f() { while(x) { if (y) { break }}}");
  519. fold("function f() { while(x) { if (y) { return }} return}",
  520. "function f() { while(x) { if (y) { break }}}");
  521. fold("function f() { while(x) { if (y) { return 5 }} return 5}",
  522. "function f() { while(x) { if (y) { break }} return 5}");
  523. // It doesn't matter if x is changed between them. We are still returning
  524. // x at whatever x value current holds. The whole x = 1 is skipped.
  525. fold("function f() { while(x) { if (y) { return x } x = 1} return x}",
  526. "function f() { while(x) { if (y) { break } x = 1} return x}");
  527. // RemoveUnreachableCode would take care of the useless breaks.
  528. fold("function f() { while(x) { if (y) { return x } return x} return x}",
  529. "function f() { while(x) { if (y) {} break }return x}");
  530. // A break here only breaks out of the inner loop.
  531. foldSame("function f() { while(x) { while (y) { return } } }");
  532. foldSame("function f() { while(1) { return 7} return 5}");
  533. foldSame("function f() {" +
  534. " try { while(x) {return f()}} catch (e) { } return f()}");
  535. foldSame("function f() {" +
  536. " try { while(x) {return f()}} finally {alert(1)} return f()}");
  537. // Both returns has the same handler
  538. fold("function f() {" +
  539. " try { while(x) { return f() } return f() } catch (e) { } }",
  540. "function f() {" +
  541. " try { while(x) { break } return f() } catch (e) { } }");
  542. // We can't fold this because it'll change the order of when foo is called.
  543. foldSame("function f() {" +
  544. " try { while(x) { return foo() } } finally { alert(1) } " +
  545. " return foo()}");
  546. // This is fine, we have no side effect in the return value.
  547. fold("function f() {" +
  548. " try { while(x) { return 1 } } finally { alert(1) } return 1}",
  549. "function f() {" +
  550. " try { while(x) { break } } finally { alert(1) } return 1}"
  551. );
  552. foldSame("function f() { try{ return a } finally { a = 2 } return a; }");
  553. fold(
  554. "function f() { switch(a){ case 1: return a; default: g();} return a;}",
  555. "function f() { switch(a){ case 1: break; default: g();} return a; }");
  556. }
  557. public void testSubsituteBreakForThrow() {
  558. foldSame("function f() { while(x) { throw Error }}");
  559. fold("function f() { while(x) { throw Error } throw Error }",
  560. "function f() { while(x) { break } throw Error}");
  561. foldSame("function f() { while(x) { throw Error(1) } throw Error(2)}");
  562. foldSame("function f() { while(x) { throw Error(1) } return Error(2)}");
  563. foldSame("function f() { while(x) { throw 5 } }");
  564. foldSame("function f() { a: { throw 5 } }");
  565. fold("function f() { while(x) { throw 5} throw 5}",
  566. "function f() { while(x) { break } throw 5}");
  567. fold("function f() { while(x) { throw x} throw x}",
  568. "function f() { while(x) { break } throw x}");
  569. foldSame("function f() { while(x) { if (y) { throw Error }}}");
  570. fold("function f() { while(x) { if (y) { throw Error }} throw Error}",
  571. "function f() { while(x) { if (y) { break }} throw Error}");
  572. fold("function f() { while(x) { if (y) { throw 5 }} throw 5}",
  573. "function f() { while(x) { if (y) { break }} throw 5}");
  574. // It doesn't matter if x is changed between them. We are still throwing
  575. // x at whatever x value current holds. The whole x = 1 is skipped.
  576. fold("function f() { while(x) { if (y) { throw x } x = 1} throw x}",
  577. "function f() { while(x) { if (y) { break } x = 1} throw x}");
  578. // RemoveUnreachableCode would take care of the useless breaks.
  579. fold("function f() { while(x) { if (y) { throw x } throw x} throw x}",
  580. "function f() { while(x) { if (y) {} break }throw x}");
  581. // A break here only breaks out of the inner loop.
  582. foldSame("function f() { while(x) { while (y) { throw Error } } }");
  583. foldSame("function f() { while(1) { throw 7} throw 5}");
  584. foldSame("function f() {" +
  585. " try { while(x) {throw f()}} catch (e) { } throw f()}");
  586. foldSame("function f() {" +
  587. " try { while(x) {throw f()}} finally {alert(1)} throw f()}");
  588. // Both throws has the same handler
  589. fold("function f() {" +
  590. " try { while(x) { throw f() } throw f() } catch (e) { } }",
  591. "function f() {" +
  592. " try { while(x) { break } throw f() } catch (e) { } }");
  593. // We can't fold this because it'll change the order of when foo is called.
  594. foldSame("function f() {" +
  595. " try { while(x) { throw foo() } } finally { alert(1) } " +
  596. " throw foo()}");
  597. // This is fine, we have no side effect in the throw value.
  598. fold("function f() {" +
  599. " try { while(x) { throw 1 } } finally { alert(1) } throw 1}",
  600. "function f() {" +
  601. " try { while(x) { break } } finally { alert(1) } throw 1}"
  602. );
  603. foldSame("function f() { try{ throw a } finally { a = 2 } throw a; }");
  604. fold(
  605. "function f() { switch(a){ case 1: throw a; default: g();} throw a;}",
  606. "function f() { switch(a){ case 1: break; default: g();} throw a; }");
  607. }
  608. public void testRemoveDuplicateReturn() {
  609. fold("function f() { return; }",
  610. "function f(){}");
  611. foldSame("function f() { return a; }");
  612. fold("function f() { if (x) { return a } return a; }",
  613. "function f() { if (x) {} return a; }");
  614. foldSame(
  615. "function f() { try { if (x) { return a } } catch(e) {} return a; }");
  616. foldSame(
  617. "function f() { try { if (x) {} } catch(e) {} return 1; }");
  618. // finally clauses may have side effects
  619. foldSame(
  620. "function f() { try { if (x) { return a } } finally { a++ } return a; }");
  621. // but they don't matter if the result doesn't have side effects and can't
  622. // be affect by side-effects.
  623. fold("function f() { try { if (x) { return 1 } } finally {} return 1; }",
  624. "function f() { try { if (x) {} } finally {} return 1; }");
  625. fold("function f() { switch(a){ case 1: return a; } return a; }",
  626. "function f() { switch(a){ case 1: } return a; }");
  627. fold("function f() { switch(a){ " +
  628. " case 1: return a; case 2: return a; } return a; }",
  629. "function f() { switch(a){ " +
  630. " case 1: break; case 2: } return a; }");
  631. }
  632. public void testRemoveDuplicateThrow() {
  633. foldSame("function f() { throw a; }");
  634. fold("function f() { if (x) { throw a } throw a; }",
  635. "function f() { if (x) {} throw a; }");
  636. foldSame(
  637. "function f() { try { if (x) {throw a} } catch(e) {} throw a; }");
  638. foldSame(
  639. "function f() { try { if (x) {throw 1} } catch(e) {f()} throw 1; }");
  640. foldSame(
  641. "function f() { try { if (x) {throw 1} } catch(e) {f()} throw 1; }");
  642. foldSame(
  643. "function f() { try { if (x) {throw 1} } catch(e) {throw 1}}");
  644. fold(
  645. "function f() { try { if (x) {throw 1} } catch(e) {throw 1} throw 1; }",
  646. "function f() { try { if (x) {throw 1} } catch(e) {} throw 1; }");
  647. // finally clauses may have side effects
  648. foldSame(
  649. "function f() { try { if (x) { throw a } } finally { a++ } throw a; }");
  650. // but they don't matter if the result doesn't have side effects and can't
  651. // be affect by side-effects.
  652. fold("function f() { try { if (x) { throw 1 } } finally {} throw 1; }",
  653. "function f() { try { if (x) {} } finally {} throw 1; }");
  654. fold("function f() { switch(a){ case 1: throw a; } throw a; }",
  655. "function f() { switch(a){ case 1: } throw a; }");
  656. fold("function f() { switch(a){ " +
  657. "case 1: throw a; case 2: throw a; } throw a; }",
  658. "function f() { switch(a){ case 1: break; case 2: } throw a; }");
  659. }
  660. public void testNestedIfCombine() {
  661. fold("if(x)if(y){while(1){}}", "if(x&&y){while(1){}}");
  662. fold("if(x||z)if(y){while(1){}}", "if((x||z)&&y){while(1){}}");
  663. fold("if(x)if(y||z){while(1){}}", "if((x)&&(y||z)){while(1){}}");
  664. foldSame("if(x||z)if(y||z){while(1){}}");
  665. fold("if(x)if(y){if(z){while(1){}}}", "if(x&&y&&z){while(1){}}");
  666. }
  667. public void testFoldTrueFalse() {
  668. fold("x = true", "x = !0");
  669. fold("x = false", "x = !1");
  670. }
  671. public void testIssue291() {
  672. fold("if (true) { f.onchange(); }", "if (1) f.onchange();");
  673. foldSame("if (f) { f.onchange(); }");
  674. foldSame("if (f) { f.bar(); } else { f.onchange(); }");
  675. fold("if (f) { f.bonchange(); }", "f && f.bonchange();");
  676. foldSame("if (f) { f['x'](); }");
  677. }
  678. public void testUndefined() {
  679. foldSame("var x = undefined");
  680. foldSame("function f(f) {var undefined=2;var x = undefined;}");
  681. this.enableNormalize();
  682. fold("var x = undefined", "var x=void 0");
  683. foldSame(
  684. "var undefined = 1;" +
  685. "function f() {var undefined=2;var x = undefined;}");
  686. foldSame("function f(undefined) {}");
  687. foldSame("try {} catch(undefined) {}");
  688. foldSame("for (undefined in {}) {}");
  689. foldSame("undefined++;");
  690. fold("undefined += undefined;", "undefined += void 0;");
  691. }
  692. public void testSplitCommaExpressions() {
  693. late = false;
  694. // Don't try to split in expressions.
  695. foldSame("while (foo(), !0) boo()");
  696. foldSame("var a = (foo(), !0);");
  697. foldSame("a = (foo(), !0);");
  698. // Don't try to split COMMA under LABELs.
  699. foldSame("a:a(),b()");
  700. fold("(x=2), foo()", "x=2; foo()");
  701. fold("foo(), boo();", "foo(); boo()");
  702. fold("(a(), b()), (c(), d());", "a(); b(); c(); d();");
  703. fold("foo(), true", "foo();1");
  704. fold("function x(){foo(), !0}", "function x(){foo(); 1}");
  705. }
  706. public void testComma1() {
  707. late = false;
  708. fold("1, 2", "1; 1");
  709. late = true;
  710. foldSame("1, 2");
  711. }
  712. public void testComma2() {
  713. late = false;
  714. test("1, a()", "1; a()");
  715. late = true;
  716. foldSame("1, a()");
  717. }
  718. public void testComma3() {
  719. late = false;
  720. test("1, a(), b()", "1; a(); b()");
  721. late = true;
  722. foldSame("1, a(), b()");
  723. }
  724. public void testComma4() {
  725. late = false;
  726. test("a(), b()", "a();b()");
  727. late = true;
  728. foldSame("a(), b()");
  729. }
  730. public void testComma5() {
  731. late = false;
  732. test("a(), b(), 1", "a();b();1");
  733. late = true;
  734. foldSame("a(), b(), 1");
  735. }
  736. public void testObjectLiteral() {
  737. test("({})", "1");
  738. test("({a:1})", "1");
  739. testSame("({a:foo()})");
  740. testSame("({'a':foo()})");
  741. }
  742. public void testArrayLiteral() {
  743. test("([])", "1");
  744. test("([1])", "1");
  745. test("([a])", "1");
  746. testSame("([foo()])");
  747. }
  748. public void testStringArraySplitting() {
  749. testSame("var x=['1','2','3','4']");
  750. testSame("var x=['1','2','3','4','5']");
  751. test("var x=['1','2','3','4','5','6']",
  752. "var x='1,2,3,4,5,6'.split(',')");
  753. test("var x=['1','2','3','4','5','6','7']",
  754. "var x='1,2,3,4,5,6,7'.split(',')");
  755. test("var x=[',',',',',',',',',',',']",
  756. "var x=', , , , , ,'.split(' ')");
  757. test("var x=[',',' ',',',',',',',',']",
  758. "var x=',; ;,;,;,;,'.split(';')");
  759. test("var x=[',',' ',',',',',',',',']",
  760. "var x=',; ;,;,;,;,'.split(';')");
  761. }
  762. public void testRemoveElseCause() {
  763. test("function f() {" +
  764. " if(x) return 1;" +
  765. " else if(x) return 2;" +
  766. " else if(x) return 3 }",
  767. "function f() {" +
  768. " if(x) return 1;" +
  769. "{ if(x) return 2;" +
  770. "{ if(x) return 3 } } }");
  771. }
  772. public void testRemoveElseCause1() {
  773. test("function f() { if (x) throw 1; else f() }",
  774. "function f() { if (x) throw 1; { f() } }");
  775. }
  776. public void testRemoveElseCause2() {
  777. test("function f() { if (x) return 1; else f() }",
  778. "function f() { if (x) return 1; { f() } }");
  779. test("function f() { if (x) return; else f() }",
  780. "function f() { if (x) {} else { f() } }");
  781. // This case is handled by minimize exit points.
  782. testSame("function f() { if (x) return; f() }");
  783. }
  784. public void testRemoveElseCause3() {
  785. testSame("function f() { a:{if (x) break a; else f() } }");
  786. testSame("function f() { if (x) { a:{ break a } } else f() }");
  787. testSame("function f() { if (x) a:{ break a } else f() }");
  788. }
  789. public void testRemoveElseCause4() {
  790. testSame("function f() { if (x) { if (y) { return 1; } } else f() }");
  791. }
  792. public void testBindToCall1() {
  793. test("(goog.bind(f))()", "f()");
  794. test("(goog.bind(f,a))()", "f.call(a)");
  795. test("(goog.bind(f,a,b))()", "f.call(a,b)");
  796. test("(goog.bind(f))(a)", "f(a)");
  797. test("(goog.bind(f,a))(b)", "f.call(a,b)");
  798. test("(goog.bind(f,a,b))(c)", "f.call(a,b,c)");
  799. test("(goog.partial(f))()", "f()");
  800. test("(goog.partial(f,a))()", "f(a)");
  801. test("(goog.partial(f,a,b))()", "f(a,b)");
  802. test("(goog.partial(f))(a)", "f(a)");
  803. test("(goog.partial(f,a))(b)", "f(a,b)");
  804. test("(goog.partial(f,a,b))(c)", "f(a,b,c)");
  805. test("((function(){}).bind())()", "((function(){}))()");
  806. test("((function(){}).bind(a))()", "((function(){})).call(a)");
  807. test("((function(){}).bind(a,b))()", "((function(){})).call(a,b)");
  808. test("((function(){}).bind())(a)", "((function(){}))(a)");
  809. test("((function(){}).bind(a))(b)", "((function(){})).call(a,b)");
  810. test("((function(){}).bind(a,b))(c)", "((function(){})).call(a,b,c)");
  811. // Without using type information we don't know "f" is a function.
  812. testSame("(f.bind())()");
  813. testSame("(f.bind(a))()");
  814. testSame("(f.bind())(a)");
  815. testSame("(f.bind(a))(b)");
  816. // Don't rewrite if the bind isn't the immediate call target
  817. testSame("(goog.bind(f)).call(g)");
  818. }
  819. public void testBindToCall2() {
  820. test("(goog$bind(f))()", "f()");
  821. test("(goog$bind(f,a))()", "f.call(a)");
  822. test("(goog$bind(f,a,b))()", "f.call(a,b)");
  823. test("(goog$bind(f))(a)", "f(a)");
  824. test("(goog$bind(f,a))(b)", "f.call(a,b)");
  825. test("(goog$bind(f,a,b))(c)", "f.call(a,b,c)");
  826. test("(goog$partial(f))()", "f()");
  827. test("(goog$partial(f,a))()", "f(a)");
  828. test("(goog$partial(f,a,b))()", "f(a,b)");
  829. test("(goog$partial(f))(a)", "f(a)");
  830. test("(goog$partial(f,a))(b)", "f(a,b)");
  831. test("(goog$partial(f,a,b))(c)", "f(a,b,c)");
  832. // Don't rewrite if the bind isn't the immediate call target
  833. testSame("(goog$bind(f)).call(g)");
  834. }
  835. public void testBindToCall3() {
  836. // TODO(johnlenz): The code generator wraps free calls with (0,...) to
  837. // prevent leaking "this", but the parser doesn't unfold it, making a
  838. // AST comparison fail. For now do a string comparison to validate the
  839. // correct code is in fact generated.
  840. // The FREE call wrapping should be moved out of the code generator
  841. // and into a denormalizing pass.
  842. new StringCompareTestCase().testBindToCall3();
  843. }
  844. public void testSimpleFunctionCall() {
  845. test("var a = String(23)", "var a = '' + 23");
  846. test("var a = String('hello')", "var a = '' + 'hello'");
  847. }
  848. private static class StringCompareTestCase extends CompilerTestCase {
  849. StringCompareTestCase() {
  850. super("", false);
  851. }
  852. @Override
  853. protected CompilerPass getProcessor(Compiler compiler) {
  854. CompilerPass peepholePass =
  855. new PeepholeOptimizationsPass(compiler,
  856. new PeepholeSubstituteAlternateSyntax(false));
  857. return peepholePass;
  858. }
  859. public void testBindToCall3() {
  860. test("(goog.bind(f.m))()", "(0,f.m)()");
  861. test("(goog.bind(f.m,a))()", "f.m.call(a)");
  862. test("(goog.bind(f.m))(a)", "(0,f.m)(a)");
  863. test("(goog.bind(f.m,a))(b)", "f.m.call(a,b)");
  864. test("(goog.partial(f.m))()", "(0,f.m)()");
  865. test("(goog.partial(f.m,a))()", "(0,f.m)(a)");
  866. test("(goog.partial(f.m))(a)", "(0,f.m)(a)");
  867. test("(goog.partial(f.m,a))(b)", "(0,f.m)(a,b)");
  868. // Without using type information we don't know "f" is a function.
  869. testSame("f.m.bind()()");
  870. testSame("f.m.bind(a)()");
  871. testSame("f.m.bind()(a)");
  872. testSame("f.m.bind(a)(b)");
  873. // Don't rewrite if the bind isn't the immediate call target
  874. testSame("goog.bind(f.m).call(g)");
  875. }
  876. }
  877. }