From bd6dee52abd607244d89f606a729e811a9b0b478 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 8 Mar 2017 03:31:51 +0800 Subject: [PATCH 01/10] fix return from recursive IIFE (#1570) `side-effects` did not account for IIFEs being able to reference itself thus making its return value potentially significant --- lib/compress.js | 3 ++- test/compress/issue-1569.js | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 test/compress/issue-1569.js diff --git a/lib/compress.js b/lib/compress.js index 8bbbc3f6aa7..85b457e35ed 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2100,7 +2100,8 @@ merge(Compressor.prototype, { def(AST_This, return_null); def(AST_Call, function(compressor, first_in_statement){ if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) { - if (this.expression instanceof AST_Function) { + if (this.expression instanceof AST_Function + && (!this.expression.name || !this.expression.name.definition().references.length)) { var node = this.clone(); node.expression = node.expression.process_expression(false); return node; diff --git a/test/compress/issue-1569.js b/test/compress/issue-1569.js new file mode 100644 index 00000000000..5f0bca34b88 --- /dev/null +++ b/test/compress/issue-1569.js @@ -0,0 +1,19 @@ +inner_reference: { + options = { + side_effects: true, + } + input: { + !function f(a) { + return a && f(a - 1) + a; + }(42); + !function g(a) { + return a; + }(42); + } + expect: { + !function f(a) { + return a && f(a - 1) + a; + }(42); + !void 0; + } +} From dedbeeff15b53a8ab79f9d477ac414e9a6c1ef16 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 8 Mar 2017 05:07:05 +0800 Subject: [PATCH 02/10] plan B for IE8 do-while semi-colon fix (#1572) - omitting trailing semi-colon in do-while breaks non-browser parser, e.g. uglify-js 1.x - trailing semi-colon only breaks IE8 if followed by `else` or `while` - always use braces in do-while body to workaround 2nd case with no size loss in compression fixes #1568 --- lib/output.js | 36 ++++++++++++------------------------ test/compress/loops.js | 10 +++++----- 2 files changed, 17 insertions(+), 29 deletions(-) diff --git a/lib/output.js b/lib/output.js index 7e16644d2f7..767abd4db22 100644 --- a/lib/output.js +++ b/lib/output.js @@ -777,16 +777,14 @@ function OutputStream(options) { DEFPRINT(AST_Do, function(self, output){ output.print("do"); output.space(); - self._do_print_body(output); + make_block(self.body, output); output.space(); output.print("while"); output.space(); output.with_parens(function(){ self.condition.print(output); }); - if (output.option("beautify") && output.option("screw_ie8")) { - output.semicolon(); - } + output.semicolon(); }); DEFPRINT(AST_While, function(self, output){ output.print("while"); @@ -906,10 +904,10 @@ function OutputStream(options) { /* -----[ if ]----- */ function make_then(self, output) { - if (output.option("bracketize")) { - make_block(self.body, output); - return; - } + var b = self.body; + if (output.option("bracketize") + || !output.option("screw_ie8") && b instanceof AST_Do) + return make_block(b, output); // The squeezer replaces "block"-s that contain only a single // statement with the statement itself; technically, the AST // is correct, but this can create problems when we output an @@ -917,9 +915,7 @@ function OutputStream(options) { // IF *without* an ELSE block (then the outer ELSE would refer // to the inner IF). This function checks for this case and // adds the block brackets if needed. - if (!self.body) - return output.force_semicolon(); - var b = self.body; + if (!b) return output.force_semicolon(); while (true) { if (b instanceof AST_If) { if (!b.alternative) { @@ -1328,15 +1324,7 @@ function OutputStream(options) { function force_statement(stat, output) { if (output.option("bracketize")) { - if (!stat || stat instanceof AST_EmptyStatement) - output.print("{}"); - else if (stat instanceof AST_BlockStatement) - stat.print(output); - else output.with_block(function(){ - output.indent(); - stat.print(output); - output.newline(); - }); + make_block(stat, output); } else { if (!stat || stat instanceof AST_EmptyStatement) output.force_semicolon(); @@ -1385,11 +1373,11 @@ function OutputStream(options) { }; function make_block(stmt, output) { - if (stmt instanceof AST_BlockStatement) { + if (!stmt || stmt instanceof AST_EmptyStatement) + output.print("{}"); + else if (stmt instanceof AST_BlockStatement) stmt.print(output); - return; - } - output.with_block(function(){ + else output.with_block(function(){ output.indent(); stmt.print(output); output.newline(); diff --git a/test/compress/loops.js b/test/compress/loops.js index 2d04e235b5d..df3011cda22 100644 --- a/test/compress/loops.js +++ b/test/compress/loops.js @@ -257,7 +257,7 @@ issue_186: { else bar(); } - expect_exact: 'var x=3;if(foo())do do alert(x);while(--x)while(x)else bar();' + expect_exact: 'var x=3;if(foo())do{do{alert(x)}while(--x)}while(x);else bar();' } issue_186_ie8: { @@ -276,7 +276,7 @@ issue_186_ie8: { else bar(); } - expect_exact: 'var x=3;if(foo())do do alert(x);while(--x)while(x)else bar();' + expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else bar();' } issue_186_beautify: { @@ -295,7 +295,7 @@ issue_186_beautify: { else bar(); } - expect_exact: 'var x = 3;\n\nif (foo()) do do alert(x); while (--x); while (x); else bar();' + expect_exact: 'var x = 3;\n\nif (foo()) do {\n do {\n alert(x);\n } while (--x);\n} while (x); else bar();' } issue_186_beautify_ie8: { @@ -314,7 +314,7 @@ issue_186_beautify_ie8: { else bar(); } - expect_exact: 'var x = 3;\n\nif (foo()) do do alert(x); while (--x) while (x) else bar();' + expect_exact: 'var x = 3;\n\nif (foo()) {\n do {\n do {\n alert(x);\n } while (--x);\n } while (x);\n} else bar();' } issue_186_bracketize: { @@ -394,5 +394,5 @@ issue_186_beautify_bracketize_ie8: { else bar(); } - expect_exact: 'var x = 3;\n\nif (foo()) {\n do {\n do {\n alert(x);\n } while (--x)\n } while (x)\n} else {\n bar();\n}' + expect_exact: 'var x = 3;\n\nif (foo()) {\n do {\n do {\n alert(x);\n } while (--x);\n } while (x);\n} else {\n bar();\n}' } From 3ee55748d466f36aff54383b53fb3deca144de97 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 8 Mar 2017 06:00:51 +0800 Subject: [PATCH 03/10] only run benchmark & jetstream on CI (#1571) --- .travis.yml | 5 +++- test/mocha/benchmark.js | 24 ------------------ test/mocha/release.js | 54 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 25 deletions(-) delete mode 100644 test/mocha/benchmark.js create mode 100644 test/mocha/release.js diff --git a/.travis.yml b/.travis.yml index b91a80e9b84..b2aef3dc15b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,13 @@ language: node_js before_install: "npm install -g npm" node_js: - - "0.12" - "0.10" + - "0.12" - "4" - "6" + - "7" +env: + - UGLIFYJS_TEST_ALL=1 matrix: fast_finish: true sudo: false diff --git a/test/mocha/benchmark.js b/test/mocha/benchmark.js deleted file mode 100644 index c2c7c02cc25..00000000000 --- a/test/mocha/benchmark.js +++ /dev/null @@ -1,24 +0,0 @@ -var assert = require("assert"); -var exec = require("child_process").exec; - -describe("test/benchmark.js", function() { - this.timeout(120000); - var command = '"' + process.argv[0] + '" test/benchmark.js '; - [ - "-b", - "-b bracketize", - "-m", - "-mc passes=3", - "-mc passes=3,toplevel", - "-mc passes=3,unsafe", - "-mc keep_fargs=false,passes=3", - "-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto", - ].forEach(function(args) { - it("Should pass with options " + args, function(done) { - exec(command + args, function(err) { - if (err) throw err; - done(); - }); - }); - }); -}); diff --git a/test/mocha/release.js b/test/mocha/release.js new file mode 100644 index 00000000000..3b2d9a72f7c --- /dev/null +++ b/test/mocha/release.js @@ -0,0 +1,54 @@ +var assert = require("assert"); +var spawn = require("child_process").spawn; + +if (!process.env.UGLIFYJS_TEST_ALL) return; + +function run(command, args, done) { + var id = setInterval(function() { + process.stdout.write("\0"); + }, 5 * 60 * 1000); + spawn(command, args, { + stdio: "ignore" + }).on("exit", function(code) { + clearInterval(id); + assert.strictEqual(code, 0); + done(); + }); +} + +describe("test/benchmark.js", function() { + this.timeout(5 * 60 * 1000); + [ + "-b", + "-b bracketize", + "-m", + "-mc passes=3", + "-mc passes=3,toplevel", + "-mc passes=3,unsafe", + "-mc keep_fargs=false,passes=3", + "-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto", + ].forEach(function(options) { + it("Should pass with options " + options, function(done) { + var args = options.split(/ /); + args.unshift("test/benchmark.js"); + run(process.argv[0], args, done); + }); + }); +}); + +describe("test/jetstream.js", function() { + this.timeout(20 * 60 * 1000); + it("Should install phantomjs-prebuilt", function(done) { + run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done); + }); + [ + "-mc warnings=false", + "-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto,warnings=false", + ].forEach(function(options) { + it("Should pass with options " + options, function(done) { + var args = options.split(/ /); + args.unshift("test/jetstream.js"); + run(process.argv[0], args, done); + }); + }); +}); From c7cdcf06a65b70b557894c1680fc099d3c7aca6a Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 8 Mar 2017 12:39:57 +0800 Subject: [PATCH 04/10] fix function name eliminiation (#1576) Function expression can be assigned to a variable and be given a name. Ensure function name is the reduced variable before clearing it out. fixes #1573 fixes #1575 --- lib/compress.js | 3 ++- test/compress/reduce_vars.js | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/compress.js b/lib/compress.js index 85b457e35ed..f423fdd452c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2611,7 +2611,8 @@ merge(Compressor.prototype, { if (compressor.option("unused") && def.references.length == 1 && compressor.find_parent(AST_Scope) === def.scope) { - if (!compressor.option("keep_fnames")) { + if (!compressor.option("keep_fnames") + && exp.name && exp.name.definition() === def) { exp.name = null; } self.expression = exp; diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 53e281522e3..10dc9d98cc8 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -1122,3 +1122,25 @@ defun_label: { }(); } } + +double_reference: { + options = { + reduce_vars: true, + unused: true, + } + input: { + function f() { + var g = function g() { + g(); + }; + g(); + } + } + expect: { + function f() { + (function g() { + g(); + })(); + } + } +} From 344d11d591ca18416ce6fe7444e451609ee14689 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 8 Mar 2017 12:41:22 +0800 Subject: [PATCH 05/10] v2.8.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 086b6910d78..6030d798644 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "homepage": "http://lisperator.net/uglifyjs", "author": "Mihai Bazon (http://lisperator.net/)", "license": "BSD-2-Clause", - "version": "2.8.8", + "version": "2.8.9", "engines": { "node": ">=0.8.0" }, From 711f88dcb49bc0daf0548f3ec240f680e05dfc27 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 8 Mar 2017 18:37:32 +0800 Subject: [PATCH 06/10] scan assignment value in drop_unused() (#1578) those were not optimised for `unused` before, which made it necessary for `reduce_vars` to have separate steps for `keep_fnames` docs update by @kzc closes #1577 --- README.md | 8 ++++---- lib/compress.js | 5 +---- test/compress/drop-unused.js | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f880fd7ba79..a33e0b32454 100644 --- a/README.md +++ b/README.md @@ -391,11 +391,11 @@ to set `true`; it's effectively a shortcut for `foo=true`). - `cascade` -- small optimization for sequences, transform `x, x` into `x` and `x = something(), x` into `x = something()` -- `collapse_vars` -- default `false`. Collapse single-use `var` and `const` - definitions when possible. +- `collapse_vars` -- Collapse single-use `var` and `const` definitions + when possible. -- `reduce_vars` -- default `false`. Improve optimization on variables assigned - with and used as constant values. +- `reduce_vars` -- Improve optimization on variables assigned with and + used as constant values. - `warnings` -- display warnings when dropping unreachable code or unused declarations etc. diff --git a/lib/compress.js b/lib/compress.js index f423fdd452c..302f8f561be 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1840,6 +1840,7 @@ merge(Compressor.prototype, { } if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) { var def = node.definitions.filter(function(def){ + if (def.value) def.value = def.value.transform(tt); if (def.name.definition().id in in_use_ids) return true; var w = { name : def.name.name, @@ -2611,10 +2612,6 @@ merge(Compressor.prototype, { if (compressor.option("unused") && def.references.length == 1 && compressor.find_parent(AST_Scope) === def.scope) { - if (!compressor.option("keep_fnames") - && exp.name && exp.name.definition() === def) { - exp.name = null; - } self.expression = exp; } } diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 20dab3b9e46..9c9605612c6 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -700,3 +700,28 @@ issue_1539: { } } } + +vardef_value: { + options = { + keep_fnames: false, + reduce_vars: true, + unused: true, + } + input: { + function f() { + function g(){ + return x(); + } + var a = g(); + return a(42); + } + } + expect: { + function f() { + var a = function(){ + return x(); + }(); + return a(42); + } + } +} From 80e81765cf073be2f62010cc9d63bf6c3106a714 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 8 Mar 2017 18:56:01 +0800 Subject: [PATCH 07/10] explain how to make a proper bug report (#1579) fixes #1574 --- .github/ISSUE_TEMPLATE.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..6c62c47fcc1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,7 @@ +- Bug report or feature request? +- [ ] `uglify-js` version (`uglifyjs -V`) +- [ ] JavaScript input - ideally as small as possible. +- [ ] The `uglifyjs` CLI command executed or `minify()` options used. +- [ ] An example of JavaScript output produced and/or the error or warning. + +Note: the release version of `uglify-js` only supports ES5. Those wishing to minify ES6 should use the experimental [`harmony`](https://github.com/mishoo/UglifyJS2#harmony) branch. \ No newline at end of file From aa80ee349d7c534a950ce6cba00c59ca0f7b5629 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 8 Mar 2017 19:19:54 +0800 Subject: [PATCH 08/10] remove checkboxes from Issues template --- .github/ISSUE_TEMPLATE.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6c62c47fcc1..31ba69d322d 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,7 +1,7 @@ - Bug report or feature request? -- [ ] `uglify-js` version (`uglifyjs -V`) -- [ ] JavaScript input - ideally as small as possible. -- [ ] The `uglifyjs` CLI command executed or `minify()` options used. -- [ ] An example of JavaScript output produced and/or the error or warning. +- `uglify-js` version (`uglifyjs -V`) +- JavaScript input - ideally as small as possible. +- The `uglifyjs` CLI command executed or `minify()` options used. +- An example of JavaScript output produced and/or the error or warning. -Note: the release version of `uglify-js` only supports ES5. Those wishing to minify ES6 should use the experimental [`harmony`](https://github.com/mishoo/UglifyJS2#harmony) branch. \ No newline at end of file +Note: the release version of `uglify-js` only supports ES5. Those wishing to minify ES6 should use the experimental [`harmony`](https://github.com/mishoo/UglifyJS2#harmony) branch. From 7e465d4a01f2cbcae24e1c60ee96969c14130287 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 9 Mar 2017 05:22:27 +0800 Subject: [PATCH 09/10] scan RHS of dropped assignments (#1581) - similar case as #1578 but against #1450 instead - fix `this` binding in #1450 as well closes #1580 --- lib/compress.js | 18 ++++++++---------- test/compress/drop-unused.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 302f8f561be..f6b76ec2359 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1901,17 +1901,15 @@ merge(Compressor.prototype, { } return node; } - if (drop_vars && assign_as_unused) { - var n = node; - while (n instanceof AST_Assign - && n.operator == "=" - && n.left instanceof AST_SymbolRef) { - var def = n.left.definition(); - if (def.id in in_use_ids - || self.variables.get(def.name) !== def) break; - n = n.right; + if (drop_vars && assign_as_unused + && node instanceof AST_Assign + && node.operator == "=" + && node.left instanceof AST_SymbolRef) { + var def = node.left.definition(); + if (!(def.id in in_use_ids) + && self.variables.get(def.name) === def) { + return maintain_this_binding(tt.parent(), node, node.right.transform(tt)); } - if (n !== node) return n; } if (node instanceof AST_For) { descend(node, this); diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 9c9605612c6..728557a63bd 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -725,3 +725,39 @@ vardef_value: { } } } + +assign_binding: { + options = { + cascade: true, + side_effects: true, + unused: true, + } + input: { + function f() { + var a; + a = f.g, a(); + } + } + expect: { + function f() { + (0, f.g)(); + } + } +} + +assign_chain: { + options = { + unused: true, + } + input: { + function f() { + var a, b; + x = a = y = b = 42; + } + } + expect: { + function f() { + x = y = 42; + } + } +} From e9920f7ca162ce062cc481b876be293d7324a714 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 9 Mar 2017 05:48:06 +0800 Subject: [PATCH 10/10] v2.8.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6030d798644..c56d8f86f2b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "homepage": "http://lisperator.net/uglifyjs", "author": "Mihai Bazon (http://lisperator.net/)", "license": "BSD-2-Clause", - "version": "2.8.9", + "version": "2.8.10", "engines": { "node": ">=0.8.0" },