From eb48a035e7880e73e7fe4f23727775cff365ffbc Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 29 Mar 2017 01:00:21 +0800 Subject: [PATCH 01/13] fix corner case in `unused` (#1718) When fixing catch-related issue in #1715, it tries to optimise for duplicate definitions but did not take anonymous functions into account. Remove such optimisation for now and we can cover this as a more general rule later. --- lib/compress.js | 3 +-- test/compress/drop-unused.js | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 64c654dd..3c0fc452 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1896,8 +1896,7 @@ merge(Compressor.prototype, { if (def.value) def.value = def.value.transform(tt); var sym = def.name.definition(); if (sym.id in in_use_ids) return true; - if (sym.orig[0] instanceof AST_SymbolCatch - && sym.scope.parent_scope.find_variable(def.name).orig[0] === def.name) { + if (sym.orig[0] instanceof AST_SymbolCatch) { def.value = def.value && def.value.drop_side_effect_free(compressor); return true; } diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index aa875ece..d0b87764 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -931,3 +931,30 @@ issue_1715_3: { } expect_stdout: "1" } + +issue_1715_4: { + options = { + unused: true, + } + input: { + var a = 1; + !function a() { + a++; + try {} catch (a) { + var a; + } + }(); + console.log(a); + } + expect: { + var a = 1; + !function() { + a++; + try {} catch (a) { + var a; + } + }(); + console.log(a); + } + expect_stdout: "1" +} From ec7f37f314eb834d36eb64dddb8b5fe0934e50b1 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 29 Mar 2017 01:27:24 +0800 Subject: [PATCH 02/13] remove UGLIFY_DEBUG (#1720) fixes #1719 --- test/mocha/directives.js | 13 +++++++------ test/run-tests.js | 2 -- tools/exports.js | 4 ---- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/test/mocha/directives.js b/test/mocha/directives.js index bc763ae0..5189f1ad 100644 --- a/test/mocha/directives.js +++ b/test/mocha/directives.js @@ -176,7 +176,7 @@ describe("Directives", function() { }); it("Should test EXPECT_DIRECTIVE RegExp", function() { - var tests = [ + [ ["", true], ["'test';", true], ["'test';;", true], @@ -185,11 +185,12 @@ describe("Directives", function() { ["'tests'; \n\t", true], ["'tests';\n\n", true], ["\n\n\"use strict\";\n\n", true] - ]; - - for (var i = 0; i < tests.length; i++) { - assert.strictEqual(uglify.EXPECT_DIRECTIVE.test(tests[i][0]), tests[i][1], tests[i][0]); - } + ].forEach(function(test) { + var out = uglify.OutputStream(); + out.print(test[0]); + out.print_string("", null, true); + assert.strictEqual(out.get() === test[0] + ';""', test[1], test[0]); + }); }); it("Should only print 2 semicolons spread over 2 lines in beautify mode", function() { diff --git a/test/run-tests.js b/test/run-tests.js index 3d291416..4870873d 100755 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -1,7 +1,5 @@ #! /usr/bin/env node -global.UGLIFY_DEBUG = true; - var U = require("../tools/node"); var path = require("path"); var fs = require("fs"); diff --git a/tools/exports.js b/tools/exports.js index d83739d5..09acc13e 100644 --- a/tools/exports.js +++ b/tools/exports.js @@ -17,7 +17,3 @@ exports["string_template"] = string_template; exports["tokenizer"] = tokenizer; exports["is_identifier"] = is_identifier; exports["SymbolDef"] = SymbolDef; - -if (global.UGLIFY_DEBUG) { - exports["EXPECT_DIRECTIVE"] = EXPECT_DIRECTIVE; -} From ae740b933fe9cad714f5a9aac625ae2857d5a04a Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 29 Mar 2017 03:13:30 +0800 Subject: [PATCH 03/13] v2.8.18 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ada98ea..3e7cc626 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.17", + "version": "2.8.18", "engines": { "node": ">=0.8.0" }, From fef0bf9ee0367f07dfbca26b144c2995c2b5db5f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 29 Mar 2017 04:40:05 +0800 Subject: [PATCH 04/13] improve beautified output of switch blocks (#1721) --- lib/output.js | 24 ++++++++++++------------ test/compress/switch.js | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/lib/output.js b/lib/output.js index c0f10523..5c11088b 100644 --- a/lib/output.js +++ b/lib/output.js @@ -960,24 +960,24 @@ function OutputStream(options) { self.expression.print(output); }); output.space(); - if (self.body.length > 0) output.with_block(function(){ - self.body.forEach(function(stmt, i){ - if (i) output.newline(); + var last = self.body.length - 1; + if (last < 0) output.print("{}"); + else output.with_block(function(){ + self.body.forEach(function(branch, i){ output.indent(true); - stmt.print(output); + branch.print(output); + if (i < last && branch.body.length > 0) + output.newline(); }); }); - else output.print("{}"); }); AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){ - if (this.body.length > 0) { + output.newline(); + this.body.forEach(function(stmt){ + output.indent(); + stmt.print(output); output.newline(); - this.body.forEach(function(stmt){ - output.indent(); - stmt.print(output); - output.newline(); - }); - } + }); }); DEFPRINT(AST_Default, function(self, output){ output.print("default:"); diff --git a/test/compress/switch.js b/test/compress/switch.js index 5c12449c..82d725f2 100644 --- a/test/compress/switch.js +++ b/test/compress/switch.js @@ -680,3 +680,44 @@ issue_1705_3: { } expect_stdout: true } + +beautify: { + beautify = { + beautify: true, + } + input: { + switch (a) { + case 0: + case 1: + break; + case 2: + default: + } + switch (b) { + case 3: + foo(); + bar(); + default: + break; + } + } + expect_exact: [ + "switch (a) {", + " case 0:", + " case 1:", + " break;", + "", + " case 2:", + " default:", + "}", + "", + "switch (b) {", + " case 3:", + " foo();", + " bar();", + "", + " default:", + " break;", + "}", + ] +} From 09f77c7d4d37350102c36b270b553f45e706d0c8 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 29 Mar 2017 18:31:55 +0800 Subject: [PATCH 05/13] output optimal representations of NaN & Infinity (#1723) - move these optimisations out from `Compressor` to `OutputStream` - fixes behaviour inconsistency when running uglified code from global or module levels due to redefinition --- lib/compress.js | 33 ++++++-------- lib/output.js | 19 +++++++- test/compress/conditionals.js | 8 ++-- test/compress/evaluate.js | 16 +++---- test/compress/issue-1105.js | 17 +++---- test/compress/issue-597.js | 86 ++++++++++++++++++++++++++++++++++- test/compress/properties.js | 2 +- 7 files changed, 137 insertions(+), 44 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 3c0fc452..66a6a18b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -413,18 +413,17 @@ merge(Compressor.prototype, { value: val }); case "number": - if (isNaN(val)) { - return make_node(AST_NaN, orig); - } - - if ((1 / val) < 0) { - return make_node(AST_UnaryPrefix, orig, { + if (isNaN(val)) return make_node(AST_NaN, orig); + if (isFinite(val)) { + return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, { operator: "-", expression: make_node(AST_Number, orig, { value: -val }) - }); + }) : make_node(AST_Number, orig, { value: val }); } - - return make_node(AST_Number, orig, { value: val }); + return val < 0 ? make_node(AST_UnaryPrefix, orig, { + operator: "-", + expression: make_node(AST_Infinity, orig) + }) : make_node(AST_Infinity, orig); case "boolean": return make_node(val ? AST_True : AST_False, orig); case "undefined": @@ -3023,7 +3022,9 @@ merge(Compressor.prototype, { } } // avoids infinite recursion of numerals - if (self.operator != "-" || !(self.expression instanceof AST_Number)) { + if (self.operator != "-" + || !(self.expression instanceof AST_Number + || self.expression instanceof AST_Infinity)) { var ev = self.evaluate(compressor); if (ev !== self) { ev = make_node_from_constant(ev, self).optimize(compressor); @@ -3455,9 +3456,9 @@ merge(Compressor.prototype, { case "undefined": return make_node(AST_Undefined, self).optimize(compressor); case "NaN": - return make_node(AST_NaN, self).optimize(compressor); + return make_node(AST_NaN, self); case "Infinity": - return make_node(AST_Infinity, self).optimize(compressor); + return make_node(AST_Infinity, self); } } if (compressor.option("evaluate") && compressor.option("reduce_vars")) { @@ -3485,14 +3486,6 @@ merge(Compressor.prototype, { return self; }); - OPT(AST_Infinity, function (self, compressor) { - return make_node(AST_Binary, self, { - operator : '/', - left : make_node(AST_Number, self, {value: 1}), - right : make_node(AST_Number, self, {value: 0}) - }); - }); - OPT(AST_Undefined, function(self, compressor){ if (compressor.option("unsafe")) { var scope = compressor.find_parent(AST_Scope); diff --git a/lib/output.js b/lib/output.js index 5c11088b..d71f6aac 100644 --- a/lib/output.js +++ b/lib/output.js @@ -592,6 +592,13 @@ function OutputStream(options) { || p instanceof AST_Call && p.expression === this; }); + PARENS([ AST_Infinity, AST_NaN ], function(output){ + var p = output.parent(); + return p instanceof AST_PropAccess && p.expression === this + || p instanceof AST_Call && p.expression === this + || p instanceof AST_Unary && p.operator != "+" && p.operator != "-"; + }); + PARENS(AST_Seq, function(output){ var p = output.parent(); return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4) @@ -1254,10 +1261,18 @@ function OutputStream(options) { }); DEFPRINT(AST_Hole, noop); DEFPRINT(AST_Infinity, function(self, output){ - output.print("Infinity"); + output.print("1"); + output.space(); + output.print("/"); + output.space(); + output.print("0"); }); DEFPRINT(AST_NaN, function(self, output){ - output.print("NaN"); + output.print("0"); + output.space(); + output.print("/"); + output.space(); + output.print("0"); }); DEFPRINT(AST_This, function(self, output){ output.print("this"); diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js index e7ea2bb2..54d4264d 100644 --- a/test/compress/conditionals.js +++ b/test/compress/conditionals.js @@ -840,8 +840,8 @@ equality_conditionals_false: { f(0, true, 0), f(1, 2, 3), f(1, null, 3), - f(NaN), - f(NaN, "foo"); + f(0/0), + f(0/0, "foo"); } expect_stdout: true } @@ -888,8 +888,8 @@ equality_conditionals_true: { f(0, true, 0), f(1, 2, 3), f(1, null, 3), - f(NaN), - f(NaN, "foo"); + f(0/0), + f(0/0, "foo"); } expect_stdout: true } diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index 7a562055..35b6b925 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -52,7 +52,7 @@ and: { a = 7; a = false; - a = NaN; + a = 0/0; a = 0; a = void 0; a = null; @@ -67,7 +67,7 @@ and: { a = 6 << condition && -4.5; a = condition && false; - a = console.log("b") && NaN; + a = console.log("b") && 0/0; a = console.log("c") && 0; a = 2 * condition && void 0; a = condition + 3 && null; @@ -149,7 +149,7 @@ or: { a = 6 << condition || -4.5; a = condition || false; - a = console.log("b") || NaN; + a = console.log("b") || 0/0; a = console.log("c") || 0; a = 2 * condition || void 0; a = condition + 3 || null; @@ -196,8 +196,8 @@ negative_zero: { console.log( -0, 0, - 1 / (-0), - 1 / (-0) + -1/0, + -1/0 ); } expect_stdout: true @@ -217,8 +217,8 @@ positive_zero: { console.log( 0, -0, - 1 / (0), - 1 / (0) + 1/0, + 1/0 ); } expect_stdout: true @@ -533,7 +533,7 @@ unsafe_array: { [1, 2, 3, a][0] + 1, 2, 3, - NaN, + 0/0, "1,21", 5, (void 0)[1] + 1 diff --git a/test/compress/issue-1105.js b/test/compress/issue-1105.js index 28f1557a..f9412165 100644 --- a/test/compress/issue-1105.js +++ b/test/compress/issue-1105.js @@ -195,11 +195,12 @@ assorted_Infinity_NaN_undefined_in_with_scope: { sequences: false, } input: { + var f = console.log; var o = { undefined : 3, NaN : 4, Infinity : 5, - } + }; if (o) { f(undefined, void 0); f(NaN, 0/0); @@ -216,25 +217,25 @@ assorted_Infinity_NaN_undefined_in_with_scope: { } } expect: { - var o = { + var f = console.log, o = { undefined : 3, NaN : 4, Infinity : 5 - } + }; if (o) { f(void 0, void 0); - f(NaN, NaN); + f(0/0, 0/0); f(1/0, 1/0); - f(-(1/0), -(1/0)); - f(NaN, NaN); + f(-1/0, -1/0); + f(0/0, 0/0); } with (o) { f(undefined, void 0); f(NaN, 0/0); f(Infinity, 1/0); - f(-Infinity, -(1/0)); + f(-Infinity, -1/0); f(9 + undefined, 9 + void 0); } } + expect_stdout: true } - diff --git a/test/compress/issue-597.js b/test/compress/issue-597.js index f243223a..3a501532 100644 --- a/test/compress/issue-597.js +++ b/test/compress/issue-597.js @@ -6,7 +6,7 @@ NaN_and_Infinity_must_have_parens: { } expect: { (1/0).toString(); - NaN.toString(); // transformation to 0/0 dropped + (0/0).toString(); } } @@ -23,3 +23,87 @@ NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: { NaN.toString(); } } + +beautify_off_1: { + options = { + evaluate: true, + } + beautify = { + beautify: false, + } + input: { + var NaN; + console.log( + null, + undefined, + Infinity, + NaN, + Infinity * undefined, + Infinity.toString(), + NaN.toString(), + (Infinity * undefined).toString() + ); + } + expect_exact: "var NaN;console.log(null,void 0,1/0,NaN,0/0,(1/0).toString(),NaN.toString(),(0/0).toString());" + expect_stdout: true +} + +beautify_off_2: { + options = { + evaluate: true, + } + beautify = { + beautify: false, + } + input: { + console.log( + null.toString(), + undefined.toString() + ); + } + expect_exact: "console.log(null.toString(),(void 0).toString());" +} + +beautify_on_1: { + options = { + evaluate: true, + } + beautify = { + beautify: true, + } + input: { + var NaN; + console.log( + null, + undefined, + Infinity, + NaN, + Infinity * undefined, + Infinity.toString(), + NaN.toString(), + (Infinity * undefined).toString() + ); + } + expect_exact: [ + "var NaN;", + "", + "console.log(null, void 0, 1 / 0, NaN, 0 / 0, (1 / 0).toString(), NaN.toString(), (0 / 0).toString());", + ] + expect_stdout: true +} + +beautify_on_2: { + options = { + evaluate: true, + } + beautify = { + beautify: true, + } + input: { + console.log( + null.toString(), + undefined.toString() + ); + } + expect_exact: "console.log(null.toString(), (void 0).toString());" +} diff --git a/test/compress/properties.js b/test/compress/properties.js index 29bdfe2a..376fb9e2 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -77,7 +77,7 @@ sub_properties: { a[3.14] = 3; a.if = 4; a["foo bar"] = 5; - a[NaN] = 6; + a[0/0] = 6; a[null] = 7; a[void 0] = 8; } From 2e41cd6394ad389080b446c20f519fc3920f81c7 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 29 Mar 2017 20:53:03 +0800 Subject: [PATCH 06/13] fix missing parentheses around NaN/Infinity shorthands (#1726) fixes #1724 fixes #1725 --- lib/output.js | 4 +++- test/compress/issue-597.js | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/output.js b/lib/output.js index d71f6aac..c8c8739f 100644 --- a/lib/output.js +++ b/lib/output.js @@ -596,7 +596,9 @@ function OutputStream(options) { var p = output.parent(); return p instanceof AST_PropAccess && p.expression === this || p instanceof AST_Call && p.expression === this - || p instanceof AST_Unary && p.operator != "+" && p.operator != "-"; + || p instanceof AST_Unary && p.operator != "+" && p.operator != "-" + || p instanceof AST_Binary && p.right === this + && (p.operator == "/" || p.operator == "%"); }); PARENS(AST_Seq, function(output){ diff --git a/test/compress/issue-597.js b/test/compress/issue-597.js index 3a501532..2aaaf3f1 100644 --- a/test/compress/issue-597.js +++ b/test/compress/issue-597.js @@ -107,3 +107,27 @@ beautify_on_2: { } expect_exact: "console.log(null.toString(), (void 0).toString());" } + +issue_1724: { + input: { + var a = 0; + ++a % Infinity | Infinity ? a++ : 0; + console.log(a); + } + expect: { + var a = 0; + ++a % (1/0) | 1/0 ? a++ : 0; + console.log(a); + } + expect_stdout: "2" +} + +issue_1725: { + input: { + ([].length === 0) % Infinity ? console.log("PASS") : console.log("FAIL"); + } + expect: { + (0 === [].length) % (1/0) ? console.log("PASS") : console.log("FAIL"); + } + expect_stdout: "PASS" +} From f1a833a7aa4204411c33c7419e94e8c6c87afe23 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 29 Mar 2017 22:08:26 +0800 Subject: [PATCH 07/13] speed up `equivalent_to()` and `AST_Switch` (#1727) --- lib/compress.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 66a6a18b..786fc567 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -196,8 +196,7 @@ merge(Compressor.prototype, { }); AST_Node.DEFMETHOD("equivalent_to", function(node){ - // XXX: this is a rather expensive way to test two node's equivalence: - return this.print_to_string() == node.print_to_string(); + return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string(); }); AST_Node.DEFMETHOD("process_expression", function(insert) { @@ -2518,7 +2517,6 @@ merge(Compressor.prototype, { self.expression = best_of_expression(expression, self.expression); } if (!compressor.option("dead_code")) return self; - var prev_block; var decl = []; var body = []; var default_branch; @@ -2547,14 +2545,16 @@ merge(Compressor.prototype, { } } if (aborts(branch)) { - var block = make_node(AST_BlockStatement, branch, branch).print_to_string(); - if (!fallthrough && prev_block === block) body[body.length - 1].body = []; + if (body.length > 0 && !fallthrough) { + var prev = body[body.length - 1]; + if (prev.body.length == branch.body.length + && make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node(AST_BlockStatement, branch, branch))) + prev.body = []; + } body.push(branch); - prev_block = block; fallthrough = false; } else { body.push(branch); - prev_block = null; fallthrough = true; } } From beb9659778b04e46556b58bd1d093f3938be9dce Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 29 Mar 2017 23:27:35 +0800 Subject: [PATCH 08/13] speed up IIFE elimination (#1728) - `side_effects` will clean up inner statements, so checking for an empty function body should suffice - drop side effects when dropping `return` from statement --- lib/compress.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 786fc567..e36ff893 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -199,7 +199,7 @@ merge(Compressor.prototype, { return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string(); }); - AST_Node.DEFMETHOD("process_expression", function(insert) { + AST_Node.DEFMETHOD("process_expression", function(insert, compressor) { var self = this; var tt = new TreeTransformer(function(node) { if (insert && node instanceof AST_SimpleStatement) { @@ -208,6 +208,12 @@ merge(Compressor.prototype, { }); } if (!insert && node instanceof AST_Return) { + if (compressor) { + var value = node.value && node.value.drop_side_effect_free(compressor, true); + return value ? make_node(AST_SimpleStatement, node, { + body: value + }) : make_node(AST_EmptyStatement, node); + } return make_node(AST_SimpleStatement, node, { body: node.value || make_node(AST_Undefined, node) }); @@ -2153,7 +2159,7 @@ merge(Compressor.prototype, { 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); + node.expression = node.expression.process_expression(false, compressor); return node; } return this; @@ -2866,11 +2872,9 @@ merge(Compressor.prototype, { return AST_Seq.from_array(args).transform(compressor); } } - if (compressor.option("side_effects")) { - if (!AST_Block.prototype.has_side_effects.call(exp, compressor)) { - var args = self.args.concat(make_node(AST_Undefined, self)); - return AST_Seq.from_array(args).transform(compressor); - } + if (compressor.option("side_effects") && all(exp.body, is_empty)) { + var args = self.args.concat(make_node(AST_Undefined, self)); + return AST_Seq.from_array(args).transform(compressor); } } if (compressor.option("drop_console")) { From 0f910ee25c3e644baf043f217b2fe91df8dc67ac Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 30 Mar 2017 00:13:46 +0800 Subject: [PATCH 09/13] improve tests from #1726 (#1729) --- test/compress/issue-597.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/compress/issue-597.js b/test/compress/issue-597.js index 2aaaf3f1..987bcacc 100644 --- a/test/compress/issue-597.js +++ b/test/compress/issue-597.js @@ -114,11 +114,7 @@ issue_1724: { ++a % Infinity | Infinity ? a++ : 0; console.log(a); } - expect: { - var a = 0; - ++a % (1/0) | 1/0 ? a++ : 0; - console.log(a); - } + expect_exact: "var a=0;++a%(1/0)|1/0?a++:0;console.log(a);" expect_stdout: "2" } @@ -126,8 +122,6 @@ issue_1725: { input: { ([].length === 0) % Infinity ? console.log("PASS") : console.log("FAIL"); } - expect: { - (0 === [].length) % (1/0) ? console.log("PASS") : console.log("FAIL"); - } + expect_exact: '(0===[].length)%(1/0)?console.log("PASS"):console.log("FAIL");' expect_stdout: "PASS" } From 7bea38a05dbe357434001fe59dbe06bb659a585f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 30 Mar 2017 12:16:58 +0800 Subject: [PATCH 10/13] optimize try-catch-finally (#1731) - eliminate empty blocks - flatten out if try-block does not throw --- lib/compress.js | 9 ++++++++ test/compress/dead-code.js | 42 ++++++++++++++++++++++++++++++++++++ test/compress/drop-unused.js | 32 ++++++++++++++++++++------- test/compress/issue-1673.js | 2 ++ test/compress/screw-ie8.js | 8 +++---- 5 files changed, 81 insertions(+), 12 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index e36ff893..be760152 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2606,6 +2606,15 @@ merge(Compressor.prototype, { OPT(AST_Try, function(self, compressor){ self.body = tighten_body(self.body, compressor); + if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null; + if (all(self.body, is_empty)) { + var body = []; + if (self.bcatch) extract_declarations_from_unreachable_code(compressor, self.bcatch, body); + if (self.bfinally) body = body.concat(self.bfinally.body); + return body.length > 0 ? make_node(AST_BlockStatement, self, { + body: body + }).optimize(compressor) : make_node(AST_EmptyStatement, self); + } return self; }); diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index 7772e76e..bb72451c 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -214,3 +214,45 @@ dead_code_const_annotation_complex_scope: { } expect_stdout: true } + +try_catch_finally: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + } + input: { + var a = 1; + !function() { + try { + if (false) throw x; + } catch (a) { + var a = 2; + console.log("FAIL"); + } finally { + a = 3; + console.log("PASS"); + } + }(); + try { + console.log(a); + } finally { + } + } + expect: { + var a = 1; + !function() { + var a; + a = 3; + console.log("PASS"); + }(); + try { + console.log(a); + } finally { + } + } + expect_stdout: [ + "PASS", + "1", + ] +} diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index d0b87764..28118fc4 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -853,7 +853,9 @@ issue_1715_1: { var a = 1; function f() { a++; - try {} catch (a) { + try { + x(); + } catch (a) { var a; } } @@ -864,7 +866,9 @@ issue_1715_1: { var a = 1; function f() { a++; - try {} catch (a) { + try { + x(); + } catch (a) { var a; } } @@ -882,7 +886,9 @@ issue_1715_2: { var a = 1; function f() { a++; - try {} catch (a) { + try { + x(); + } catch (a) { var a = 2; } } @@ -893,7 +899,9 @@ issue_1715_2: { var a = 1; function f() { a++; - try {} catch (a) { + try { + x(); + } catch (a) { var a; } } @@ -911,7 +919,9 @@ issue_1715_3: { var a = 1; function f() { a++; - try {} catch (a) { + try { + console; + } catch (a) { var a = 2 + x(); } } @@ -922,7 +932,9 @@ issue_1715_3: { var a = 1; function f() { a++; - try {} catch (a) { + try { + console; + } catch (a) { var a = x(); } } @@ -940,7 +952,9 @@ issue_1715_4: { var a = 1; !function a() { a++; - try {} catch (a) { + try { + x(); + } catch (a) { var a; } }(); @@ -950,7 +964,9 @@ issue_1715_4: { var a = 1; !function() { a++; - try {} catch (a) { + try { + x(); + } catch (a) { var a; } }(); diff --git a/test/compress/issue-1673.js b/test/compress/issue-1673.js index 4628e37c..081b0e5f 100644 --- a/test/compress/issue-1673.js +++ b/test/compress/issue-1673.js @@ -70,6 +70,7 @@ side_effects_finally: { function f() { function g() { try { + x(); } catch (e) { } finally { console.log("PASS"); @@ -83,6 +84,7 @@ side_effects_finally: { function f() { (function() { try { + x(); } catch (e) { } finally { console.log("PASS"); diff --git a/test/compress/screw-ie8.js b/test/compress/screw-ie8.js index fc492112..68d1a364 100644 --- a/test/compress/screw-ie8.js +++ b/test/compress/screw-ie8.js @@ -204,13 +204,13 @@ issue_1586_1: { input: { function f() { try { + x(); } catch (err) { console.log(err.message); } } } - expect_exact: "function f(){try{}catch(c){console.log(c.message)}}" - expect_stdout: true + expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}" } issue_1586_2: { @@ -223,11 +223,11 @@ issue_1586_2: { input: { function f() { try { + x(); } catch (err) { console.log(err.message); } } } - expect_exact: "function f(){try{}catch(c){console.log(c.message)}}" - expect_stdout: true + expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}" } From 7cb1adf455f8ab440e1971ae41265c1f7f9a806a Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 30 Mar 2017 16:09:00 +0800 Subject: [PATCH 11/13] remove paranthesis for `-(x*y)` (#1732) --- lib/compress.js | 7 +++++++ test/compress/numbers.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/lib/compress.js b/lib/compress.js index be760152..ac7ea357 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3034,6 +3034,13 @@ merge(Compressor.prototype, { })).optimize(compressor); } } + if (e instanceof AST_Binary + && (self.operator == "+" || self.operator == "-") + && (e.operator == "*" || e.operator == "/" || e.operator == "%")) { + self.expression = e.left; + e.left = self; + return e.optimize(compressor); + } // avoids infinite recursion of numerals if (self.operator != "-" || !(self.expression instanceof AST_Number diff --git a/test/compress/numbers.js b/test/compress/numbers.js index ea439ecc..946a7f2d 100644 --- a/test/compress/numbers.js +++ b/test/compress/numbers.js @@ -168,3 +168,37 @@ issue_1710: { } expect_stdout: true } + +unary_binary_parenthesis: { + input: { + var v = [ 0, 1, NaN, Infinity, null, undefined, true, false, "", "foo", /foo/ ]; + v.forEach(function(x) { + v.forEach(function(y) { + console.log( + +(x*y), + +(x/y), + +(x%y), + -(x*y), + -(x/y), + -(x%y) + ); + }); + }); + } + expect: { + var v = [ 0, 1, 0/0, 1/0, null, void 0, true, false, "", "foo", /foo/ ]; + v.forEach(function(x) { + v.forEach(function(y) { + console.log( + +x*y, + +x/y, + +x%y, + -x*y, + -x/y, + -x%y + ); + }); + }); + } + expect_stdout: true +} From c595b84032b3083b87a976c8387010bf6074ad93 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 31 Mar 2017 02:57:47 +0800 Subject: [PATCH 12/13] fix catch symbol mangling (#1734) Only need to look up the immediate non-block/catch scope for the same-name special case. fixes #1733 --- lib/scope.js | 8 ++- test/compress/issue-1733.js | 97 +++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 test/compress/issue-1733.js diff --git a/lib/scope.js b/lib/scope.js index 025d4ca3..df0a7e00 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -79,9 +79,7 @@ SymbolDef.prototype = { if (!options.screw_ie8 && sym instanceof AST_SymbolLambda) s = s.parent_scope; var def; - if (options.screw_ie8 - && sym instanceof AST_SymbolCatch - && (def = s.parent_scope.find_variable(sym))) { + if (this.defun && (def = this.defun.variables.get(this.name))) { this.mangled_name = def.mangled_name || def.name; } else this.mangled_name = s.next_mangled(options, this); @@ -171,7 +169,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ } } else if (node instanceof AST_SymbolCatch) { - scope.def_variable(node); + scope.def_variable(node).defun = defun; } else if (node instanceof AST_LabelRef) { var sym = labels.get(node.name); @@ -227,7 +225,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ if (node instanceof AST_SymbolCatch) { var name = node.name; var refs = node.thedef.references; - var scope = node.thedef.scope.parent_scope; + var scope = node.thedef.defun; var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node); refs.forEach(function(ref) { ref.thedef = def; diff --git a/test/compress/issue-1733.js b/test/compress/issue-1733.js new file mode 100644 index 00000000..3a940c96 --- /dev/null +++ b/test/compress/issue-1733.js @@ -0,0 +1,97 @@ +function_iife_catch: { + mangle = { + screw_ie8: true, + } + input: { + function f(n) { + !function() { + try { + throw 0; + } catch (n) { + var a = 1; + console.log(n, a); + } + }(); + } + f(); + } + expect_exact: "function f(o){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();" + expect_stdout: "0 1" +} + +function_iife_catch_ie8: { + mangle = { + screw_ie8: false, + } + input: { + function f(n) { + !function() { + try { + throw 0; + } catch (n) { + var a = 1; + console.log(n, a); + } + }(); + } + f(); + } + expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();" + expect_stdout: "0 1" +} + +function_catch_catch: { + mangle = { + screw_ie8: true, + } + input: { + var o = 0; + function f() { + try { + throw 1; + } catch (c) { + try { + throw 2; + } catch (o) { + var o = 3; + console.log(o); + } + } + console.log(o); + } + f(); + } + expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();" + expect_stdout: [ + "3", + "undefined", + ] +} + +function_catch_catch_ie8: { + mangle = { + screw_ie8: false, + } + input: { + var o = 0; + function f() { + try { + throw 1; + } catch (c) { + try { + throw 2; + } catch (o) { + var o = 3; + console.log(o); + } + } + console.log(o); + } + f(); + } + expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();" + expect_stdout: [ + "3", + "undefined", + ] +} From a84564d1a8b1fda740963a577f4916d15a20138b Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 31 Mar 2017 12:26:10 +0800 Subject: [PATCH 13/13] v2.8.19 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3e7cc626..90172312 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.18", + "version": "2.8.19", "engines": { "node": ">=0.8.0" },