From dc33facfcb7899c0422cb14b08dddfcf06b1c949 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 26 May 2017 16:08:51 +0800 Subject: [PATCH] fix `dead_code` on block-scoped `function` under "use strict" (#2006) Technically not part of ES5, but commonly used code exists in the wild. --- lib/compress.js | 2 +- test/compress/dead-code.js | 87 ++++++++++++++++++++++- test/compress/issue-1034.js | 134 ++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 3 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index a2f8f267..bd017e10 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1222,7 +1222,7 @@ merge(Compressor.prototype, { target.push(node); return true; } - if (node instanceof AST_Defun) { + if (node instanceof AST_Defun && (node === stat || !compressor.has_directive("use strict"))) { target.push(node); return true; } diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index 00dac069..20e154b2 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -31,7 +31,7 @@ dead_code_2_should_warn: { function f() { g(); x = 10; - throw "foo"; + throw new Error("foo"); // completely discarding the `if` would introduce some // bugs. UglifyJS v1 doesn't deal with this issue; in v2 // we copy any declarations to the upper scope. @@ -46,16 +46,60 @@ dead_code_2_should_warn: { })(); } } + f(); } expect: { function f() { g(); x = 10; - throw "foo"; + throw new Error("foo"); var x; function g(){}; } + f(); } + expect_stdout: true + node_version: "<=4" +} + +dead_code_2_should_warn_strict: { + options = { + dead_code: true + }; + input: { + "use strict"; + function f() { + g(); + x = 10; + throw new Error("foo"); + // completely discarding the `if` would introduce some + // bugs. UglifyJS v1 doesn't deal with this issue; in v2 + // we copy any declarations to the upper scope. + if (x) { + y(); + var x; + function g(){}; + // but nested declarations should not be kept. + (function(){ + var q; + function y(){}; + })(); + } + } + f(); + } + expect: { + "use strict"; + function f() { + g(); + x = 10; + throw new Error("foo"); + var x; + } + f(); + } + expect_stdout: true + node_version: "=4" } dead_code_constant_boolean_should_warn_more: { @@ -78,6 +122,7 @@ dead_code_constant_boolean_should_warn_more: { foo(); var moo; } + bar(); } expect: { var foo; @@ -86,8 +131,46 @@ dead_code_constant_boolean_should_warn_more: { // as for the for, it should keep: var x = 10, y; var moo; + bar(); + } + expect_stdout: true + node_version: "<=4" +} + +dead_code_constant_boolean_should_warn_more_strict: { + options = { + dead_code : true, + loops : true, + booleans : true, + conditionals : true, + evaluate : true, + side_effects : true, + }; + input: { + "use strict"; + while (!((foo && bar) || (x + "0"))) { + console.log("unreachable"); + var foo; + function bar() {} + } + for (var x = 10, y; x && (y || x) && (!typeof x); ++x) { + asdf(); + foo(); + var moo; + } + bar(); + } + expect: { + "use strict"; + var foo; + // nothing for the while + // as for the for, it should keep: + var x = 10, y; + var moo; + bar(); } expect_stdout: true + node_version: ">=4" } try_catch_finally: { diff --git a/test/compress/issue-1034.js b/test/compress/issue-1034.js index 57c584ab..28e47f07 100644 --- a/test/compress/issue-1034.js +++ b/test/compress/issue-1034.js @@ -116,3 +116,137 @@ non_hoisted_function_after_return_2b: { "WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]", ] } + +non_hoisted_function_after_return_strict: { + options = { + hoist_funs: false, dead_code: true, conditionals: true, comparisons: true, + evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true, + if_return: true, join_vars: true, cascade: true, side_effects: true + } + input: { + "use strict"; + function foo(x) { + if (x) { + return bar(); + not_called1(); + } else { + return baz(); + not_called2(); + } + function bar() { return 7; } + return not_reached; + function UnusedFunction() {} + function baz() { return 8; } + } + console.log(foo(0), foo(1)); + } + expect: { + "use strict"; + function foo(x) { + return x ? bar() : baz(); + function bar() { return 7 } + function baz() { return 8 } + } + console.log(foo(0), foo(1)); + } + expect_stdout: "8 7" + expect_warnings: [ + 'WARN: Dropping unreachable code [test/compress/issue-1034.js:131,16]', + "WARN: Dropping unreachable code [test/compress/issue-1034.js:134,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:137,12]", + "WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:138,21]" + ] +} + +non_hoisted_function_after_return_2a_strict: { + options = { + hoist_funs: false, dead_code: true, conditionals: true, comparisons: true, + evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true, + if_return: true, join_vars: true, cascade: true, side_effects: true, + collapse_vars: false, passes: 2, warnings: "verbose" + } + input: { + "use strict"; + function foo(x) { + if (x) { + return bar(1); + var a = not_called(1); + } else { + return bar(2); + var b = not_called(2); + } + var c = bar(3); + function bar(x) { return 7 - x; } + function nope() {} + return b || c; + } + console.log(foo(0), foo(1)); + } + expect: { + "use strict"; + function foo(x) { + return bar(x ? 1 : 2); + function bar(x) { + return 7 - x; + } + } + console.log(foo(0), foo(1)); + } + expect_stdout: "5 6" + expect_warnings: [ + "WARN: Dropping unreachable code [test/compress/issue-1034.js:173,16]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:173,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:176,16]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:176,16]", + "WARN: Dropping unused variable a [test/compress/issue-1034.js:173,20]", + "WARN: Dropping unused function nope [test/compress/issue-1034.js:180,21]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:178,12]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:181,12]", + "WARN: Dropping unused variable b [test/compress/issue-1034.js:176,20]", + "WARN: Dropping unused variable c [test/compress/issue-1034.js:178,16]", + ] +} + +non_hoisted_function_after_return_2b_strict: { + options = { + hoist_funs: false, dead_code: true, conditionals: true, comparisons: true, + evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true, + if_return: true, join_vars: true, cascade: true, side_effects: true, + collapse_vars: false + } + input: { + "use strict"; + function foo(x) { + if (x) { + return bar(1); + } else { + return bar(2); + var b; + } + var c = bar(3); + function bar(x) { + return 7 - x; + } + return b || c; + } + console.log(foo(0), foo(1)); + } + expect: { + "use strict"; + function foo(x) { + return bar(x ? 1 : 2); + function bar(x) { return 7 - x; } + } + console.log(foo(0), foo(1)); + } + expect_stdout: "5 6" + expect_warnings: [ + // duplicate warnings no longer emitted + "WARN: Dropping unreachable code [test/compress/issue-1034.js:225,16]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:225,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:227,12]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:227,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:231,12]", + ] +} -- 2.34.1