diff --git a/README.md b/README.md index 643d6dac..383b5480 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,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. @@ -215,24 +215,54 @@ 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 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). @@ -241,7 +271,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. @@ -269,9 +299,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 @@ -282,6 +323,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 @@ -662,10 +710,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 diff --git a/bin/uglifyjs b/bin/uglifyjs index 0194269d..8ea7d16b 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)); @@ -38,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."); @@ -114,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) { @@ -171,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) { @@ -258,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/compress.js b/lib/compress.js index 904a15e9..9779d656 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -991,15 +991,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; } //--- @@ -1248,8 +1248,15 @@ merge(Compressor.prototype, { target.push(node); return true; } - if (node instanceof AST_Defun) { - target.push(node); + if (node instanceof AST_Defun && (node === stat || !compressor.has_directive("use strict"))) { + target.push(node === stat ? node : make_node(AST_Var, node, { + definitions: [ + make_node(AST_VarDef, node, { + name: make_node(AST_SymbolVar, node.name, node.name), + value: null + }) + ] + })); return true; } if (node instanceof AST_Scope) { @@ -2386,11 +2393,13 @@ merge(Compressor.prototype, { dirs.push(node); return make_node(AST_EmptyStatement, node); } - if (hoist_funs && node instanceof AST_Defun && !(tt.parent() instanceof AST_Export)) { + if (hoist_funs && node instanceof AST_Defun + && !(tt.parent() instanceof AST_Export) + && tt.parent() === self) { 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){ if (def.name instanceof AST_Destructuring) return; vars.set(def.name.name, def); diff --git a/lib/minify.js b/lib/minify.js index b7eb7341..a44444d8 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" ]); @@ -77,10 +78,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) { @@ -97,19 +102,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; @@ -125,7 +134,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]); } } @@ -144,6 +155,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/lib/scope.js b/lib/scope.js index 39a4a1a7..84747848 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -237,16 +237,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; @@ -440,9 +432,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 @@ -453,13 +443,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; diff --git a/package.json b/package.json index dcdc2f1d..095964f8 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "author": "Mihai Bazon (http://lisperator.net/)", "license": "BSD-2-Clause", - "version": "3.0.11", + "version": "3.0.12", "engines": { "node": ">=0.8.0" }, 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/compress/dead-code.js b/test/compress/dead-code.js index afe4026c..e1536652 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(){}; + var g; } + f(); } + expect_stdout: true + node_version: ">=6" +} + +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,16 +122,55 @@ dead_code_constant_boolean_should_warn_more: { foo(); var moo; } + bar(); } expect: { var foo; - function bar() {} + var bar; // nothing for the while // as for the for, it should keep: var x = 10, y; var moo; + bar(); } expect_stdout: true + node_version: ">=6" +} + +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" } dead_code_block_decls_die: { @@ -134,7 +217,7 @@ dead_code_const_declaration: { var unused; const CONST_FOO = !1; var moo; - function bar() {} + var bar; } expect_stdout: true } @@ -162,7 +245,7 @@ dead_code_const_annotation: { var unused; var CONST_FOO_ANN = !1; var moo; - function bar() {} + var bar; } expect_stdout: true } @@ -229,7 +312,7 @@ dead_code_const_annotation_complex_scope: { var CONST_FOO_ANN = !1; var unused_var_2; var moo; - function bar() {} + var bar; var beef = 'good'; var meat = 'beef'; var pork = 'bad'; diff --git a/test/compress/functions.js b/test/compress/functions.js index 3a560f00..68830364 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 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 'function' 'function'", + ] + node_version: ">=6" +} + +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" +} 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]", + ] +} diff --git a/test/compress/issue-1052.js b/test/compress/issue-1052.js index 0a77f006..3eb00309 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,47 @@ 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 h() {} + if (window) function g() {} + } + } +} 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"); 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"; }