diff --git a/lib/compress.js b/lib/compress.js index 0fea284d93..1bfdd75e22 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2143,12 +2143,16 @@ Compressor.prototype.compress = function(node) { if (sequencesize_2(statements, compressor)) changed = 7; if (!changed && last_changed == 7) break; } - if (compressor.option("join_vars")) { - if (join_consecutive_vars(statements)) changed = 8; + if (compressor.option("arrows") && compressor.option("module")) { + if (arrowify(statements)) changed = 8; if (!changed && last_changed == 8) break; } + if (compressor.option("join_vars")) { + if (join_consecutive_vars(statements)) changed = 9; + if (!changed && last_changed == 9) break; + } if (compressor.option("collapse_vars")) { - if (collapse(statements, compressor)) changed = 9; + if (collapse(statements, compressor)) changed = 10; } } while (changed && max_iter-- > 0); return statements; @@ -4252,6 +4256,48 @@ Compressor.prototype.compress = function(node) { } } + function arrowify(statements) { + var changed = false, defns = [], len = 0; + statements.forEach(function(stat) { + var def; + if ((stat instanceof AST_AsyncDefun || stat instanceof AST_Defun) + && !stat.uses_arguments + && !stat.pinned() + && (def = stat.name.definition()).escaped + && def.escaped.depth != 1 + && !compressor.exposed(def) + && !stat.contains_this()) { + var name = make_node(AST_SymbolVar, stat.name); + var arrow = make_node(is_async(stat) ? AST_AsyncArrow : AST_Arrow, stat); + arrow.init_vars(stat.parent_scope, stat); + arrow.variables.del("arguments"); + var defn = make_node(AST_VarDef, stat, { + name: name, + value: arrow, + }); + defns.push(defn); + def.orig.push(name); + def.eliminated++; + if (def.fixed) { + var fixed = function() { + return defn.value; + }; + fixed.assigns = [ defn ]; + if (def.fixed === stat) def.fixed = fixed; + def.references.forEach(function(node) { + if (node.fixed === stat) node.fixed = fixed; + }); + } + changed = true; + } else { + statements[len++] = stat; + } + }); + statements.length = len; + if (defns.length > 0) statements.unshift(make_node(AST_Var, compressor.self(), { definitions: defns })); + return changed; + } + function extract_exprs(body) { if (body instanceof AST_Assign) return [ body ]; if (body instanceof AST_Sequence) return body.expressions.slice(); @@ -11138,7 +11184,7 @@ Compressor.prototype.compress = function(node) { && !exp.uses_arguments && !exp.pinned() && !exp.contains_this()) { - var arrow = make_node(is_async(exp) ? AST_AsyncArrow : AST_Arrow, exp, exp); + var arrow = make_node(is_async(exp) ? AST_AsyncArrow : AST_Arrow, exp); arrow.init_vars(exp.parent_scope, exp); arrow.variables.del("arguments"); self.expression = arrow.transform(compressor); @@ -12807,8 +12853,7 @@ Compressor.prototype.compress = function(node) { var value; if (def.recursive_refs > 0) { value = fixed.clone(true); - var defun_def = value.name.definition(); - var lambda_def = value.variables.get(value.name.name); + var lambda_def = value.variables.get(self.name); var name = lambda_def && lambda_def.orig[0]; var def_fn_name, symbol_type; if (value instanceof AST_Class) { @@ -12816,41 +12861,41 @@ Compressor.prototype.compress = function(node) { symbol_type = AST_SymbolClass; } else { def_fn_name = "def_variable"; - symbol_type = AST_SymbolLambda; + symbol_type = value.name ? AST_SymbolLambda : AST_SymbolVar; } if (!(name instanceof symbol_type)) { name = make_node(symbol_type, value.name); name.scope = value; - value.name = name; + if (value.name) value.name = name; lambda_def = value[def_fn_name](name); lambda_def.recursive_refs = def.recursive_refs; } value.walk(new TreeWalker(function(node) { if (node instanceof AST_SymbolDeclaration) { if (node !== name) { - var def = node.definition(); - def.orig.push(node); - def.eliminated++; + var d = node.definition(); + d.orig.push(node); + d.eliminated++; } return; } if (!(node instanceof AST_SymbolRef)) return; - var def = node.definition(); - if (def === defun_def) { - node.thedef = def = lambda_def; + var d = node.definition(); + if (d === def) { + node.thedef = d = lambda_def; } else { - def.single_use = false; + d.single_use = false; var fn = node.fixed_value(); if (is_lambda(fn) && fn.name - && fn.name.definition() === def - && def.scope === fn.name.scope - && fixed.variables.get(fn.name.name) === def) { + && fn.name.definition() === d + && d.scope === fn.name.scope + && fixed.variables.get(fn.name.name) === d) { fn.name = fn.name.clone(); - node.thedef = def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name); + node.thedef = d = value.variables.get(fn.name.name) || value[def_fn_name](fn.name); } } - def.references.push(node); + d.references.push(node); })); } else { if (fixed instanceof AST_Scope) { diff --git a/test/compress/arrows.js b/test/compress/arrows.js index 8d778addb9..9b5ab9477f 100644 --- a/test/compress/arrows.js +++ b/test/compress/arrows.js @@ -829,6 +829,143 @@ keep_new_var: { node_version: ">=4" } +arrowify: { + options = { + arrows: true, + module: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + return "foo"; + } + console.log(typeof f()); + } + expect: { + console.log(typeof (() => "foo")()); + } + expect_stdout: "string" + node_version: ">=4" +} + +no_arrowify: { + options = { + arrows: true, + module: true, + reduce_vars: true, + toplevel: false, + unused: true, + } + input: { + // may be referenced by prepending code + function f() { + return "foo"; + } + console.log(typeof f()); + } + expect: { + function f() { + return "foo"; + } + console.log(typeof f()); + } + expect_stdout: "string" +} + +no_arrowify_new: { + options = { + arrows: true, + module: true, + reduce_vars: true, + toplevel: true, + } + input: { + function f() {} + console.log(typeof new f(f)); + } + expect: { + function f() {} + console.log(typeof new f(f)); + } + expect_stdout: "object" +} + +arrowify_new: { + options = { + arrows: true, + module: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + console.log("PASS"); + } + new f(f); + } + expect: { + (() => { + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +arrowify_recursive: { + options = { + arrows: true, + inline: true, + module: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + return (() => f)(); + } + console.log(typeof f()); + } + expect: { + var f = () => f; + console.log(typeof f); + } + expect_stdout: "function" + node_version: ">=4" +} + +arrowify_farg: { + options = { + arrows: true, + module: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + console.log(a); + } + f("PASS"); + function g() {} + f; + } + expect: { + var f = a => { + console.log(a); + }; + f("PASS"); + f; + } + expect_stdout: "PASS" + node_version: ">=4" +} + issue_4388: { options = { inline: true,