/* * Copyright 2008 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; /** * Test for {@link UnreachableCodeElimination}. * */ public final class UnreachableCodeEliminationTest extends CompilerTestCase { private boolean removeNoOpStatements = true; @Override protected CompilerPass getProcessor(Compiler compiler) { return new UnreachableCodeElimination(compiler, removeNoOpStatements); } @Override public void setUp() throws Exception { super.setUp(); enableComputeSideEffects(); removeNoOpStatements = true; } public void testRemoveUnreachableCode() { // switch statement with stuff after "return" test("function foo(){switch(foo){case 1:x=1;return;break;" + "case 2:{x=2;return;break}default:}}", "function foo(){switch(foo){case 1:x=1;return;" + "case 2:{x=2}default:}}"); // if/else statements with returns test("function bar(){if(foo)x=1;else if(bar){return;x=2}" + "else{x=3;return;x=4}return 5;x=5}", "function bar(){if(foo)x=1;else if(bar){return}" + "else{x=3;return}return 5}"); // if statements without blocks test("function foo(){if(x==3)return;x=4;y++;while(y==4){return;x=3}}", "function foo(){if(x==3)return;x=4;y++;while(y==4){return}}"); // for/do/while loops test("function baz(){for(i=0;i<n;i++){x=3;break;x=4}" + "do{x=2;break;x=4}while(x==4);" + "while(i<4){x=3;return;x=6}}", "function baz(){for(i=0;i<n;){x=3;break}" + "do{x=2;break}while(x==4);" + "while(i<4){x=3;return}}"); // return statements on the same level as conditionals test("function foo(){if(x==3){return}return 5;while(y==4){x++;return;x=4}}", "function foo(){if(x==3){return}return 5}"); // return statements on the same level as conditionals test("function foo(){return 3;for(;y==4;){x++;return;x=4}}", "function foo(){return 3}"); // try/catch statements test("function foo(){try{x=3;return x+1;x=5}catch(e){x=4;return 5;x=5}}", "function foo(){try{x=3;return x+1}catch(e){x=4;return 5}}"); // try/finally statements test("function foo(){try{x=3;return x+1;x=5}finally{x=4;return 5;x=5}}", "function foo(){try{x=3;return x+1}finally{x=4;return 5}}"); // try/catch/finally statements test("function foo(){try{x=3;return x+1;x=5}catch(e){x=3;return;x=2}" + "finally{x=4;return 5;x=5}}", "function foo(){try{x=3;return x+1}catch(e){x=3;return}" + "finally{x=4;return 5}}"); // test a combination of blocks test("function foo(){x=3;if(x==4){x=5;return;x=6}else{x=7}return 5;x=3}", "function foo(){x=3;if(x==4){x=5;return}else{x=7}return 5}"); // test removing multiple statements test("function foo() { return 1; var x = 2; var y = 10; return 2;}", "function foo() { var y; var x; return 1}"); test("function foo() { return 1; x = 2; y = 10; return 2;}", "function foo(){ return 1}"); } public void testRemoveUselessNameStatements() { test("a;", ""); test("a.b;", ""); test("a.b.MyClass.prototype.memberName;", ""); } public void testRemoveUselessStrings() { test("'a';", ""); } public void testNoRemoveUseStrict() { test("'use strict';", "'use strict'"); } public void testNoRemoveUselessNameStatements() { removeNoOpStatements = false; testSame("a;"); testSame("a.b;"); testSame("a.b.MyClass.prototype.memberName;"); } public void testRemoveDo() { testSame("do { print(1); break } while(1)"); test("while(1) { break; do { print(1); break } while(1) }", "while(1) { break; do {} while(1) }"); } public void testRemoveUselessLiteralValueStatements() { test("true;", ""); test("'hi';", ""); test("if (x) 1;", ""); test("while (x) 1;", "while (x);"); test("do 1; while (x);", "do ; while (x);"); test("for (;;) 1;", "for (;;);"); test("switch(x){case 1:true;case 2:'hi';default:true}", "switch(x){case 1:case 2:default:}"); } public void testConditionalDeadCode() { test("function f() { if (1) return 5; else return 5; x = 1}", "function f() { if (1) return 5; else return 5; }"); } public void testSwitchCase() { test("function f() { switch(x) { default: return 5; foo()}}", "function f() { switch(x) { default: return 5;}}"); testSame("function f() { switch(x) { default: return; case 1: foo(); bar()}}"); test("function f() { switch(x) { default: return; case 1: return 5;bar()}}", "function f() { switch(x) { default: return; case 1: return 5;}}"); } public void testTryCatchFinally() { testSame("try {foo()} catch (e) {bar()}"); testSame("try { try {foo()} catch (e) {bar()}} catch (x) {bar()}"); test("try {var x = 1} catch (e) {e()}", "try {var x = 1} finally {}"); test("try {var x = 1} catch (e) {e()} finally {x()}", " try {var x = 1} finally {x()}"); test("try {var x = 1} catch (e) {e()} finally {}", " try {var x = 1} finally {}"); testSame("try {var x = 1} finally {x()}"); testSame("try {var x = 1} finally {}"); test("function f() {return; try{var x = 1}catch(e){} }", "function f() {var x;}"); } public void testRemovalRequiresRedeclaration() { test("while(1) { break; var x = 1}", "var x; while(1) { break } "); test("while(1) { break; var x=1; var y=1}", "var y; var x; while(1) { break } "); } public void testAssignPropertyOnCreatedObject() { testSame("this.foo = 3;"); testSame("a.foo = 3;"); testSame("bar().foo = 3;"); testSame("({}).foo = bar();"); testSame("(new X()).foo = 3;"); test("({}).foo = 3;", ""); test("(function() {}).prototype.toString = function(){};", ""); test("(function() {}).prototype['toString'] = function(){};", ""); test("(function() {}).prototype[f] = function(){};", ""); } public void testUselessUnconditionalReturn() { test("function foo() { return }", " function foo() { }"); test("function foo() { return; return; x=1 }", "function foo() { }"); test("function foo() { return; return; var x=1}", "function foo() {var x}"); test("function foo() { return; function bar() {} }", "function foo() { function bar() {} }" ); testSame("function foo() { return 5 }"); test("function f() {switch (a) { case 'a': return}}", "function f() {switch (a) { case 'a': }}"); testSame("function f() {switch (a) { case 'a': case foo(): }}"); testSame("function f() {switch (a) {" + " default: return; case 'a': alert(1)}}"); testSame("function f() {switch (a) {" + " case 'a': return; default: alert(1)}}"); } public void testUselessUnconditionalContinue() { test("for(;1;) {continue}", " for(;1;) {}"); test("for(;0;) {continue}", " for(;0;) {}"); testSame("X: for(;1;) { for(;1;) { if (x()) {continue X} x = 1}}"); test("for(;1;) { X: for(;1;) { if (x()) {continue X} }}", "for(;1;) { X: for(;1;) { if (x()) {}}}"); test("do { continue } while(1);", "do { } while(1);"); } public void testUselessUnconditionalBreak() { test("switch (a) { case 'a': break }", "switch (a) { case 'a': }"); test("switch (a) { case 'a': break; case foo(): }", "switch (a) { case 'a': case foo(): }"); test("switch (a) { default: break; case 'a': }", "switch (a) { default: case 'a': }"); testSame("switch (a) { case 'a': alert(a); break; default: alert(a); }"); testSame("switch (a) { default: alert(a); break; case 'a': alert(a); }"); test("X: {switch (a) { case 'a': break X}}", "X: {switch (a) { case 'a': }}"); testSame("X: {switch (a) { case 'a': if (a()) {break X} a = 1}}"); test("X: {switch (a) { case 'a': if (a()) {break X}}}", "X: {switch (a) { case 'a': if (a()) {}}}"); test("X: {switch (a) { case 'a': if (a()) {break X}}}", "X: {switch (a) { case 'a': if (a()) {}}}"); testSame("do { break } while(1);"); testSame("for(;1;) { break }"); } // These tests all require the analysis to go to a fixpoint in order to pass public void testIteratedRemoval() { test("switch (a) { case 'a': break; case 'b': break; case 'c': break }", " switch (a) { case 'a': case 'b': case 'c': }"); test("function foo() {" + " switch (a) { case 'a':return; case 'b':return; case 'c':return }}", " function foo() { switch (a) { case 'a': case 'b': case 'c': }}"); test("for (;;) {\n" + " switch (a) {\n" + " case 'a': continue;\n" + " case 'b': continue;\n" + " case 'c': continue;\n" + " }\n" + " }", " for (;;) { switch (a) { case 'a': case 'b': case 'c': } }"); test("function foo() { if (x) { return; } if (x) { return; } x; }", " function foo() {}"); test("var x; \n" + " out: { \n" + " try { break out; } catch (e) { break out; } \n" + " x = undefined; \n" + " }", " var x; out: {}"); } public void testIssue311() { test("function a(b) {\n" + " switch (b.v) {\n" + " case 'SWITCH':\n" + " if (b.i >= 0) {\n" + " return b.o;\n" + " } else {\n" + " return;\n" + " }\n" + " break;\n" + " }\n" + "}", "function a(b) {\n" + " switch (b.v) {\n" + " case 'SWITCH':\n" + " if (b.i >= 0) {\n" + " return b.o;\n" + " } else {\n" + " }\n" + " }\n" + "}"); } public void testIssue4177428a() { testSame( "f = function() {\n" + " var action;\n" + " a: {\n" + " var proto = null;\n" + " try {\n" + " proto = new Proto\n" + " } finally {\n" + " action = proto;\n" + " break a\n" + // Keep this... " }\n" + " }\n" + " alert(action)\n" + // and this. "};"); } public void testIssue4177428b() { testSame( "f = function() {\n" + " var action;\n" + " a: {\n" + " var proto = null;\n" + " try {\n" + " try {\n" + " proto = new Proto\n" + " } finally {\n" + " action = proto;\n" + " break a\n" + // Keep this... " }\n" + " } finally {\n" + " }\n" + " }\n" + " alert(action)\n" + // and this. "};"); } public void testIssue4177428c() { testSame( "f = function() {\n" + " var action;\n" + " a: {\n" + " var proto = null;\n" + " try {\n" + " } finally {\n" + " try {\n" + " proto = new Proto\n" + " } finally {\n" + " action = proto;\n" + " break a\n" + // Keep this... " }\n" + " }\n" + " }\n" + " alert(action)\n" + // and this. "};"); } public void testIssue4177428_continue() { testSame( "f = function() {\n" + " var action;\n" + " a: do {\n" + " var proto = null;\n" + " try {\n" + " proto = new Proto\n" + " } finally {\n" + " action = proto;\n" + " continue a\n" + // Keep this... " }\n" + " } while(false)\n" + " alert(action)\n" + // and this. "};"); } public void testIssue4177428_return() { test( "f = function() {\n" + " var action;\n" + " a: {\n" + " var proto = null;\n" + " try {\n" + " proto = new Proto\n" + " } finally {\n" + " action = proto;\n" + " return\n" + // Keep this... " }\n" + " }\n" + " alert(action)\n" + // and remove this. "};", "f = function() {\n" + " var action;\n" + " a: {\n" + " var proto = null;\n" + " try {\n" + " proto = new Proto\n" + " } finally {\n" + " action = proto;\n" + " return\n" + " }\n" + " }\n" + "};" ); } public void testIssue4177428_multifinally() { testSame( "a: {\n" + " try {\n" + " try {\n" + " } finally {\n" + " break a;\n" + " }\n" + " } finally {\n" + " x = 1;\n" + " }\n" + "}"); } public void testIssue5215541_deadVarDeclar() { testSame("throw 1; var x"); testSame("throw 1; function x() {}"); testSame("throw 1; var x; var y;"); test("throw 1; var x = foo", "var x; throw 1"); } public void testForInLoop() { testSame("for(var x in y) {}"); } public void testDontRemoveBreakInTryFinally() throws Exception { testSame("function f() {b:try{throw 9} finally {break b} return 1;}"); } public void testDontRemoveBreakInTryFinallySwitch() throws Exception { testSame("function f() {b:try{throw 9} finally {" + "switch(x) {case 1: break b} } return 1;}"); } public void testIssue1001() throws Exception { test("function f(x) { x.property = 3; } f({})", "function f(x) { x.property = 3; }"); test("function f(x) { x.property = 3; } new f({})", "function f(x) { x.property = 3; }"); } }