From e5cf7972eab25581f9aa020bc8dbdd8bf25743cb Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 29 Aug 2017 01:10:04 +0800 Subject: [PATCH 1/9] fix `unused` patching of `AST_For.init` blocks (#2289) fixes #2288 --- lib/compress.js | 9 +++++---- test/compress/drop-unused.js | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 7a16ba86..9121d0ce 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2423,17 +2423,18 @@ merge(Compressor.prototype, { // We fix it at this stage by moving the `var` outside the `for`. if (node instanceof AST_For) { descend(node, this); + var block; if (node.init instanceof AST_BlockStatement) { - var block = node.init; + block = node.init; node.init = block.body.pop(); block.body.push(node); - return in_list ? MAP.splice(block.body) : block; - } else if (node.init instanceof AST_SimpleStatement) { + } + if (node.init instanceof AST_SimpleStatement) { node.init = node.init.body; } else if (is_empty(node.init)) { node.init = null; } - return node; + return !block ? node : in_list ? MAP.splice(block.body) : block; } if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) { descend(node, this); diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 34d47d0d..c9048540 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1239,3 +1239,25 @@ issue_2226_3: { } expect_stdout: "3" } + +issue_2288: { + options = { + unused: true, + } + beautify = { + beautify: true, + } + input: { + function foo(o) { + for (var j = o.a, i = 0; i < 0; i++); + for (var i = 0; i < 0; i++); + } + } + expect: { + function foo(o) { + o.a; + for (i = 0; i < 0; i++); + for (var i = 0; i < 0; i++); + } + } +} From eb7adaa6fc52cca3766b876745895eb2047ee323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=C5=A0anda?= Date: Tue, 29 Aug 2017 17:49:20 +0200 Subject: [PATCH 2/9] Fix CLI source-maps examples (#2291) fixes #2284 --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8db77e57..15d8d7c7 100644 --- a/README.md +++ b/README.md @@ -150,19 +150,19 @@ debugging your compressed JavaScript. To get a source map, pass Additional options: -- `--source-map filename=` to specify the name of the source map. +- `--source-map "filename=''"` to specify the name of the source map. -- `--source-map root=` to pass the URL where the original files can be found. +- `--source-map "root=''"` to pass the URL where the original files can be found. Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the `//# sourceMappingURL=` directive. -- `--source-map url=` to specify the URL where the source map can be found. +- `--source-map "url=''"` to specify the URL where the source map can be found. For example: uglifyjs js/file1.js js/file2.js \ -o foo.min.js -c -m \ - --source-map root="http://foo.com/src",url=foo.min.js.map + --source-map "root='http://foo.com/src',url='foo.min.js.map'" The above will compress and mangle `file1.js` and `file2.js`, will drop the output in `foo.min.js` and the source map in `foo.min.js.map`. The source @@ -181,8 +181,8 @@ CoffeeScript → compiled JS, UglifyJS can generate a map from CoffeeScript → compressed JS by mapping every token in the compiled JS to its original location. -To use this feature pass `--source-map content="/path/to/input/source.map"` -or `--source-map content=inline` if the source map is included inline with +To use this feature pass `--source-map "content='/path/to/input/source.map'"` +or `--source-map "content=inline"` if the source map is included inline with the sources. ## CLI compress options From 71d52f147dedfb49bf45bccb46613cd1f46ec286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=C5=A0anda?= Date: Wed, 30 Aug 2017 18:55:32 +0200 Subject: [PATCH 3/9] Fix CLI example for mangle reserved list of names (#2294) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15d8d7c7..6bfede08 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ When mangling is enabled but you want to prevent certain names from being mangled, you can declare those names with `--mangle reserved` — pass a comma-separated list of names. For example: - uglifyjs ... -m reserved=[$,require,exports] + uglifyjs ... -m reserved=['$','require','exports'] to prevent the `require`, `exports` and `$` names from being changed. From 3f355866cf903c40c2bab2cd841ab2b56a2bacf1 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 3 Sep 2017 17:23:31 +0800 Subject: [PATCH 4/9] correctly count declarations after `hoist_vars` (#2297) fixes #2295 --- lib/compress.js | 1 + test/compress/hoist_vars.js | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lib/compress.js b/lib/compress.js index 9121d0ce..b6a984cc 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3113,6 +3113,7 @@ merge(Compressor.prototype, { })); if (reduce_vars) name.definition().fixed = false; } + remove(def.name.definition().orig, def.name); return a; }, []); if (assignments.length == 0) return null; diff --git a/test/compress/hoist_vars.js b/test/compress/hoist_vars.js index 6fe1c773..6aa1f7b4 100644 --- a/test/compress/hoist_vars.js +++ b/test/compress/hoist_vars.js @@ -88,3 +88,24 @@ sequences_funs: { } } } + +issue_2295: { + options = { + collapse_vars: true, + hoist_vars: true, + } + input: { + function foo(o) { + var a = o.a; + if (a) return a; + var a = 1; + } + } + expect: { + function foo(o) { + var a = o.a; + if (a) return a; + a = 1; + } + } +} From 395a17ccda95362b1a7a5bd9ac0e6cda7f0946a8 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 4 Sep 2017 02:32:33 +0800 Subject: [PATCH 5/9] fix `collapse_vars` on default function argument (#2299) Avoid collision with local variable `undefined` under certain corner cases. fixes #2298 --- lib/compress.js | 2 +- test/compress/collapse_vars.js | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/compress.js b/lib/compress.js index b6a984cc..6e766fb0 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -847,7 +847,7 @@ merge(Compressor.prototype, { if (sym.name in names) continue; names[sym.name] = true; var arg = iife.args[i]; - if (!arg) arg = make_node(AST_Undefined, sym); + if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor); else { var tw = new TreeWalker(function(node) { if (!arg) return true; diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 7686addf..72123d4b 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -2342,3 +2342,45 @@ duplicate_argname: { } expect_stdout: "PASS" } + +issue_2298: { + options = { + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function f() { + var a = undefined; + var undefined = a++; + try { + !function g(b) { + b[1] = "foo"; + }(); + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + f(); + }(); + } + expect: { + !function() { + (function() { + var a = undefined; + var undefined = a++; + try { + !function(b) { + (void 0)[1] = "foo"; + }(); + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + })(); + }(); + } + expect_stdout: "PASS" +} From 8b890721903a0a983ffe74a3cecc6b922fa0518e Mon Sep 17 00:00:00 2001 From: kzc Date: Wed, 6 Sep 2017 14:44:26 -0400 Subject: [PATCH 6/9] add `Date` and other known globals to `unsafe` compress option (#2302) --- lib/compress.js | 2 +- test/compress/dead-code.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/compress.js b/lib/compress.js index 6e766fb0..3da7a315 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -693,7 +693,7 @@ merge(Compressor.prototype, { return node instanceof AST_SymbolRef && node.definition().undeclared; } - var global_names = makePredicate("Array Boolean console Error Function Math Number RegExp Object String"); + var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Math Number parseFloat parseInt RangeError ReferenceError RegExp Object setInterval setTimeout String SyntaxError TypeError unescape URIError"); AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) { return !this.definition().undeclared || compressor.option("unsafe") && global_names(this.name); diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index abf5297c..aea0e540 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -241,18 +241,54 @@ issue_2233_1: { Array.isArray; Boolean; console.log; + Date; + decodeURI; + decodeURIComponent; + encodeURI; + encodeURIComponent; Error.name; + escape; + eval; + EvalError; Function.length; + isFinite; + isNaN; + JSON; Math.random; Number.isNaN; + parseFloat; + parseInt; RegExp; Object.defineProperty; String.fromCharCode; + RangeError; + ReferenceError; + SyntaxError; + TypeError; + unescape; + URIError; } expect: {} expect_stdout: true } +global_timeout_and_interval_symbols: { + options = { + pure_getters: "strict", + side_effects: true, + unsafe: true, + } + input: { + // These global symbols do not exist in the test sandbox + // and must be tested separately. + clearInterval; + clearTimeout; + setInterval; + setTimeout; + } + expect: {} +} + issue_2233_2: { options = { pure_getters: "strict", From aacf3edc68558cc19873d5247951dea328d8f7a9 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 7 Sep 2017 22:08:34 +0800 Subject: [PATCH 7/9] extend `unsafe` on pure global functions (#2303) --- lib/compress.js | 13 ++++++-- test/compress/dead-code.js | 68 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 3da7a315..0ba2951b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1990,6 +1990,15 @@ merge(Compressor.prototype, { return this.pure = pure; }); + var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError"); + AST_Call.DEFMETHOD("is_expr_pure", function(compressor) { + if (compressor.option("unsafe")) { + var expr = this.expression; + if (is_undeclared_ref(expr) && global_pure_fns(expr.name)) return true; + } + return this.has_pure_annotation(compressor) || !compressor.pure_funcs(this); + }); + // determine if expression has side effects (function(def){ def(AST_Node, return_true); @@ -1999,7 +2008,7 @@ merge(Compressor.prototype, { def(AST_This, return_false); def(AST_Call, function(compressor){ - if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return true; + if (!this.is_expr_pure(compressor)) return true; for (var i = this.args.length; --i >= 0;) { if (this.args[i].has_side_effects(compressor)) return true; @@ -2618,7 +2627,7 @@ merge(Compressor.prototype, { def(AST_Constant, return_null); 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.is_expr_pure(compressor)) { if (this.expression instanceof AST_Function && (!this.expression.name || !this.expression.name.definition().references.length)) { var node = this.clone(); diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index aea0e540..e7630562 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -345,3 +345,71 @@ issue_2233_3: { UndeclaredGlobal; } } + +global_fns: { + options = { + side_effects: true, + unsafe: true, + } + input: { + Boolean(1, 2); + decodeURI(1, 2); + decodeURIComponent(1, 2); + Date(1, 2); + encodeURI(1, 2); + encodeURIComponent(1, 2); + Error(1, 2); + escape(1, 2); + EvalError(1, 2); + isFinite(1, 2); + isNaN(1, 2); + Number(1, 2); + Object(1, 2); + parseFloat(1, 2); + parseInt(1, 2); + RangeError(1, 2); + ReferenceError(1, 2); + String(1, 2); + SyntaxError(1, 2); + TypeError(1, 2); + unescape(1, 2); + URIError(1, 2); + try { + Function(1, 2); + } catch (e) { + console.log(e.name); + } + try { + RegExp(1, 2); + } catch (e) { + console.log(e.name); + } + try { + Array(NaN); + } catch (e) { + console.log(e.name); + } + } + expect: { + try { + Function(1, 2); + } catch (e) { + console.log(e.name); + } + try { + RegExp(1, 2); + } catch (e) { + console.log(e.name); + } + try { + Array(NaN); + } catch (e) { + console.log(e.name); + } + } + expect_stdout: [ + "SyntaxError", + "SyntaxError", + "RangeError", + ] +} From 8158b1bdcf9ad194876fb0bc746346eb9154614a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 9 Sep 2017 20:08:15 +0200 Subject: [PATCH 8/9] Testing all leading comments against being PURE comments (#2305) --- lib/compress.js | 8 +++-- test/compress/issue-1261.js | 61 +++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 0ba2951b..da68dbc7 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1980,12 +1980,14 @@ merge(Compressor.prototype, { if (!compressor.option("side_effects")) return false; if (this.pure !== undefined) return this.pure; var pure = false; - var comments, last_comment; + var comments, pure_comment; if (this.start && (comments = this.start.comments_before) && comments.length - && /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) { - pure = last_comment; + && (pure_comment = find_if(function (comment) { + return /[@#]__PURE__/.test(comment.value); + }, comments))) { + pure = pure_comment; } return this.pure = pure; }); diff --git a/test/compress/issue-1261.js b/test/compress/issue-1261.js index 343c175b..994a00b3 100644 --- a/test/compress/issue-1261.js +++ b/test/compress/issue-1261.js @@ -96,6 +96,13 @@ pure_function_calls_toplevel: { })(); })(); + // pure top-level calls will be dropped regardless of the leading comments position + var MyClass = /*#__PURE__*//*@class*/(function(){ + function MyClass() {} + MyClass.prototype.method = function() {}; + return MyClass; + })(); + // comment #__PURE__ comment bar(), baz(), quux(); a.b(), /* @__PURE__ */ c.d.e(), f.g(); @@ -110,10 +117,12 @@ pure_function_calls_toplevel: { "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:92,37]", "WARN: Dropping unused variable iife2 [test/compress/issue-1261.js:92,16]", "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:90,8]", - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:100,8]", - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:101,31]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:107,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:108,31]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:84,33]", "WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:100,45]", + "WARN: Dropping unused variable MyClass [test/compress/issue-1261.js:100,12]", ] } @@ -148,29 +157,29 @@ should_warn: { baz(); } expect_warnings: [ - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,61]", - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,23]", - "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:128,23]", - "WARN: Boolean || always true [test/compress/issue-1261.js:129,23]", - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:129,23]", - "WARN: Condition always true [test/compress/issue-1261.js:129,23]", - "WARN: Condition left of || always true [test/compress/issue-1261.js:130,8]", - "WARN: Condition always true [test/compress/issue-1261.js:130,8]", - "WARN: Boolean && always false [test/compress/issue-1261.js:131,23]", - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:131,23]", - "WARN: Condition always false [test/compress/issue-1261.js:131,23]", - "WARN: Condition left of && always false [test/compress/issue-1261.js:132,8]", - "WARN: Condition always false [test/compress/issue-1261.js:132,8]", - "WARN: + in boolean context always true [test/compress/issue-1261.js:133,23]", - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:133,23]", - "WARN: Condition always true [test/compress/issue-1261.js:133,23]", - "WARN: + in boolean context always true [test/compress/issue-1261.js:134,8]", - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:134,31]", - "WARN: Condition always true [test/compress/issue-1261.js:134,8]", - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:135,23]", - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:136,24]", - "WARN: Condition always true [test/compress/issue-1261.js:136,8]", - "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,31]", - "WARN: Condition always false [test/compress/issue-1261.js:137,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,61]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,23]", + "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:137,23]", + "WARN: Boolean || always true [test/compress/issue-1261.js:138,23]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:138,23]", + "WARN: Condition always true [test/compress/issue-1261.js:138,23]", + "WARN: Condition left of || always true [test/compress/issue-1261.js:139,8]", + "WARN: Condition always true [test/compress/issue-1261.js:139,8]", + "WARN: Boolean && always false [test/compress/issue-1261.js:140,23]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:140,23]", + "WARN: Condition always false [test/compress/issue-1261.js:140,23]", + "WARN: Condition left of && always false [test/compress/issue-1261.js:141,8]", + "WARN: Condition always false [test/compress/issue-1261.js:141,8]", + "WARN: + in boolean context always true [test/compress/issue-1261.js:142,23]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]", + "WARN: Condition always true [test/compress/issue-1261.js:142,23]", + "WARN: + in boolean context always true [test/compress/issue-1261.js:143,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,31]", + "WARN: Condition always true [test/compress/issue-1261.js:143,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,23]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:145,24]", + "WARN: Condition always true [test/compress/issue-1261.js:145,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:146,31]", + "WARN: Condition always false [test/compress/issue-1261.js:146,8]", ] } From cd27f4ec38f2899707859459970406ae67c3adb6 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 10 Sep 2017 15:17:24 +0800 Subject: [PATCH 9/9] v3.1.0 --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index dddc82ed..15ef15f0 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": "3.0.28", + "version": "3.1.0", "engines": { "node": ">=0.8.0" }, @@ -34,8 +34,8 @@ }, "devDependencies": { "acorn": "~5.1.1", - "mocha": "~3.4.2", - "semver": "~5.3.0" + "mocha": "~3.5.1", + "semver": "~5.4.1" }, "scripts": { "test": "node test/run-tests.js"