From 2c2fd89e343626f8d7dc83812a6476b0ab99b784 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 5 Nov 2017 22:14:11 +0800 Subject: [PATCH 1/6] inline single-use functions that are not constant expressions (#2434) fixes #2428 --- lib/compress.js | 20 +++----------------- lib/minify.js | 10 ++++------ test/compress/functions.js | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 454c1666..ba7c10f4 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -150,7 +150,9 @@ merge(Compressor.prototype, { } var passes = +this.options.passes || 1; var last_count = 1 / 0; + var mangle = { ie8: this.option("ie8") }; for (var pass = 0; pass < passes; pass++) { + node.figure_out_scope(mangle); if (pass > 0 || this.option("reduce_vars")) node.reset_opt_flags(this); node = node.transform(this); @@ -3528,6 +3530,7 @@ merge(Compressor.prototype, { && !exp.uses_arguments && !exp.uses_eval && exp.body.length == 1 + && !exp.contains_this() && all(exp.argnames, function(arg) { return arg.__unused; }) @@ -3541,23 +3544,6 @@ merge(Compressor.prototype, { expression: stat.body }); } - if (value) { - var tw = new TreeWalker(function(node) { - if (!value) return true; - if (node instanceof AST_SymbolRef) { - var ref = node.scope.find_variable(node); - if (ref && ref.scope.parent_scope === fn.parent_scope) { - value = null; - return true; - } - } - if (node instanceof AST_This && !tw.find_parent(AST_Scope)) { - value = null; - return true; - } - }); - value.walk(tw); - } if (value) { var args = self.args.concat(value); return make_sequence(self, args).optimize(compressor); diff --git a/lib/minify.js b/lib/minify.js index 773e953a..f9d726bf 100644 --- a/lib/minify.js +++ b/lib/minify.js @@ -137,11 +137,9 @@ function minify(files, options) { if (options.wrap) { toplevel = toplevel.wrap_commonjs(options.wrap); } - 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 (timings) timings.scope = Date.now(); if (options.mangle) toplevel.figure_out_scope(options.mangle); if (timings) timings.mangle = Date.now(); if (options.mangle) { @@ -199,9 +197,9 @@ 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), + parse: 1e-3 * (timings.compress - timings.parse), + compress: 1e-3 * (timings.scope - timings.compress), + scope: 1e-3 * (timings.mangle - timings.scope), mangle: 1e-3 * (timings.properties - timings.mangle), properties: 1e-3 * (timings.output - timings.properties), output: 1e-3 * (timings.end - timings.output), diff --git a/test/compress/functions.js b/test/compress/functions.js index febf81c1..3c2ccce3 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -508,3 +508,41 @@ issue_2114_2: { } expect_stdout: "2" } + +issue_2428: { + options = { + collapse_vars: true, + inline: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + function bar(k) { + console.log(k); + } + function foo(x) { + return bar(x); + } + function baz(a) { + foo(a); + } + baz(42); + baz("PASS"); + } + expect: { + function baz(a) { + console.log(a); + } + baz(42); + baz("PASS"); + } + expect_stdout: [ + "42", + "PASS", + ] +} From 6c4510187066555c77003f03fd26e2cf5ff47491 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 6 Nov 2017 14:25:10 +0800 Subject: [PATCH 2/6] consolidate & enhance `unused` (#2439) - defer declaration removal in `collapse_vars` - account for `AST_SymbolFunarg` in deduplication - private accounting for `collapse_vars` - avoid issues with identity reference due to deep cloning fixes #2437 --- lib/compress.js | 142 +++++++++++++++++---------------- test/compress/collapse_vars.js | 66 ++++++++++++++- test/compress/issue-973.js | 1 + test/compress/reduce_vars.js | 12 +-- 4 files changed, 145 insertions(+), 76 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index ba7c10f4..d8482337 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -788,6 +788,14 @@ merge(Compressor.prototype, { || compressor.option("unsafe") && global_names(this.name); }); + function drop_decl(def) { + def._eliminiated = (def._eliminiated || 0) + 1; + if (def.orig.length == def._eliminiated) { + def.scope.functions.del(def.name); + def.scope.variables.del(def.name); + } + } + function tighten_body(statements, compressor) { var CHANGED, max_iter = 10; do { @@ -1000,7 +1008,8 @@ merge(Compressor.prototype, { function get_lhs(expr) { if (expr instanceof AST_VarDef) { var def = expr.name.definition(); - if (def.orig.length > 1 && !(expr.name instanceof AST_SymbolFunarg) + if (def.orig.length - (def._eliminiated || 0) > 1 + && !(expr.name instanceof AST_SymbolFunarg) || def.references.length == 1 && !compressor.exposed(def)) { return make_node(AST_SymbolRef, expr.name, expr.name); } @@ -1009,6 +1018,10 @@ merge(Compressor.prototype, { } } + function get_rvalue(expr) { + return expr[expr instanceof AST_Assign ? "right" : "value"]; + } + function get_lvalues(expr) { var lvalues = Object.create(null); if (expr instanceof AST_Unary) return lvalues; @@ -1019,7 +1032,7 @@ merge(Compressor.prototype, { lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent()); } }); - expr[expr instanceof AST_Assign ? "right" : "value"].walk(tw); + get_rvalue(expr).walk(tw); return lvalues; } @@ -1043,7 +1056,9 @@ merge(Compressor.prototype, { if (node === expr) { found = true; if (node instanceof AST_VarDef) { - remove(node.name.definition().orig, node.name); + drop_decl(node.name.definition()); + node.value = null; + return node; } return in_list ? MAP.skip : null; } @@ -1052,16 +1067,13 @@ merge(Compressor.prototype, { case 0: return null; case 1: return node.expressions[0]; } - if (node instanceof AST_Definitions && node.definitions.length == 0 - || node instanceof AST_SimpleStatement && !node.body) { - return null; - } + if (node instanceof AST_SimpleStatement && !node.body) return null; })); } function value_has_side_effects(expr) { if (expr instanceof AST_Unary) return false; - return expr[expr instanceof AST_Assign ? "right" : "value"].has_side_effects(compressor); + return get_rvalue(expr).has_side_effects(compressor); } function references_in_scope(def) { @@ -2303,61 +2315,63 @@ merge(Compressor.prototype, { // this scope (not in nested scopes). var scope = this; var tw = new TreeWalker(function(node, descend){ - if (node !== self) { - if (node instanceof AST_Defun) { - if (!drop_funcs && scope === self) { - var node_def = node.name.definition(); + if (node === self) return; + if (node instanceof AST_Defun) { + if (!drop_funcs && scope === self) { + var node_def = node.name.definition(); + if (!(node_def.id in in_use_ids)) { + in_use_ids[node_def.id] = true; + in_use.push(node_def); + } + } + initializations.add(node.name.name, node); + return true; // don't go in nested scopes + } + if (node instanceof AST_SymbolFunarg && scope === self) { + var_defs_by_id.add(node.definition().id, node); + } + if (node instanceof AST_Definitions && scope === self) { + node.definitions.forEach(function(def){ + var node_def = def.name.definition(); + if (def.name instanceof AST_SymbolVar) { + var_defs_by_id.add(node_def.id, def); + } + if (!drop_vars) { if (!(node_def.id in in_use_ids)) { in_use_ids[node_def.id] = true; in_use.push(node_def); } } - initializations.add(node.name.name, node); - return true; // don't go in nested scopes - } - if (node instanceof AST_Definitions && scope === self) { - node.definitions.forEach(function(def){ - var node_def = def.name.definition(); - if (def.name instanceof AST_SymbolVar) { - var_defs_by_id.add(node_def.id, def); + if (def.value) { + initializations.add(def.name.name, def.value); + if (def.value.has_side_effects(compressor)) { + def.value.walk(tw); } - if (!drop_vars) { - if (!(node_def.id in in_use_ids)) { - in_use_ids[node_def.id] = true; - in_use.push(node_def); - } - } - if (def.value) { - initializations.add(def.name.name, def.value); - if (def.value.has_side_effects(compressor)) { - def.value.walk(tw); - } - } - }); - return true; - } - var sym; - if (scope === self - && (sym = assign_as_unused(node)) instanceof AST_SymbolRef - && self.variables.get(sym.name) === sym.definition()) { - if (node instanceof AST_Assign) node.right.walk(tw); - return true; - } - if (node instanceof AST_SymbolRef) { - var node_def = node.definition(); - if (!(node_def.id in in_use_ids)) { - in_use_ids[node_def.id] = true; - in_use.push(node_def); } - return true; - } - if (node instanceof AST_Scope) { - var save_scope = scope; - scope = node; - descend(); - scope = save_scope; - return true; + }); + return true; + } + var sym; + if (scope === self + && (sym = assign_as_unused(node)) instanceof AST_SymbolRef + && self.variables.get(sym.name) === sym.definition()) { + if (node instanceof AST_Assign) node.right.walk(tw); + return true; + } + if (node instanceof AST_SymbolRef) { + var node_def = node.definition(); + if (!(node_def.id in in_use_ids)) { + in_use_ids[node_def.id] = true; + in_use.push(node_def); } + return true; + } + if (node instanceof AST_Scope) { + var save_scope = scope; + scope = node; + descend(); + scope = save_scope; + return true; } }); self.walk(tw); @@ -2415,7 +2429,7 @@ merge(Compressor.prototype, { var def = node.name.definition(); if (!(def.id in in_use_ids)) { compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name)); - drop_decl(def, node.name); + drop_decl(def); return make_node(AST_EmptyStatement, node); } return node; @@ -2437,7 +2451,7 @@ merge(Compressor.prototype, { if (var_defs.length > 1 && !def.value) { compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name)); remove(var_defs, def); - drop_decl(sym, def.name); + drop_decl(sym); return; } } @@ -2470,7 +2484,7 @@ merge(Compressor.prototype, { } else { compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name)); } - drop_decl(sym, def.name); + drop_decl(sym); } }); if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) { @@ -2479,7 +2493,7 @@ merge(Compressor.prototype, { var def = tail.pop(); compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name)); remove(var_defs, def); - drop_decl(def.name.definition(), def.name); + drop_decl(def.name.definition()); side_effects.unshift(make_node(AST_Assign, def, { operator: "=", left: make_node(AST_SymbolRef, def.name, def.name), @@ -2561,14 +2575,6 @@ merge(Compressor.prototype, { col : sym.start.col }; } - - function drop_decl(def, decl) { - remove(def.orig, decl); - if (!def.orig.length) { - def.scope.functions.del(def.name); - def.scope.variables.del(def.name); - } - } } ); self.transform(tt); @@ -3288,7 +3294,7 @@ merge(Compressor.prototype, { })); if (reduce_vars) name.definition().fixed = false; } - remove(def.name.definition().orig, def.name); + drop_decl(def.name.definition()); return a; }, []); if (assignments.length == 0) return null; diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index e2c5f1be..fe8e4097 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -1388,6 +1388,7 @@ issue_1605_1: { options = { collapse_vars: true, toplevel: false, + unused: true, } input: { function foo(x) { @@ -1410,6 +1411,7 @@ issue_1605_2: { options = { collapse_vars: true, toplevel: "vars", + unused: true, } input: { function foo(x) { @@ -1537,6 +1539,7 @@ issue_1631_3: { var_side_effects_1: { options = { collapse_vars: true, + unused: true, } input: { var print = console.log.bind(console); @@ -1559,6 +1562,7 @@ var_side_effects_1: { var_side_effects_2: { options = { collapse_vars: true, + unused: true, } input: { var print = console.log.bind(console); @@ -1584,6 +1588,7 @@ var_side_effects_3: { collapse_vars: true, pure_getters: true, unsafe: true, + unused: true, } input: { var print = console.log.bind(console); @@ -1659,6 +1664,7 @@ iife_2: { }(foo); } expect: { + var foo; !function(x) { console.log(x); }(bar()); @@ -1945,6 +1951,7 @@ ref_scope: { chained_1: { options = { collapse_vars: true, + unused: true, } input: { var a = 2; @@ -1961,6 +1968,7 @@ chained_1: { chained_2: { options = { collapse_vars: true, + unused: true, } input: { var a; @@ -2061,6 +2069,7 @@ inner_lvalues: { double_def: { options = { collapse_vars: true, + unused: true, } input: { var a = x, a = a && y; @@ -2075,6 +2084,7 @@ double_def: { toplevel_single_reference: { options = { collapse_vars: true, + unused: true, } input: { var a; @@ -2084,9 +2094,10 @@ toplevel_single_reference: { } } expect: { - var a; - for (var b in x) + for (var b in x) { + var a; b(a = b); + } } } @@ -2889,6 +2900,7 @@ pure_getters_chain: { options = { collapse_vars: true, pure_getters: true, + unused: true, } input: { function o(t, r) { @@ -2909,6 +2921,7 @@ pure_getters_chain: { conditional_1: { options = { collapse_vars: true, + unused: true, } input: { function f(a, b) { @@ -2933,6 +2946,7 @@ conditional_1: { conditional_2: { options = { collapse_vars: true, + unused: true, } input: { function f(a, b) { @@ -3015,3 +3029,51 @@ issue_2425_3: { } expect_stdout: "15" } + +issue_2437: { + options = { + collapse_vars: true, + conditionals: true, + inline: true, + join_vars: true, + reduce_vars: true, + side_effects: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + function foo() { + bar(); + } + function bar() { + if (xhrDesc) { + var req = new XMLHttpRequest(); + var result = !!req.onreadystatechange; + Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {}); + return result; + } + else { + var req = new XMLHttpRequest(); + var detectFunc = function () { }; + req.onreadystatechange = detectFunc; + var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc; + req.onreadystatechange = null; + return result; + } + } + foo(); + } + expect: { + !function() { + if (xhrDesc) + return result = !!(req = new XMLHttpRequest()).onreadystatechange, + Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}), + result; + var req = new XMLHttpRequest(), detectFunc = function() {}; + req.onreadystatechange = detectFunc; + var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc; + req.onreadystatechange = null; + }(); + } +} diff --git a/test/compress/issue-973.js b/test/compress/issue-973.js index 30f886a8..fee05dfc 100644 --- a/test/compress/issue-973.js +++ b/test/compress/issue-973.js @@ -51,6 +51,7 @@ this_binding_collapse_vars: { options = { collapse_vars: true, toplevel: true, + unused: true, }; input: { var c = a; c(); diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 7714ad5d..1acd902b 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -2131,14 +2131,13 @@ redefine_farg_1: { } expect: { function f(a) { - var a; return typeof a; } function g() { - return"number"; + return "number"; } function h(a, b) { - var a = b; + a = b; return typeof a; } console.log(f([]), g([]), h([])); @@ -2173,10 +2172,9 @@ redefine_farg_2: { } expect: { console.log(function(a) { - var a; return typeof a; }([]), "number",function(a, b) { - var a = b; + a = b; return typeof a; }([])); } @@ -2185,11 +2183,13 @@ redefine_farg_2: { redefine_farg_3: { options = { + cascade: true, evaluate: true, inline: true, keep_fargs: false, - passes: 3, + passes: 2, reduce_vars: true, + sequences: true, side_effects: true, toplevel: true, unused: true, From 2cfb5aa7dadd744bf7fbe16756696fc595f134a2 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 6 Nov 2017 16:10:57 +0800 Subject: [PATCH 3/6] account for `eval` & `with` in `reduce_vars` (#2441) fixes #2440 --- lib/compress.js | 15 ++--- test/compress/reduce_vars.js | 112 +++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 7 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index d8482337..c1232420 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -317,7 +317,7 @@ merge(Compressor.prototype, { d.fixed = false; } else if (d.fixed) { var value = node.fixed_value(); - if (unused && value && d.references.length == 1) { + if (value && ref_once(d)) { if (value instanceof AST_Lambda) { d.single_use = d.scope === node.scope && !(d.orig[0] instanceof AST_SymbolFunarg) @@ -385,7 +385,7 @@ merge(Compressor.prototype, { } else { d.fixed = node; mark(d, true); - if (unused && d.references.length == 1) { + if (ref_once(d)) { d.single_use = d.scope === d.references[0].scope || node.is_constant_expression(d.references[0].scope); } @@ -564,7 +564,7 @@ merge(Compressor.prototype, { function reset_def(def) { def.direct_access = false; def.escaped = false; - if (def.scope.uses_eval) { + if (def.scope.uses_eval || def.scope.uses_with) { def.fixed = false; } else if (!compressor.exposed(def)) { def.fixed = undefined; @@ -576,6 +576,10 @@ merge(Compressor.prototype, { def.single_use = undefined; } + function ref_once(def) { + return unused && !def.scope.uses_eval && !def.scope.uses_with && def.references.length == 1; + } + function is_immutable(value) { if (!value) return false; return value.is_constant() @@ -4237,10 +4241,7 @@ merge(Compressor.prototype, { if (fixed instanceof AST_Defun) { d.fixed = fixed = make_node(AST_Function, fixed, fixed); } - if (compressor.option("unused") - && fixed - && d.references.length == 1 - && d.single_use) { + if (fixed && d.single_use) { var value = fixed.optimize(compressor); return value === fixed ? fixed.clone(true) : value; } diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 1acd902b..25f95ff8 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -3542,3 +3542,115 @@ issue_2423_6: { "2", ] } + +issue_2440_eval_1: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function foo() { + return bar(); + } + baz = { + quux: foo + }; + exec = function() { + return eval("foo()"); + }; + } + expect: { + function foo() { + return bar(); + } + baz = { + quux: foo + }; + exec = function() { + return eval("foo()"); + }; + } +} + +issue_2440_eval_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + baz = { + quux: foo + }; + exec = function() { + return eval("foo()"); + }; + function foo() { + return bar(); + } + } + expect: { + baz = { + quux: foo + }; + exec = function() { + return eval("foo()"); + }; + function foo() { + return bar(); + } + } +} + +issue_2440_with_1: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function foo() { + return bar(); + } + baz = { + quux: foo + }; + with (o) whatever(); + } + expect: { + function foo() { + return bar(); + } + baz = { + quux: foo + }; + with (o) whatever(); + } +} + +issue_2440_with_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + baz = { + quux: foo + }; + with (o) whatever(); + function foo() { + return bar(); + } + } + expect: { + baz = { + quux: foo + }; + with (o) whatever(); + function foo() { + return bar(); + } + } +} From bbedbf4ea03580c5b7aa39f32b92fdda9216c5b4 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 7 Nov 2017 02:37:23 +0800 Subject: [PATCH 4/6] handle circular `function` reference gracefully (#2446) fixes #2442 --- lib/compress.js | 15 ++- test/compress/reduce_vars.js | 191 +++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 2 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index c1232420..274ab604 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4242,8 +4242,19 @@ merge(Compressor.prototype, { d.fixed = fixed = make_node(AST_Function, fixed, fixed); } if (fixed && d.single_use) { - var value = fixed.optimize(compressor); - return value === fixed ? fixed.clone(true) : value; + var recurse; + if (fixed instanceof AST_Function) { + for (var i = 0; recurse = compressor.parent(i); i++) { + if (recurse instanceof AST_Lambda) { + var name = recurse.name; + if (name && name.definition() === d) break; + } + } + } + if (!recurse) { + var value = fixed.optimize(compressor); + return value === fixed ? fixed.clone(true) : value; + } } if (fixed && d.should_replace === undefined) { var init; diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 25f95ff8..f1a27ff9 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -3654,3 +3654,194 @@ issue_2440_with_2: { } } } + +issue_2442: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function foo() { + foo(); + } + } + expect: {} +} + +recursive_inlining_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + !function() { + function foo() { bar(); } + function bar() { foo(); } + console.log("PASS"); + }(); + } + expect: { + !function() { + console.log("PASS"); + }(); + } + expect_stdout: "PASS" +} + +recursive_inlining_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + !function() { + function foo() { qux(); } + function bar() { foo(); } + function qux() { bar(); } + console.log("PASS"); + }(); + } + expect: { + !function() { + console.log("PASS"); + }(); + } + expect_stdout: "PASS" +} + +recursive_inlining_3: { + options = { + reduce_vars: true, + unused: true, + } + input: { + !function() { + function foo(x) { console.log("foo", x); if (x) bar(x-1); } + function bar(x) { console.log("bar", x); if (x) qux(x-1); } + function qux(x) { console.log("qux", x); if (x) foo(x-1); } + qux(4); + }(); + } + expect: { + !function() { + function qux(x) { + console.log("qux", x); + if (x) (function(x) { + console.log("foo", x); + if (x) (function(x) { + console.log("bar", x); + if (x) qux(x - 1); + })(x - 1); + })(x - 1); + } + qux(4); + }(); + } + expect_stdout: [ + "qux 4", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + ] +} + +recursive_inlining_4: { + options = { + reduce_vars: true, + unused: true, + } + input: { + !function() { + function foo(x) { console.log("foo", x); if (x) bar(x-1); } + function bar(x) { console.log("bar", x); if (x) qux(x-1); } + function qux(x) { console.log("qux", x); if (x) foo(x-1); } + qux(4); + bar(5); + }(); + } + expect: { + !function() { + function bar(x) { + console.log("bar", x); + if (x) qux(x - 1); + } + function qux(x) { + console.log("qux", x); + if (x) (function(x) { + console.log("foo", x); + if (x) bar(x - 1); + })(x - 1); + } + qux(4); + bar(5); + }(); + } + expect_stdout: [ + "qux 4", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + "bar 5", + "qux 4", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + ] +} + +recursive_inlining_5: { + options = { + reduce_vars: true, + unused: true, + } + input: { + !function() { + function foo(x) { console.log("foo", x); if (x) bar(x-1); } + function bar(x) { console.log("bar", x); if (x) qux(x-1); } + function qux(x) { console.log("qux", x); if (x) foo(x-1); } + qux(4); + bar(5); + foo(3); + }(); + } + expect: { + !function() { + function foo(x) { + console.log("foo", x); + if (x) bar(x - 1); + } + function bar(x) { + console.log("bar", x); + if (x) qux(x - 1); + } + function qux(x) { + console.log("qux", x); + if (x) foo(x - 1); + } + qux(4); + bar(5); + foo(3); + }(); + } + expect_stdout: [ + "qux 4", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + "bar 5", + "qux 4", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + ] +} From 38bfb73f0643d73d429d7a79667f6b8fa3fd6fc5 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 7 Nov 2017 03:55:16 +0800 Subject: [PATCH 5/6] v3.1.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1f490865..5bcb54f1 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.1.7", + "version": "3.1.8", "engines": { "node": ">=0.8.0" }, From bcf95ac02c77efb1db350bb9ff021e86944b286c Mon Sep 17 00:00:00 2001 From: alexlamsl Date: Tue, 7 Nov 2017 04:49:57 +0800 Subject: [PATCH 6/6] update tests --- test/compress/collapse_vars.js | 8 ++++++-- test/compress/issue-2001.js | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index efb266ff..4e7bd323 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -2226,10 +2226,11 @@ unused_orig: { expect: { var a = 1; console.log(function(b) { - var a; var c = b; - for (var d in c) + for (var d in c) { + var a; return --b + (a = c[0]); + } a && a.NaN; }([2]), a); } @@ -2576,7 +2577,9 @@ issue_2250_1: { options = { collapse_vars: true, conditionals: true, + passes: 2, reduce_vars: true, + unused: true, } input: { function f(x) { @@ -2618,6 +2621,7 @@ issue_2250_2: { reduce_vars: true, side_effects: true, toplevel: true, + unused: true, } input: { { diff --git a/test/compress/issue-2001.js b/test/compress/issue-2001.js index 3938072a..f2c9504b 100644 --- a/test/compress/issue-2001.js +++ b/test/compress/issue-2001.js @@ -177,6 +177,7 @@ export_mangle_2: { export_mangle_3: { options = { collapse_vars: true, + unused: true, } mangle = { toplevel: true, @@ -195,6 +196,7 @@ export_mangle_3: { export_mangle_4: { options = { collapse_vars: true, + unused: true, } mangle = { toplevel: true,