From 21bd4c4a9dfd230ebbf37d646a59e1ee826ddd96 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 6 Apr 2022 21:12:03 +0100 Subject: [PATCH] fix corner cases in `collapse_vars` & `hoist_vars` (#5412) fixes #5411 --- lib/compress.js | 37 ++++++++++++----- test/compress/hoist_vars.js | 83 ++++++++++++++++++++++++++++++++++--- 2 files changed, 105 insertions(+), 15 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 88231a47..a345f100 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2059,13 +2059,17 @@ Compressor.prototype.compress = function(node) { if (is_lhs(node, parent)) { if (value_def && !hit_rhs) assign_used = true; return node; - } else if (value_def) { + } + if (!hit_rhs && verify_ref && node.fixed !== lhs.fixed) { + abort = true; + return node; + } + if (value_def) { if (stop_if_hit && assign_pos == 0) assign_pos = remaining - replaced; if (!hit_rhs) replaced++; return node; - } else { - replaced++; } + replaced++; changed = abort = true; AST_Node.info("Collapsing {node} [{file}:{line},{col}]", { node: node, @@ -2240,6 +2244,7 @@ Compressor.prototype.compress = function(node) { var candidate = hit_stack[hit_stack.length - 1]; var assign_pos = -1; var assign_used = false; + var verify_ref = false; var remaining; var value_def = null; var stop_after = null; @@ -2958,6 +2963,7 @@ Compressor.prototype.compress = function(node) { if (matches < remaining) { remaining = matches; assign_pos = 0; + verify_ref = true; } } if (expr.operator == "=") mangleable_var(expr.right); @@ -3266,9 +3272,13 @@ Compressor.prototype.compress = function(node) { } var def = lhs.definition(); if (def.references.length - def.replaced == referenced) return true; - return def.fixed && lhs.fixed && def.references.filter(function(ref) { + if (!def.fixed) return false; + if (!lhs.fixed) return false; + if (def.references.filter(function(ref) { return ref.fixed === lhs.fixed; - }).length == referenced; + }).length != referenced) return false; + verify_ref = true; + return true; } function symbol_in_lvalues(sym, parent) { @@ -7843,7 +7853,7 @@ Compressor.prototype.compress = function(node) { var consts = new Dictionary(); var dirs = []; var hoisted = []; - var vars = new Dictionary(), vars_found = 0; + var vars = new Dictionary(); var tt = new TreeTransformer(function(node, descend, in_list) { if (node === self) return; if (node instanceof AST_Directive) { @@ -7871,7 +7881,6 @@ Compressor.prototype.compress = function(node) { })) return node; node.definitions.forEach(function(defn) { vars.set(defn.name.name, defn); - ++vars_found; }); var seq = node.to_assignments(); if (p instanceof AST_ForEnumeration && p.init === node) { @@ -7890,7 +7899,7 @@ Compressor.prototype.compress = function(node) { } }); self.transform(tt); - if (vars_found > 0) { + if (vars.size() > 0) { // collect only vars which don't show up in self's arguments list var defns = []; if (self instanceof AST_Lambda) self.each_argname(function(argname) { @@ -9658,8 +9667,16 @@ Compressor.prototype.compress = function(node) { fixed.escaped = def.escaped; name.fixed = fixed; def.references.forEach(function(ref) { - var assigns = ref.fixed && ref.fixed.assigns; - if (assigns && assigns[0] === defn) assigns[0] = assign; + if (!ref.fixed) return; + var assigns = ref.fixed.assigns; + if (!assigns) return; + if (assigns[0] !== defn) return; + if (assigns.length > 1 || ref.fixed.to_binary || ref.fixed.to_prefix) { + assigns[0] = assign; + } else { + ref.fixed = fixed; + if (def.fixed === ref.fixed) def.fixed = fixed; + } }); def.references.push(name); } diff --git a/test/compress/hoist_vars.js b/test/compress/hoist_vars.js index 30fb4837..99a13fe6 100644 --- a/test/compress/hoist_vars.js +++ b/test/compress/hoist_vars.js @@ -224,8 +224,7 @@ issue_4489: { console.log(k); } expect: { - !(A = 0); - for (var k in true); + for (var k in !(A = 0)); console.log(k); } expect_stdout: "undefined" @@ -407,9 +406,9 @@ issue_4893_2: { expect: { try{ (function() { - var b; - b = null; - b.p += 42; + var a; + a = null; + a.p += 42; })(); } catch (e) { console.log("PASS"); @@ -530,3 +529,77 @@ issue_5378: { "undefined", ] } + +issue_5411_1: { + options = { + collapse_vars: true, + dead_code: true, + hoist_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = "PASS"; + b++; + b = a; + var b = b, c = c && c[b]; + console.log(b); + } + expect: { + var b, c, a = "PASS"; + b++; + b = a; + c = c && c[b]; + console.log(b); + } + expect_stdout: "PASS" +} + +issue_5411_2: { + options = { + collapse_vars: true, + dead_code: true, + evaluate: true, + hoist_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + b++; + b = a; + var b = b, c = c && c[b]; + console.log(b); + } + expect: { + var b, c; + b++; + b = "PASS", + c = c && c[b]; + console.log(b); + } + expect_stdout: "PASS" +} + +issue_5411_3: { + options = { + collapse_vars: true, + hoist_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = console; + a++; + var a = A = a; + console.log(A); + } + expect: { + var a = console; + a = A = ++a; + console.log(A); + } + expect_stdout: "NaN" +}