From 8c2b76eff98fbdfc7ed2154e75a86b028b25345f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 17 Jan 2022 15:18:42 +0000 Subject: [PATCH] enhance `side_effects` (#5304) --- lib/compress.js | 57 ++++++++++++++++++++++++++++------------ test/compress/spreads.js | 24 ++++++++++++++++- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 7729ffde..1807bbfe 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -5303,21 +5303,43 @@ Compressor.prototype.compress = function(node) { return map && map[prop]; }); - function spread_side_effects(exp) { - while ((exp = exp.tail_node()) instanceof AST_SymbolRef) { - exp = exp.fixed_value(); - if (!exp) return true; - } - return !(exp instanceof AST_Array - || exp.TYPE == "Binary" && !lazy_op[exp.operator] - || exp instanceof AST_Constant - || exp instanceof AST_Lambda - || exp instanceof AST_Object && all(exp.properties, function(prop) { + // determine if object spread syntax may cause runtime exception + (function(def) { + def(AST_Node, return_false); + def(AST_Array, return_true); + def(AST_Assign, function() { + switch (this.operator) { + case "=": + return this.right.safe_to_spread(); + case "&&=": + case "||=": + case "??=": + return this.left.safe_to_spread() && this.right.safe_to_spread(); + } + return true; + }); + def(AST_Binary, function() { + return !lazy_op[this.operator] || this.left.safe_to_spread() && this.right.safe_to_spread(); + }); + def(AST_Constant, return_true); + def(AST_Lambda, return_true); + def(AST_Object, function() { + return all(this.properties, function(prop) { return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread); - }) - || exp instanceof AST_ObjectIdentity - || exp instanceof AST_Unary); - } + }); + }); + def(AST_ObjectIdentity, return_true); + def(AST_Sequence, function() { + return this.tail_node().safe_to_spread(); + }); + def(AST_SymbolRef, function() { + var fixed = this.fixed_value(); + return fixed && fixed.safe_to_spread(); + }); + def(AST_Unary, return_true); + })(function(node, func) { + node.DEFMETHOD("safe_to_spread", func); + }); // determine if expression has side effects (function(def) { @@ -5329,7 +5351,8 @@ Compressor.prototype.compress = function(node) { }); } function array_spread(node, compressor) { - return !node.expression.is_string(compressor) || node.expression.has_side_effects(compressor); + var exp = node.expression; + return !exp.is_string(compressor) || exp.has_side_effects(compressor); } def(AST_Node, return_true); def(AST_Array, function(compressor) { @@ -5411,7 +5434,7 @@ Compressor.prototype.compress = function(node) { def(AST_Object, function(compressor) { return any(this.properties, compressor, function(node, compressor) { var exp = node.expression; - return spread_side_effects(exp) || exp.has_side_effects(compressor); + return !exp.safe_to_spread() || exp.has_side_effects(compressor); }); }); def(AST_ObjectIdentity, return_false); @@ -8387,7 +8410,7 @@ Compressor.prototype.compress = function(node) { }); var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) { var exp = node.expression; - return spread_side_effects(exp) ? node : exp.drop_side_effect_free(compressor, first_in_statement); + return exp.safe_to_spread() ? exp.drop_side_effect_free(compressor, first_in_statement) : node; }); if (!values) return null; if (values === exprs && !all(values, function(node) { diff --git a/test/compress/spreads.js b/test/compress/spreads.js index 13b12af8..49ebc2a8 100644 --- a/test/compress/spreads.js +++ b/test/compress/spreads.js @@ -241,7 +241,29 @@ drop_empty_call_2: { node_version: ">=6" } -convert_hole: { +convert_hole_array: { + options = { + spreads: true, + } + input: { + [ ...[ "PASS", , 42 ] ].forEach(function(a) { + console.log(a); + }); + } + expect: { + [ "PASS", void 0, 42 ].forEach(function(a) { + console.log(a); + }); + } + expect_stdout: [ + "PASS", + "undefined", + "42", + ] + node_version: ">=6" +} + +convert_hole_call: { options = { spreads: true, }