From 7d3b941e6e12883e9631655d69ae99b1f1dde3db Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 24 May 2017 02:30:09 +0800 Subject: [PATCH 01/11] reinstate `describe_ast()` on CLI (#1996) fixes #1995 --- bin/uglifyjs | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/uglifyjs b/bin/uglifyjs index 0194269d..822e9266 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -24,6 +24,7 @@ var options = { program.version(info.name + ' ' + info.version); program.parseArgv = program.parse; program.parse = undefined; +if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast; program.option("-p, --parse ", "Specify parser options.", parse_js("parse", true)); program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true)); program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true)); From a277fe168dede9d61781547ab30adfaf593e6e6e Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 25 May 2017 02:32:36 +0800 Subject: [PATCH 02/11] ensure new line after `describe_ast()` (#1999) --- tools/node.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/node.js b/tools/node.js index 227c23bd..07c62d1e 100644 --- a/tools/node.js +++ b/tools/node.js @@ -61,5 +61,5 @@ function describe_ast() { } }; doitem(AST_Node); - return out + ""; + return out + "\n"; } From 793d61499b4ab53cdfdd3b81b9faf9f36b77bae8 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 25 May 2017 07:15:55 +0800 Subject: [PATCH 03/11] report timing breakdown (#2000) fix corner cases with `sourceMap` fixes #1998 --- README.md | 2 +- bin/uglifyjs | 14 ++++++++------ lib/minify.js | 47 +++++++++++++++++++++++++++++++++++------------ test/benchmark.js | 9 ++------- test/jetstream.js | 2 +- 5 files changed, 47 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 45c95e3a..47012c11 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ a double dash to prevent input files being used as option arguments: the source map. `url` If specified, path to the source map to append in `//# sourceMappingURL`. - --stats Display operations run time on STDERR. + --timings Display operations run time on STDERR. --toplevel Compress and/or mangle variables in top level scope. --verbose Print diagnostic messages. --warn Print warning messages. diff --git a/bin/uglifyjs b/bin/uglifyjs index 822e9266..8ea7d16b 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -39,7 +39,7 @@ program.option("--keep-fnames", "Do not mangle/drop function names. Useful for c program.option("--name-cache ", "File to hold mangled name mappings."); program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)"); program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map()); -program.option("--stats", "Display operations run time on STDERR.") +program.option("--timings", "Display operations run time on STDERR.") program.option("--toplevel", "Compress and/or mangle variables in toplevel scope."); program.option("--verbose", "Print diagnostic messages."); program.option("--warn", "Print warning messages."); @@ -115,10 +115,10 @@ if (program.output == "ast") { }; } if (program.parse) { - if (program.parse.acorn || program.parse.spidermonkey) { - if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser"); - } else { + if (!program.parse.acorn && !program.parse.spidermonkey) { options.parse = program.parse; + } else if (program.sourceMap && program.sourceMap.content == "inline") { + fatal("ERROR: inline source map only works with built-in parser"); } } var convert_path = function(name) { @@ -172,7 +172,7 @@ function run() { UglifyJS.AST_Node.warn_function = function(msg) { console.error("WARN:", msg); }; - if (program.stats) program.stats = Date.now(); + if (program.timings) options.timings = true; try { if (program.parse) { if (program.parse.acorn) { @@ -259,7 +259,9 @@ function run() { return value instanceof UglifyJS.Dictionary ? value.toObject() : value; })); } - if (program.stats) console.error("Elapsed:", Date.now() - program.stats); + if (result.timings) for (var phase in result.timings) { + console.error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s"); + } } function fatal(message) { diff --git a/lib/minify.js b/lib/minify.js index a827930a..16f5b189 100644 --- a/lib/minify.js +++ b/lib/minify.js @@ -30,9 +30,6 @@ function set_shorthand(name, options, keys) { function minify(files, options) { var warn_function = AST_Node.warn_function; try { - if (typeof files == "string") { - files = [ files ]; - } options = defaults(options, { compress: {}, ie8: false, @@ -41,10 +38,14 @@ function minify(files, options) { output: {}, parse: {}, sourceMap: false, + timings: false, toplevel: false, warnings: false, wrap: false, }, true); + var timings = options.timings && { + start: Date.now() + }; set_shorthand("ie8", options, [ "compress", "mangle", "output" ]); set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); set_shorthand("toplevel", options, [ "compress", "mangle" ]); @@ -75,10 +76,14 @@ function minify(files, options) { warnings.push(warning); }; } + if (timings) timings.parse = Date.now(); var toplevel; if (files instanceof AST_Toplevel) { toplevel = files; } else { + if (typeof files == "string") { + files = [ files ]; + } options.parse = options.parse || {}; options.parse.toplevel = null; for (var name in files) { @@ -95,19 +100,23 @@ function minify(files, options) { if (options.wrap) { toplevel = toplevel.wrap_commonjs(options.wrap); } - if (options.compress) { - toplevel.figure_out_scope(options.mangle); - toplevel = new Compressor(options.compress).compress(toplevel); - } + if (timings) timings.scope1 = Date.now(); + if (options.compress) toplevel.figure_out_scope(options.mangle); + if (timings) timings.compress = Date.now(); + if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel); + if (timings) timings.scope2 = Date.now(); + if (options.mangle) toplevel.figure_out_scope(options.mangle); + if (timings) timings.mangle = Date.now(); if (options.mangle) { - toplevel.figure_out_scope(options.mangle); base54.reset(); toplevel.compute_char_frequency(options.mangle); toplevel.mangle_names(options.mangle); - if (options.mangle.properties) { - toplevel = mangle_properties(toplevel, options.mangle.properties); - } } + if (timings) timings.properties = Date.now(); + if (options.mangle && options.mangle.properties) { + toplevel = mangle_properties(toplevel, options.mangle.properties); + } + if (timings) timings.output = Date.now(); var result = {}; if (options.output.ast) { result.ast = toplevel; @@ -123,7 +132,9 @@ function minify(files, options) { root: options.sourceMap.root }); if (options.sourceMap.includeSources) { - for (var name in files) { + if (files instanceof AST_Toplevel) { + throw new Error("original source content unavailable"); + } else for (var name in files) { options.output.source_map.get().setSourceContent(name, files[name]); } } @@ -142,6 +153,18 @@ function minify(files, options) { } } } + if (timings) { + timings.end = Date.now(); + result.timings = { + parse: 1e-3 * (timings.scope1 - timings.parse), + scope: 1e-3 * (timings.compress - timings.scope1 + timings.mangle - timings.scope2), + compress: 1e-3 * (timings.scope2 - timings.compress), + mangle: 1e-3 * (timings.properties - timings.mangle), + properties: 1e-3 * (timings.output - timings.properties), + output: 1e-3 * (timings.end - timings.output), + total: 1e-3 * (timings.end - timings.start) + } + } if (warnings.length) { result.warnings = warnings; } diff --git a/test/benchmark.js b/test/benchmark.js index d7b0263f..e27ed2c3 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -9,7 +9,7 @@ var args = process.argv.slice(2); if (!args.length) { args.push("-mc"); } -args.push("--stats"); +args.push("--timings"); var urls = [ "https://code.jquery.com/jquery-3.2.1.js", "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js", @@ -29,12 +29,7 @@ function done() { var info = results[url]; console.log(); console.log(url); - var elapsed = 0; - console.log(info.log.replace(/Elapsed: ([0-9]+)\s*/g, function(match, time) { - elapsed += 1e-3 * parseInt(time); - return ""; - })); - console.log("Run-time:", elapsed.toFixed(3), "s"); + console.log(info.log); console.log("Original:", info.input, "bytes"); console.log("Uglified:", info.output, "bytes"); console.log("SHA1 sum:", info.sha1); diff --git a/test/jetstream.js b/test/jetstream.js index 56da7ad8..4f13a281 100644 --- a/test/jetstream.js +++ b/test/jetstream.js @@ -14,7 +14,7 @@ if (typeof phantom == "undefined") { if (!args.length) { args.push("-mc"); } - args.push("--stats"); + args.push("--timings"); var child_process = require("child_process"); try { require("phantomjs-prebuilt"); From c70fb6038448cf072cfa96cee78eaa55aa99c0fa Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 26 May 2017 03:58:35 +0800 Subject: [PATCH 04/11] clean up `lib/scope.js` (#2003) fixes #2004 --- lib/scope.js | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/lib/scope.js b/lib/scope.js index 14ffb46f..8bc96072 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -183,16 +183,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ self.walk(tw); // pass 2: find back references and eval - var func = null; - var globals = self.globals = new Dictionary(); + self.globals = new Dictionary(); var tw = new TreeWalker(function(node, descend){ - if (node instanceof AST_Lambda) { - var prev_func = func; - func = node; - descend(); - func = prev_func; - return true; - } if (node instanceof AST_LoopControl && node.label) { node.label.thedef.references.push(node); return true; @@ -361,9 +353,7 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options){ }); // labels are always mangleable -AST_Label.DEFMETHOD("unmangleable", function(){ - return false; -}); +AST_Label.DEFMETHOD("unmangleable", return_false); AST_Symbol.DEFMETHOD("unreferenced", function(){ return this.definition().references.length == 0 @@ -374,13 +364,9 @@ AST_Symbol.DEFMETHOD("undeclared", function(){ return this.definition().undeclared; }); -AST_LabelRef.DEFMETHOD("undeclared", function(){ - return false; -}); +AST_LabelRef.DEFMETHOD("undeclared", return_false); -AST_Label.DEFMETHOD("undeclared", function(){ - return false; -}); +AST_Label.DEFMETHOD("undeclared", return_false); AST_Symbol.DEFMETHOD("definition", function(){ return this.thedef; 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 05/11] 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]", + ] +} From 695e182d596c00bfdf9bacff36fd8e65c6f35bf3 Mon Sep 17 00:00:00 2001 From: kzc Date: Fri, 26 May 2017 13:25:51 -0400 Subject: [PATCH 06/11] fix and expand --mangle-props documentation (#2008) fixes #2007 --- README.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 47012c11..d147e619 100644 --- a/README.md +++ b/README.md @@ -224,18 +224,47 @@ separate step, different from variable name mangling. Pass object literal, or that are assigned to. For example: ```javascript +// example.js var x = { - foo: 1 + baz_: 0, + foo_: 1, + calc: function() { + return this.foo_ + this.baz_; + } }; - -x.bar = 2; -x["baz"] = 3; -x[condition ? "moo" : "boo"] = 4; -console.log(x.something()); +x.bar_ = 2; +x["baz_"] = 3; +console.log(x.calc()); +``` +Mangle all properties (except for javascript `builtins`): +```bash +$ uglifyjs example.js -c -m --mangle-props +``` +```javascript +var x={o:0,_:1,l:function(){return this._+this.o}};x.t=2,x.o=3,console.log(x.l()); +``` +Mangle all properties except for `reserved` properties: +```bash +$ uglifyjs example.js -c -m --mangle-props reserved=[foo_,bar_] +``` +```javascript +var x={o:0,foo_:1,_:function(){return this.foo_+this.o}};x.bar_=2,x.o=3,console.log(x._()); +``` +Mangle all properties matching a `regex`: +```bash +$ uglifyjs example.js -c -m --mangle-props regex=/_$/ +``` +```javascript +var x={o:0,_:1,calc:function(){return this._+this.o}};x.l=2,x.o=3,console.log(x.calc()); ``` -In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced -with single characters, while `something()` will be left as is. +Combining mangle properties options: +```bash +$ uglifyjs example.js -c -m --mangle-props regex=/_$/,reserved=[bar_] +``` +```javascript +var x={o:0,_:1,calc:function(){return this._+this.o}};x.bar_=2,x.o=3,console.log(x.calc()); +``` In order for this to be of any use, we avoid mangling standard JS names by default (`--mangle-props builtins` to override). @@ -244,7 +273,7 @@ A default exclusion file is provided in `tools/domprops.json` which should cover most standard JS and DOM properties defined in various browsers. Pass `--mangle-props domprops` to disable this feature. -You can also use a regular expression to define which property names should be +A regular expression can be used to define which property names should be mangled. For example, `--mangle-props regex=/^_/` will only mangle property names that start with an underscore. @@ -272,9 +301,20 @@ Using quoted property name (`o["foo"]`) reserves the property name (`foo`) so that it is not mangled throughout the entire script even when used in an unquoted style (`o.foo`). Example: +```javascript +// stuff.js +var o = { + "foo": 1, + bar: 3 +}; +o.foo += o.bar; +console.log(o.foo); +``` ```bash -$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props keep_quoted -mc -var o={foo:1,a:3};o.foo+=o.a,console.log(o.foo); +$ uglifyjs stuff.js --mangle-props keep_quoted -c -m +``` +```javascript +var o={foo:1,o:3};o.foo+=o.o,console.log(o.foo); ``` ### Debugging property name mangling @@ -285,6 +325,13 @@ would mangle to `o._$foo$_` with this option. This allows property mangling of a large codebase while still being able to debug the code and identify where mangling is breaking things. +```bash +$ uglifyjs stuff.js --mangle-props debug -c -m +``` +```javascript +var o={_$foo$_:1,_$bar$_:3};o._$foo$_+=o._$bar$_,console.log(o._$foo$_); +``` + You can also pass a custom suffix using `--mangle-props debug=XYZ`. This would then mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a script to identify how a property got mangled. One technique is to pass a From 78309a293d88e4ac59477af71a3cff4766f94371 Mon Sep 17 00:00:00 2001 From: kzc Date: Fri, 26 May 2017 14:28:43 -0400 Subject: [PATCH 07/11] better document mangle properties options (#2009) --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d147e619..9db3e6c7 100644 --- a/README.md +++ b/README.md @@ -712,10 +712,15 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code; ### Mangle properties options -- `regex` — Pass a RegExp to only mangle certain names -- `keep_quoted` — Only mangle unquoted property names -- `debug` — Mangle names with the original name still present. Defaults to `false`. - Pass an empty string to enable, or a non-empty string to set the suffix. +- `reserved` (default: `[]`) -- Do not mangle property names listed in the + `reserved` array. +- `regex` (default: `null`) -— Pass a RegExp literal to only mangle property + names matching the regular expression. +- `keep_quoted` (default: `false`) -— Only mangle unquoted property names. +- `debug` (default: `false`) -— Mangle names with the original name still present. + Pass an empty string `""` to enable, or a non-empty string to set the debug suffix. +- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin + DOM properties. Not recommended to override this setting. ## Output options From 1ff8e9dd386beb071945c4e3f73ed347c72527d8 Mon Sep 17 00:00:00 2001 From: kzc Date: Sat, 27 May 2017 01:17:30 -0400 Subject: [PATCH 08/11] clarify what --mangle-props does (#2012) --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9db3e6c7..985fe507 100644 --- a/README.md +++ b/README.md @@ -218,10 +218,11 @@ to prevent the `require`, `exports` and `$` names from being changed. ### CLI mangling property names (`--mangle-props`) -**Note:** this will probably break your code. Mangling property names is a -separate step, different from variable name mangling. Pass -`--mangle-props`. It will mangle all properties that are seen in some -object literal, or that are assigned to. For example: +**Note:** THIS WILL PROBABLY BREAK YOUR CODE. Mangling property names +is a separate step, different from variable name mangling. Pass +`--mangle-props` to enable it. It will mangle all properties in the +input code with the exception of built in DOM properties and properties +in core javascript classes. For example: ```javascript // example.js From 95094b9c22d5f7b303383f75e660fc32d0a7f25f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 27 May 2017 13:41:49 +0800 Subject: [PATCH 09/11] fix `if_return` on `AST_Defun` (#2010) Previous fiix for #1052 perturbs declaration order of functions which leads to incorrect behaviour under "use strict". --- lib/compress.js | 4 +- test/compress/issue-1052.js | 88 ++++++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index bd017e10..efa8e988 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -965,15 +965,15 @@ merge(Compressor.prototype, { CHANGED = true; stat = stat.clone(); stat.condition = stat.condition.negate(compressor); + var funs = extract_functions_from_statement_array(ret); var body = as_statement_array(stat.alternative).concat(ret); - var funs = extract_functions_from_statement_array(body); stat.body = make_node(AST_BlockStatement, stat, { body: body }); stat.alternative = value ? make_node(AST_SimpleStatement, value, { body: value.expression }) : null; - ret = funs.concat([ stat.transform(compressor) ]); + ret = [ stat.transform(compressor) ].concat(funs); continue loop; } //--- diff --git a/test/compress/issue-1052.js b/test/compress/issue-1052.js index 0a77f006..e3dc7322 100644 --- a/test/compress/issue-1052.js +++ b/test/compress/issue-1052.js @@ -1,90 +1,91 @@ multiple_functions: { - options = { if_return: true, hoist_funs: false }; + options = { + hoist_funs: false, + if_return: true, + } input: { ( function() { if ( !window ) { return; } - function f() {} function g() {} } )(); } expect: { ( function() { - function f() {} - function g() {} - // NOTE: other compression steps will reduce this // down to just `window`. if ( window ); + function f() {} + function g() {} } )(); } } single_function: { - options = { if_return: true, hoist_funs: false }; + options = { + hoist_funs: false, + if_return: true, + } input: { ( function() { if ( !window ) { return; } - function f() {} } )(); } expect: { ( function() { - function f() {} - if ( window ); + function f() {} } )(); } } deeply_nested: { - options = { if_return: true, hoist_funs: false }; + options = { + hoist_funs: false, + if_return: true, + } input: { ( function() { if ( !window ) { return; } - function f() {} function g() {} - if ( !document ) { return; } - function h() {} } )(); } expect: { ( function() { - function f() {} - function g() {} - - function h() {} - // NOTE: other compression steps will reduce this // down to just `window`. if ( window ) if (document); + function f() {} + function g() {} + function h() {} } )(); } } not_hoisted_when_already_nested: { - options = { if_return: true, hoist_funs: false }; + options = { + hoist_funs: false, + if_return: true, + } input: { ( function() { if ( !window ) { return; } - if ( foo ) function f() {} - } )(); } expect: { @@ -94,3 +95,48 @@ not_hoisted_when_already_nested: { } )(); } } + +defun_if_return: { + options = { + hoist_funs: false, + if_return: true, + } + input: { + function e() { + function f() {} + if (!window) return; + else function g() {} + function h() {} + } + } + expect: { + function e() { + function f() {} + if (window) function g() {} + function h() {} + } + } +} + +defun_hoist_funs: { + options = { + hoist_funs: true, + if_return: true, + } + input: { + function e() { + function f() {} + if (!window) return; + else function g() {} + function h() {} + } + } + expect: { + function e() { + function f() {} + function g() {} + function h() {} + !window; + } + } +} From 7b13159cda54b6e2dc5faef02c87ae87daa59237 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 27 May 2017 17:44:59 +0800 Subject: [PATCH 10/11] fix `hoist_funs` on block-scoped `function` under "use strict" (#2013) Technically not part of ES5, but commonly used code exists in the wild. --- lib/compress.js | 5 ++- test/compress/functions.js | 78 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index efa8e988..6359696b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2251,11 +2251,12 @@ merge(Compressor.prototype, { dirs.push(node); return make_node(AST_EmptyStatement, node); } - if (node instanceof AST_Defun && hoist_funs) { + if (hoist_funs && node instanceof AST_Defun + && (tt.parent() === self || !compressor.has_directive("use strict"))) { hoisted.push(node); return make_node(AST_EmptyStatement, node); } - if (node instanceof AST_Var && hoist_vars) { + if (hoist_vars && node instanceof AST_Var) { node.definitions.forEach(function(def){ vars.set(def.name.name, def); ++vars_found; diff --git a/test/compress/functions.js b/test/compress/functions.js index 3a560f00..ce8bab80 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -167,3 +167,81 @@ function_returning_constant_literal: { } expect_stdout: "Hello there" } + +hoist_funs: { + options = { + hoist_funs: true, + } + input: { + console.log(1, typeof f, typeof g); + if (console.log(2, typeof f, typeof g)) + console.log(3, typeof f, typeof g); + else { + console.log(4, typeof f, typeof g); + function f() {} + console.log(5, typeof f, typeof g); + } + function g() {} + console.log(6, typeof f, typeof g); + } + expect: { + function f() {} + function g() {} + console.log(1, typeof f, typeof g); + if (console.log(2, typeof f, typeof g)) + console.log(3, typeof f, typeof g); + else { + console.log(4, typeof f, typeof g); + console.log(5, typeof f, typeof g); + } + console.log(6, typeof f, typeof g); + } + expect_stdout: [ + "1 'function' 'function'", + "2 'function' 'function'", + "4 'function' 'function'", + "5 'function' 'function'", + "6 'function' 'function'", + ] + node_version: "<=4" +} + +hoist_funs_strict: { + options = { + hoist_funs: true, + } + input: { + "use strict"; + console.log(1, typeof f, typeof g); + if (console.log(2, typeof f, typeof g)) + console.log(3, typeof f, typeof g); + else { + console.log(4, typeof f, typeof g); + function f() {} + console.log(5, typeof f, typeof g); + } + function g() {} + console.log(6, typeof f, typeof g); + } + expect: { + "use strict"; + function g() {} + console.log(1, typeof f, typeof g); + if (console.log(2, typeof f, typeof g)) + console.log(3, typeof f, typeof g); + else { + console.log(4, typeof f, typeof g); + function f() {} + console.log(5, typeof f, typeof g); + } + console.log(6, typeof f, typeof g); + } + expect_stdout: [ + "1 'undefined' 'function'", + "2 'undefined' 'function'", + "4 'function' 'function'", + "5 'function' 'function'", + "6 'undefined' 'function'", + ] + node_version: "=4" +} From c3f14a1481efc0ec381370fefafcf66c4b68d94b Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 27 May 2017 18:08:09 +0800 Subject: [PATCH 11/11] v3.0.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0ba0c22..adccb121 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.11", + "version": "3.0.12", "engines": { "node": ">=0.8.0" },