diff --git a/lib/compress.js b/lib/compress.js index f028deee..cf79b559 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -366,7 +366,7 @@ merge(Compressor.prototype, { safe_ids = save_ids; return true; } - if (node instanceof AST_Function || node instanceof AST_Arrow) { + if (is_func_expr(node)) { push(); var iife; if (!node.name @@ -564,6 +564,10 @@ merge(Compressor.prototype, { return orig.length == 1 && orig[0] instanceof AST_SymbolLambda; }); + function is_func_expr(node) { + return node instanceof AST_Arrow || node instanceof AST_Function; + } + function is_lhs_read_only(lhs) { if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda; if (lhs instanceof AST_PropAccess) { @@ -707,7 +711,7 @@ merge(Compressor.prototype, { function is_iife_call(node) { if (node instanceof AST_Call && !(node instanceof AST_New)) { - return node.expression instanceof AST_Function || is_iife_call(node.expression); + return is_func_expr(node.expression) || is_iife_call(node.expression); } return false; } @@ -1320,6 +1324,7 @@ merge(Compressor.prototype, { return false; }); def(AST_Function, return_false); + def(AST_Arrow, return_false); def(AST_UnaryPostfix, return_false); def(AST_UnaryPrefix, function() { return this.operator == "void"; @@ -1593,9 +1598,6 @@ merge(Compressor.prototype, { def(AST_Lambda, function(){ throw def; }); - def(AST_Arrow, function() { - throw def; - }); def(AST_Class, function() { throw def; }); @@ -1649,8 +1651,7 @@ merge(Compressor.prototype, { case "typeof": // Function would be evaluated to an array and so typeof would // incorrectly return 'object'. Hence making is a special case. - if (e instanceof AST_Function || - e instanceof AST_Arrow) return typeof function(){}; + if (is_func_expr(e)) return typeof function(){}; e = ev(e, compressor); @@ -1822,6 +1823,9 @@ merge(Compressor.prototype, { def(AST_Function, function(){ return basic_negation(this); }); + def(AST_Arrow, function(){ + return basic_negation(this); + }); def(AST_UnaryPrefix, function(){ if (this.operator == "!") return this.expression; @@ -2568,7 +2572,7 @@ merge(Compressor.prototype, { def(AST_This, return_null); def(AST_Call, function(compressor, first_in_statement){ if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) { - if (this.expression instanceof AST_Function + if (is_func_expr(this.expression) && (!this.expression.name || !this.expression.name.definition().references.length)) { var node = this.clone(); node.expression.process_expression(false, compressor); @@ -3095,10 +3099,10 @@ merge(Compressor.prototype, { }); if (compressor.option("unused") && simple_args - && (fn instanceof AST_Function + && (is_func_expr(fn) || compressor.option("reduce_vars") && fn instanceof AST_SymbolRef - && (fn = fn.fixed_value()) instanceof AST_Function) + && is_func_expr(fn = fn.fixed_value())) && !fn.uses_arguments && !fn.uses_eval) { var pos = 0, last = 0; @@ -3272,7 +3276,7 @@ merge(Compressor.prototype, { if (self.args.length == 0) return make_node(AST_Function, self, { argnames: [], body: [] - }); + }).optimize(compressor); if (all(self.args, function(x) { return x instanceof AST_String; })) { @@ -3295,12 +3299,18 @@ merge(Compressor.prototype, { var fun; ast.walk(new TreeWalker(function(node) { if (fun) return true; - if (node instanceof AST_Function) { + if (is_func_expr(node)) { fun = node; return true; } })); - if (!fun) return self; + if (fun.body instanceof AST_Node) { + fun.body = [ + make_node(AST_Return, fun.body, { + value: fun.body + }) + ]; + } var args = fun.argnames.map(function(arg, i) { return make_node(AST_String, self.args[i], { value: arg.print_to_string() @@ -3324,7 +3334,14 @@ merge(Compressor.prototype, { } } } - var stat = fn instanceof AST_Function && fn.body[0]; + var stat = is_func_expr(fn) && fn.body; + if (stat instanceof AST_Node) { + stat = make_node(AST_Return, stat, { + value: stat + }); + } else if (stat) { + stat = stat[0]; + } if (compressor.option("inline") && stat instanceof AST_Return) { var value = stat.value; if (!value || value.is_constant_expression()) { @@ -3332,10 +3349,10 @@ merge(Compressor.prototype, { return make_sequence(self, args).transform(compressor); } } - if (exp instanceof AST_Function && !exp.is_generator && !exp.async) { + if (is_func_expr(exp) && !exp.is_generator && !exp.async) { if (compressor.option("inline") && !exp.name - && exp.body.length == 1 + && (exp.body instanceof AST_Node || exp.body.length == 1) && !exp.uses_arguments && !exp.uses_eval && simple_args @@ -3385,6 +3402,13 @@ merge(Compressor.prototype, { value: value })); var body = fn.transform(compressor).body; + if (body instanceof AST_Node) { + body = [ + make_node(AST_Return, body, { + value: body + }) + ]; + } if (body.length == 0) return make_node(AST_Undefined, self); if (body.length == 1 && body[0] instanceof AST_Return) { value = body[0].value; @@ -3411,7 +3435,7 @@ merge(Compressor.prototype, { if (value !== self) return value; } } - if (compressor.option("side_effects") && all(exp.body, is_empty)) { + if (compressor.option("side_effects") && !(exp.body instanceof AST_Node) && all(exp.body, is_empty)) { var args = self.args.concat(make_node(AST_Undefined, self)); return make_sequence(self, args).transform(compressor); } @@ -4085,7 +4109,7 @@ merge(Compressor.prototype, { d.fixed = fixed = make_node(AST_Function, fixed, fixed); } if (compressor.option("unused") - && fixed instanceof AST_Function + && is_func_expr(fixed) && d.references.length == 1 && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) && !d.scope.uses_eval diff --git a/test/compress/arrow.js b/test/compress/arrow.js index 9ed08801..02b39571 100644 --- a/test/compress/arrow.js +++ b/test/compress/arrow.js @@ -280,3 +280,261 @@ issue_27: { })(jQuery); } } + +issue_2105_1: { + options = { + arrows: true, + collapse_vars: true, + ecma: 6, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + !function(factory) { + factory(); + }( function() { + return function(fn) { + fn()().prop(); + }( function() { + function bar() { + var quux = function() { + console.log("PASS"); + }, foo = function() { + console.log; + quux(); + }; + return { prop: foo }; + } + return bar; + } ); + }); + } + expect: { + !void (() => { + var quux = () => { + console.log("PASS"); + }; + return { + prop: () => { + console.log; + quux(); + } + }; + })().prop(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_2105_2: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + ((factory) => { + factory(); + })( () => { + return ((fn) => { + fn()().prop(); + })( () => { + let bar = () => { + var quux = () => { + console.log("PASS"); + }, foo = () => { + console.log; + quux(); + }; + return { prop: foo }; + }; + return bar; + } ); + }); + } + expect: { + !void (() => { + var quux = () => { + console.log("PASS"); + }; + return { + prop: () => { + console.log; + quux(); + } + }; + })().prop(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_2136_2: { + options = { + arrows: true, + collapse_vars: true, + ecma: 6, + inline: true, + side_effects: true, + unused: true, + } + input: { + function f(x) { + console.log(x); + } + !function(a, ...b) { + f(b[0]); + }(1, 2, 3); + } + expect: { + function f(x) { + console.log(x); + } + f([2,3][0]); + } + expect_stdout: "2" + node_version: ">=6" +} + +issue_2136_3: { + options = { + arrows: true, + collapse_vars: true, + ecma: 6, + evaluate: true, + inline: true, + passes: 3, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + function f(x) { + console.log(x); + } + !function(a, ...b) { + f(b[0]); + }(1, 2, 3); + } + expect: { + console.log(2); + } + expect_stdout: "2" + node_version: ">=6" +} + +call_args: { + options = { + arrows: true, + ecma: 6, + evaluate: true, + inline: true, + reduce_vars: true, + } + input: { + const a = 1; + console.log(a); + +function(a) { + return a; + }(a); + } + expect: { + const a = 1; + console.log(1); + +(1, 1); + } + expect_stdout: true +} + +call_args_drop_param: { + options = { + arrows: true, + ecma: 6, + evaluate: true, + inline: true, + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + const a = 1; + console.log(a); + +function(a) { + return a; + }(a, b); + } + expect: { + const a = 1; + console.log(1); + +(b, 1); + } + expect_stdout: true +} + +issue_485_crashing_1530: { + options = { + arrows: true, + conditionals: true, + dead_code: true, + ecma: 6, + evaluate: true, + inline: true, + } + input: { + (function(a) { + if (true) return; + var b = 42; + })(this); + } + expect: { + this, void 0; + } +} + +issue_2084: { + options = { + arrows: true, + collapse_vars: true, + conditionals: true, + ecma: 6, + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var c = 0; + !function() { + !function(c) { + c = 1 + c; + var c = 0; + function f14(a_1) { + if (c = 1 + c, 0 !== 23..toString()) + c = 1 + c, a_1 && (a_1[0] = 0); + } + f14(); + }(-1); + }(); + console.log(c); + } + expect: { + var c = 0; + !((c) => { + c = 1 + c, + c = 1 + (c = 0), + 0 !== 23..toString() && (c = 1 + c); + })(-1), + console.log(c); + } + expect_stdout: "0" + node_version: ">=4" +} diff --git a/test/compress/issue-203.js b/test/compress/issue-203.js index 94f43b4f..c5722273 100644 --- a/test/compress/issue-203.js +++ b/test/compress/issue-203.js @@ -49,8 +49,8 @@ compress_new_function_with_destruct_arrows: { new Function("[[aa]], [{bb}]", 'return aa;'); } expect: { - Function("aa, [bb]", 'return aa;'); - Function("aa, {bb}", 'return aa;'); - Function("[[aa]], [{bb}]", 'return aa;'); + Function("N", "[a]", 'return N'); + Function("b", "{bb:N}", 'return b'); + Function("[[b]]", "[{bb:N}]", 'return b'); } }