From 9b58b54e2d7ce4ab2c015611add52c7475de9eab Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 4 Jan 2018 12:58:40 +0800 Subject: [PATCH] extend `inline` (#2714) - compress `function` with variables within loops - restrict to `AST_Var` for better compatibility with ES6+ --- lib/compress.js | 57 +++++++++++++++++++++++--------------- test/compress/functions.js | 8 ++++-- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 300bc61d..85bb0910 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4109,7 +4109,7 @@ merge(Compressor.prototype, { stat = null; for (var i = 0; i < len; i++) { var line = fn.body[i]; - if (line instanceof AST_Definitions) { + if (line instanceof AST_Var) { if (stat && !all(line.definitions, function(var_def) { return !var_def.value; })) { @@ -4124,7 +4124,7 @@ merge(Compressor.prototype, { return return_value(stat); } - function can_inject_args(catches, defs, safe_to_inject) { + function can_inject_args(catches, safe_to_inject) { for (var i = 0, len = fn.argnames.length; i < len; i++) { var arg = fn.argnames[i]; if (arg.__unused) continue; @@ -4143,7 +4143,7 @@ merge(Compressor.prototype, { var len = fn.body.length; for (var i = 0; i < len; i++) { var stat = fn.body[i]; - if (!(stat instanceof AST_Definitions)) continue; + if (!(stat instanceof AST_Var)) continue; if (!safe_to_inject) return false; for (var j = stat.definitions.length; --j >= 0;) { var name = stat.definitions[j].name; @@ -4152,6 +4152,7 @@ merge(Compressor.prototype, { || scope.var_names()[name.name]) { return false; } + if (in_loop) in_loop.push(name.definition()); } } return true; @@ -4171,8 +4172,8 @@ merge(Compressor.prototype, { } while (!(scope instanceof AST_Scope)); var safe_to_inject = !(scope instanceof AST_Toplevel) || compressor.toplevel.vars; var inline = compressor.option("inline"); - if (!can_inject_vars(catches, !in_loop && inline >= 3 && safe_to_inject)) return false; - if (!can_inject_args(catches, in_loop, inline >= 2 && safe_to_inject)) return false; + if (!can_inject_vars(catches, inline >= 3 && safe_to_inject)) return false; + if (!can_inject_args(catches, inline >= 2 && safe_to_inject)) return false; return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop); } @@ -4181,13 +4182,13 @@ merge(Compressor.prototype, { scope.var_names()[name.name] = true; scope.variables.set(name.name, def); scope.enclosed.push(def); - decls.push(make_node(AST_VarDef, name, { + decls.unshift(make_node(AST_VarDef, name, { name: name, value: null })); var sym = make_node(AST_SymbolRef, name, name); def.references.push(sym); - if (value) expressions.push(make_node(AST_Assign, self, { + if (value) expressions.unshift(make_node(AST_Assign, self, { operator: "=", left: sym, right: value @@ -4195,11 +4196,15 @@ merge(Compressor.prototype, { } function flatten_args(decls, expressions) { - for (var len = fn.argnames.length, i = len; --i >= 0;) { + var len = fn.argnames.length; + for (var i = self.args.length; --i >= len;) { + expressions.unshift(self.args[i]); + } + for (i = len; --i >= 0;) { var name = fn.argnames[i]; var value = self.args[i]; if (name.__unused || scope.var_names()[name.name]) { - if (value) expressions.push(value); + if (value) expressions.unshift(value); } else { var symbol = make_node(AST_SymbolVar, name, name); name.definition().orig.push(symbol); @@ -4207,30 +4212,38 @@ merge(Compressor.prototype, { append_var(decls, expressions, symbol, value); } } - decls.reverse(); - expressions.reverse(); - for (i = len, len = self.args.length; i < len; i++) { - expressions.push(self.args[i]); - } } - function flatten_body(decls, expressions) { - for (i = 0, len = fn.body.length; i < len; i++) { + function flatten_vars(decls, expressions) { + if (in_loop) in_loop.length = 0; + for (var i = fn.body.length; --i >= 0;) { var stat = fn.body[i]; - if (stat instanceof AST_Definitions) { - stat.definitions.forEach(function(var_def) { - append_var(decls, expressions, var_def.name, var_def.value); - }); + if (!(stat instanceof AST_Var)) continue; + for (var j = stat.definitions.length; --j >= 0;) { + var var_def = stat.definitions[j]; + var name = var_def.name; + append_var(decls, expressions, name, var_def.value); + if (in_loop) { + var def = name.definition(); + var sym = make_node(AST_SymbolRef, name, name); + def.references.push(sym); + in_loop.unshift(make_node(AST_Assign, var_def, { + operator: "=", + left: sym, + right: make_node(AST_Undefined, name) + })); + } } } - expressions.push(value); + if (in_loop) [].unshift.apply(expressions, in_loop); } function flatten_fn() { var decls = []; var expressions = []; + flatten_vars(decls, expressions); flatten_args(decls, expressions); - flatten_body(decls, expressions); + expressions.push(value); if (decls.length) { i = scope.body.indexOf(compressor.parent(level - 1)) + 1; scope.body.splice(i, 0, make_node(AST_Var, fn, { diff --git a/test/compress/functions.js b/test/compress/functions.js index b6b68982..ff3baeb2 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -1299,6 +1299,7 @@ issue_2630_2: { passes: 2, reduce_vars: true, sequences: true, + side_effects: true, unused: true, } input: { @@ -1905,6 +1906,7 @@ inline_true: { use_before_init_in_loop: { options = { inline: true, + side_effects: true, toplevel: true, } input: { @@ -1918,9 +1920,9 @@ use_before_init_in_loop: { } expect: { var a = "PASS"; - for (var b = 2; --b >= 0;) (function() { - var c = (c && (a = "FAIL"), 1); - })(); + for (var b = 2; --b >= 0;) + c = void 0, c = (c && (a = "FAIL"), 1); + var c; console.log(a); } expect_stdout: "PASS" -- 2.34.1