From 027e30b993cc0b759dd5d67b3a1249cba2666de8 Mon Sep 17 00:00:00 2001 From: alexlamsl Date: Sat, 15 Jun 2024 13:10:19 +0300 Subject: [PATCH] enhance `dead_code` & `side_effects` --- lib/ast.js | 2 -- lib/compress.js | 59 ++++++++++++++++++++++++--------- test/compress/arrows.js | 2 +- test/compress/functions.js | 2 +- test/compress/side_effects.js | 62 +++++++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 20 deletions(-) diff --git a/lib/ast.js b/lib/ast.js index 16ea9c805e5..1a79bf01f6a 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -169,8 +169,6 @@ DEF_BITPROPS(AST_Node, [ "private", // AST_Call "pure", - // AST_Assign - "redundant", // AST_Node "single_use", // AST_ClassProperty diff --git a/lib/compress.js b/lib/compress.js index 5cf2a2e6613..ad9a175c1aa 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -638,15 +638,26 @@ Compressor.prototype.compress = function(node) { } function push(tw, sequential) { + tw.defined_ids = Object.create(tw.defined_ids); var safe_ids = Object.create(tw.safe_ids); if (!sequential) safe_ids.seq = {}; tw.safe_ids = safe_ids; } function pop(tw) { + tw.defined_ids = Object.getPrototypeOf(tw.defined_ids); tw.safe_ids = Object.getPrototypeOf(tw.safe_ids); } + function access(tw, def) { + tw.defined_ids[def.id] = [ true ]; + } + + function assign(tw, def) { + var defined = tw.defined_ids[def.id]; + if (defined) defined[0] = false; + } + function mark(tw, def) { tw.safe_ids[def.id] = {}; } @@ -939,9 +950,13 @@ Compressor.prototype.compress = function(node) { return fixed_node; }, visit); walk_lambda(fn, tw); + var defined_ids = tw.defined_ids; var safe_ids = tw.safe_ids; pop_scope(tw, fn); - if (!aborts) tw.safe_ids = safe_ids; + if (!aborts) { + tw.defined_ids = defined_ids; + tw.safe_ids = safe_ids; + } return true; function visit(node, fixed) { @@ -966,10 +981,10 @@ Compressor.prototype.compress = function(node) { var scan = ld || left instanceof AST_Destructured; switch (node.operator) { case "=": + if (ld) assign(tw, ld); if (left.equals(right) && !left.has_side_effects(compressor)) { right.walk(tw); walk_prop(left); - node.redundant = true; return true; } if (ld && right instanceof AST_LambdaExpression) { @@ -990,6 +1005,7 @@ Compressor.prototype.compress = function(node) { case "||=": case "??=": var lazy = true; + if (ld) assign(tw, ld); default: if (!scan) { mark_assignment_to_arguments(left); @@ -1238,6 +1254,12 @@ Compressor.prototype.compress = function(node) { tw.in_loop = save_loop; return true; }); + def(AST_Dot, function(tw, descend) { + descend(); + var expr = this.expression; + if (expr instanceof AST_SymbolRef) access(tw, expr.definition()); + return true; + }); def(AST_For, function(tw, descend, compressor) { var node = this; reset_block_variables(tw, compressor, node); @@ -1336,10 +1358,13 @@ Compressor.prototype.compress = function(node) { return true; }); def(AST_Sub, function(tw) { - if (!this.optional) return; - this.expression.walk(tw); + var node = this; + if (!node.optional) return; + var expr = node.expression; + expr.walk(tw); + if (expr instanceof AST_SymbolRef) access(tw, expr.definition()); push(tw, true); - this.property.walk(tw); + node.property.walk(tw); pop(tw); return true; }); @@ -1390,6 +1415,7 @@ Compressor.prototype.compress = function(node) { var d = ref.definition(); var fixed = d.fixed || d.last_ref && d.last_ref.fixed; push_ref(d, ref); + if ((tw.defined_ids[d.id] || [])[0]) ref.defined = true; if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) { tw.loop_ids[d.id] = tw.in_loop; } @@ -1618,6 +1644,8 @@ Compressor.prototype.compress = function(node) { reset_flags(node); return node.reduce_vars(tw, descend, compressor); } : reset_flags); + // Side-effect tracking on sequential property access + tw.defined_ids = Object.create(null); // Flow control for visiting lambda definitions tw.fn_scanning = null; tw.fn_visited = []; @@ -4712,6 +4740,7 @@ Compressor.prototype.compress = function(node) { return this.tail_node()._dot_throw(compressor); }); def(AST_SymbolRef, function(compressor, force) { + if (this.defined) return false; if (this.is_undefined) return true; if (!is_strict(compressor, force)) return false; if (is_undeclared_ref(this) && this.is_declared(compressor)) return false; @@ -12945,20 +12974,18 @@ Compressor.prototype.compress = function(node) { if (compressor.option("dead_code")) { if (self.left instanceof AST_PropAccess) { if (self.operator == "=") { - if (self.redundant) { - var exprs = [ self.left.expression ]; - if (self.left instanceof AST_Sub) exprs.push(self.left.property); - exprs.push(self.right); - return make_sequence(self, exprs).optimize(compressor); - } - if (self.left.equals(self.right) && !self.left.has_side_effects(compressor)) { - return self.right; - } var exp = self.left.expression; + if (self.left.equals(self.right)) { + var defined = exp.defined; + exp.defined = false; + var drop_lhs = !self.left.has_side_effects(compressor); + exp.defined = defined; + if (drop_lhs) return self.right; + } if (exp instanceof AST_Lambda || !compressor.has_directive("use strict") - && exp instanceof AST_Constant - && !exp.may_throw_on_access(compressor)) { + && exp instanceof AST_Constant + && !exp.may_throw_on_access(compressor)) { return self.left instanceof AST_Dot ? self.right : make_sequence(self, [ self.left.property, self.right diff --git a/test/compress/arrows.js b/test/compress/arrows.js index 86ac8d494ca..b20316f8807 100644 --- a/test/compress/arrows.js +++ b/test/compress/arrows.js @@ -1312,7 +1312,7 @@ issue_5653: { } expect: { console.log((a => { - return console, +{}; + return +{}; })()); } expect_stdout: "NaN" diff --git a/test/compress/functions.js b/test/compress/functions.js index e0d504d7c23..0e4a2aaa103 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -8200,7 +8200,7 @@ issue_5316_1: { expect: { do { console.log("PASS"); - } while (b = a = void 0, b = (42, console[a = a] = a++), void 0); + } while (b = (42, console[a = b = a = void 0] = a++), void 0); var a, b; } expect_stdout: "PASS" diff --git a/test/compress/side_effects.js b/test/compress/side_effects.js index 090a6566207..cc317619e27 100644 --- a/test/compress/side_effects.js +++ b/test/compress/side_effects.js @@ -724,3 +724,65 @@ retain_instanceof: { } expect_stdout: "PASS" } + +drop_access: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + } + input: { + var o = {}; + o.p; + var f = function() { + o.q; + }; + f(); + console.log("PASS"); + } + expect: { + var o = {}; + o.p; + var f = function() {}; + f(); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +keep_access: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + } + input: { + var o = {}; + o.p; + o = null; + var f = function() { + o.q; + }; + try { + f(); + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + expect: { + var o = {}; + o.p; + o = null; + var f = function() { + o.q; + }; + try { + f(); + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +}