From 5fba98608ce2ce359cbf788196e64e4bbb48e303 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 8 Dec 2020 00:52:14 +0000 Subject: [PATCH] fix corner case in `reduce_vars` (#4348) fixes #4347 --- lib/compress.js | 69 ++++++++++++++++++++---------------------- test/compress/async.js | 59 ++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 36 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 1b5b8be6..8936125d 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -340,8 +340,7 @@ merge(Compressor.prototype, { return !immutable && parent.expression === node && !parent.is_expr_pure(compressor) - && (!(value instanceof AST_Function) - || !(parent instanceof AST_New) && value.contains_this()); + && (!is_function(value) || !(parent instanceof AST_New) && value.contains_this()); } if (parent instanceof AST_ForIn) return parent.init === node; if (parent instanceof AST_ObjectKeyVal) { @@ -390,13 +389,13 @@ merge(Compressor.prototype, { def.fixed = !def.const_redefs && !def.scope.pinned() && !compressor.exposed(def) - && !(def.init instanceof AST_Function && def.init !== def.scope) + && !(is_function(def.init) && def.init !== def.scope) && def.init; - if (def.fixed instanceof AST_Defun && !all(def.references, function(ref) { + if (is_defun(def.fixed) && !all(def.references, function(ref) { var scope = ref.scope.resolve(); do { if (def.scope === scope) return true; - } while (scope instanceof AST_Function && (scope = scope.parent_scope.resolve())); + } while (is_function(scope) && (scope = scope.parent_scope.resolve())); })) { tw.defun_ids[def.id] = false; } @@ -421,7 +420,7 @@ merge(Compressor.prototype, { scope.may_call_this = noop; if (!scope.contains_this()) return; scope.functions.each(function(def) { - if (def.init instanceof AST_Defun && !(def.id in tw.defun_ids)) { + if (is_defun(def.init) && !(def.id in tw.defun_ids)) { tw.defun_ids[def.id] = false; } }); @@ -458,7 +457,7 @@ merge(Compressor.prototype, { function walk_defuns(tw, scope) { scope.functions.each(function(def) { - if (def.init instanceof AST_Defun && !tw.defun_visited[def.id]) { + if (is_defun(def.init) && !tw.defun_visited[def.id]) { tw.defun_ids[def.id] = tw.safe_ids; def.init.walk(tw); } @@ -496,7 +495,7 @@ merge(Compressor.prototype, { } return !safe.assign || safe.assign === tw.safe_ids; } - return def.fixed instanceof AST_Defun; + return is_defun(def.fixed); } function safe_to_assign(tw, def, declare) { @@ -651,6 +650,20 @@ merge(Compressor.prototype, { lhs.walk(scanner); } + function reduce_defun(tw, descend, compressor) { + var id = this.name.definition().id; + if (tw.defun_visited[id]) return true; + if (tw.defun_ids[id] !== tw.safe_ids) return true; + tw.defun_visited[id] = true; + this.inlined = false; + push(tw); + reset_variables(tw, compressor, this); + descend(); + pop(tw); + walk_defuns(tw, this); + return true; + } + def(AST_Assign, function(tw, descend, compressor) { var node = this; var left = node.left; @@ -732,6 +745,7 @@ merge(Compressor.prototype, { } } }); + def(AST_AsyncDefun, reduce_defun); def(AST_Binary, function(tw) { if (!lazy_op[this.operator]) return; this.left.walk(tw); @@ -748,31 +762,27 @@ merge(Compressor.prototype, { def(AST_Call, function(tw, descend) { tw.find_parent(AST_Scope).may_call_this(); var exp = this.expression; - var tail = exp.tail_node(); - if (tail instanceof AST_Function) { - if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) { - node.walk(tw); - }); + if (is_function(exp)) { this.args.forEach(function(arg) { arg.walk(tw); }); - tail.walk(tw); + exp.walk(tw); return true; - } else if (tail instanceof AST_SymbolRef) { - var def = tail.definition(); + } else if (exp instanceof AST_SymbolRef) { + var def = exp.definition(); if (this.TYPE == "Call" && tw.in_boolean_context()) def.bool_fn++; - if (!(def.fixed instanceof AST_Defun)) return; + if (!is_defun(def.fixed)) return; var defun = mark_defun(tw, def); if (!defun) return; descend(); defun.walk(tw); return true; } else if (this.TYPE == "Call" - && tail instanceof AST_Assign - && tail.operator == "=" - && tail.left instanceof AST_SymbolRef + && exp instanceof AST_Assign + && exp.operator == "=" + && exp.left instanceof AST_SymbolRef && tw.in_boolean_context()) { - tail.left.definition().bool_fn++; + exp.left.definition().bool_fn++; } }); def(AST_Conditional, function(tw) { @@ -785,19 +795,7 @@ merge(Compressor.prototype, { pop(tw); return true; }); - def(AST_Defun, function(tw, descend, compressor) { - var id = this.name.definition().id; - if (tw.defun_visited[id]) return true; - if (tw.defun_ids[id] !== tw.safe_ids) return true; - tw.defun_visited[id] = true; - this.inlined = false; - push(tw); - reset_variables(tw, compressor, this); - descend(); - pop(tw); - walk_defuns(tw, this); - return true; - }); + def(AST_Defun, reduce_defun); def(AST_Do, function(tw) { var saved_loop = tw.in_loop; tw.in_loop = this; @@ -1007,8 +1005,7 @@ merge(Compressor.prototype, { } if (!this.fixed) this.fixed = d.fixed; var parent; - if (d.fixed instanceof AST_Defun - && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) { + if (is_defun(d.fixed) && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) { var defun = mark_defun(tw, d); if (defun) defun.walk(tw); } diff --git a/test/compress/async.js b/test/compress/async.js index ca604034..4f342895 100644 --- a/test/compress/async.js +++ b/test/compress/async.js @@ -328,3 +328,62 @@ property_access_expression: { expect_stdout: "function" node_version: ">=8" } + +issue_4347_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "foo"; + f(); + a = "bar"; + f(); + async function f() { + console.log(a); + } + } + expect: { + var a = "foo"; + f(); + a = "bar"; + f(); + async function f() { + console.log(a); + } + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=8" +} + +issue_4347_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + (async function() { + throw 42; + a = "FAIL"; + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (async function() { + throw 42; + a = "FAIL"; + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} -- 2.34.1