diff --git a/lib/compress.js b/lib/compress.js index a442f94e..d8823d61 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -389,7 +389,18 @@ merge(Compressor.prototype, { } var lhs = is_lhs(node, parent); if (lhs) return lhs; + if (level == 0 && value && value.is_constant()) return; if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1); + if (parent instanceof AST_Assign) switch (parent.operator) { + case "=": + return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive); + case "&&=": + case "||=": + case "??=": + return is_modified(compressor, tw, parent, parent, level + 1); + default: + return; + } if (parent instanceof AST_Binary) { if (!lazy_op[parent.operator]) return; return is_modified(compressor, tw, parent, parent, level + 1); @@ -625,7 +636,7 @@ merge(Compressor.prototype, { if (def.fixed === undefined) return declare || all(def.orig, function(sym) { return !(sym instanceof AST_SymbolLet); }); - if (def.fixed === false) return false; + if (def.fixed === false || def.fixed === 0) return false; var safe = tw.safe_ids[def.id]; if (def.safe_ids) { def.safe_ids[def.id] = false; @@ -973,7 +984,13 @@ merge(Compressor.prototype, { } var d = sym.definition(); d.assignments++; - if (fixed && !modified && !sym.in_arg && safe_to_assign(tw, d)) { + if (!fixed || sym.in_arg || !safe_to_assign(tw, d)) { + walk(); + d.fixed = false; + } else if (modified) { + walk(); + d.fixed = 0; + } else { push_ref(d, sym); mark(tw, d); if (left instanceof AST_Destructured @@ -984,9 +1001,6 @@ merge(Compressor.prototype, { mark_escaped(tw, d, sym.scope, node, right, 0, 1); sym.fixed = d.fixed = fixed; sym.fixed.assigns = [ node ]; - } else { - walk(); - d.fixed = false; } }); } @@ -1265,7 +1279,7 @@ merge(Compressor.prototype, { if (!safe) return; safe.assign = true; }); - if (d.fixed === false) { + if (d.fixed === false || d.fixed === 0) { var redef = d.redefined(); if (redef && cross_scope(d.scope, this.scope)) redef.single_use = false; } else if (d.fixed === undefined || !safe_to_read(tw, d)) { @@ -1290,7 +1304,7 @@ merge(Compressor.prototype, { if (d.single_use) { d.single_use = "m"; } else { - d.fixed = false; + d.fixed = 0; } } if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true; @@ -1475,9 +1489,14 @@ merge(Compressor.prototype, { AST_Symbol.DEFMETHOD("fixed_value", function() { var fixed = this.definition().fixed; + if (fixed) { + if (this.fixed) fixed = this.fixed; + return fixed instanceof AST_Node ? fixed : fixed(); + } + fixed = fixed === 0 && this.fixed; if (!fixed) return fixed; - if (this.fixed) fixed = this.fixed; - return fixed instanceof AST_Node ? fixed : fixed(); + var value = fixed instanceof AST_Node ? fixed : fixed(); + return value.is_constant() && value; }); AST_SymbolRef.DEFMETHOD("is_immutable", function() { @@ -3593,11 +3612,11 @@ merge(Compressor.prototype, { if (!(node.left instanceof AST_PropAccess)) return; var sym = node.left.expression; if (!(sym instanceof AST_SymbolRef)) return; - if (!names[sym.name]) return; + if (!(sym.name in names)) return; if (!node.right.is_constant_expression(scope)) return; var prop = node.left.property; if (prop instanceof AST_Node) { - if (try_join(prop)) prop = node.left.property = prop.right; + if (try_join(prop)) prop = node.left.property = prop.right.clone(); prop = prop.evaluate(compressor); } if (prop instanceof AST_Node) return; @@ -3615,7 +3634,7 @@ merge(Compressor.prototype, { if (!all(value.properties, diff)) return; value.properties.push(make_node(AST_ObjectKeyVal, node, { key: prop, - value: node.right + value: node.right, })); return true; } diff --git a/test/compress/join_vars.js b/test/compress/join_vars.js index f7dd4ae3..94612c4b 100644 --- a/test/compress/join_vars.js +++ b/test/compress/join_vars.js @@ -507,7 +507,7 @@ chained_assignments: { expect_stdout: "PASS" } -folded_assignments: { +folded_assignments_1: { options = { evaluate: true, join_vars: true, @@ -527,6 +527,30 @@ folded_assignments: { expect_stdout: "PASS 42" } +folded_assignments_2: { + options = { + evaluate: true, + join_vars: true, + } + input: { + "use strict"; + var a = {}; + a[42] = "FAIL"; + a[a.PASS = 42] = "PASS"; + console.log(a[42], a.PASS); + } + expect: { + "use strict"; + var a = { + 42: "FAIL", + PASS: 42, + }; + a[42] = "PASS"; + console.log(a[42], a.PASS); + } + expect_stdout: "PASS 42" +} + inlined_assignments: { options = { join_vars: true, @@ -550,6 +574,7 @@ typescript_enum: { rename = true options = { assignments: true, + collapse_vars: true, evaluate: true, hoist_props: true, inline: true, diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index ebb6229a..43e1ff1a 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -7320,6 +7320,46 @@ local_assignment_loop: { expect_stdout: "PASS" } +local_assignment_modified: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a; + (a = a || {}).p = 42; + console.log(a.p); + } + expect: { + var a; + (a = {}).p = 42; + console.log(a.p); + } + expect_stdout: "42" +} + +local_definition_modified: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = a || {}; + a.p = 42; + console.log(a.p); + } + expect: { + var a = {}; + a.p = 42; + console.log(a.p); + } + expect_stdout: "42" +} + issue_3957_1: { options = { evaluate: true, @@ -7435,6 +7475,7 @@ issue_4030: { collapse_vars: true, evaluate: true, reduce_vars: true, + side_effects: true, toplevel: true, unused: true, }