From 8b0c8365150c1471eef98ed37e33bb66a51c3a45 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 23 May 2021 23:54:48 +0100 Subject: [PATCH] fix corner cases in `rename` & `varify` (#4955) fixes #4954 --- lib/compress.js | 2 ++ lib/scope.js | 29 +++++++++++++++----- test/compress/const.js | 53 ++++++++++++++++++++++++++++++++++++ test/compress/drop-unused.js | 8 ++---- test/compress/varify.js | 38 ++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 12 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 4f50f52a..5f1d63ff 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -8604,6 +8604,8 @@ merge(Compressor.prototype, { if (def.scope === scope) return; def.scope = scope; scope.variables.set(def.name, def); + scope.enclosed.push(def); + scope.var_names()[def.name] = true; }), value: defn.value, }); diff --git a/lib/scope.js b/lib/scope.js index bb36583c..3ff0c631 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -70,7 +70,9 @@ SymbolDef.prototype = { } else if (!this.mangled_name && !this.unmangleable(options)) { var def = this.redefined(); if (def) { - this.mangled_name = def.mangled_name || def.name; + var name = def.mangled_name || def.name; + this.mangled_name = name; + names_in_use(this.scope, options)[name] = true; } else { this.mangled_name = next_mangled_name(this, options); } @@ -80,15 +82,16 @@ SymbolDef.prototype = { } }, redefined: function() { - var scope = this.defun; + var self = this; + var scope = self.defun; if (!scope) return; - var name = this.name; + var name = self.name; var def = scope.variables.get(name) || scope instanceof AST_Toplevel && scope.globals.get(name) - || this.orig[0] instanceof AST_SymbolConst && find_if(function(def) { - return def.name == name; + || self.orig[0] instanceof AST_SymbolConst && find_if(function(def) { + return def.name == name && def !== self; }, scope.enclosed); - if (def && def !== this) return def.redefined() || def; + if (def && def !== self) return def.redefined() || def; }, unmangleable: function(options) { return this.global && !options.toplevel @@ -122,6 +125,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { // pass 1: setup scope chaining and handle definitions var self = this; + var const_names = null; var defun = null; var exported = false; var next_def_id = 0; @@ -200,6 +204,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { } else if (node instanceof AST_SymbolConst) { var def = scope.def_variable(node); def.defun = defun; + const_names.add(def.name, def); if (exported) def.exported = true; } else if (node instanceof AST_SymbolDefun) { var def = defun.def_function(node, tw.parent()); @@ -222,13 +227,21 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { function walk_scope(descend) { node.init_vars(scope); + var save_names = const_names; var save_defun = defun; var save_scope = scope; - if (node instanceof AST_Scope) defun = node; + if (node instanceof AST_Scope) { + const_names = new Dictionary(); + defun = node; + } scope = node; descend(); + if (node instanceof AST_Scope) const_names.each(function(defs, name) { + if (defs.length > 1 && !node.variables.has(name)) push_uniq(node.enclosed, defs[0]); + }); scope = save_scope; defun = save_defun; + const_names = save_names; } function entangle(defun, scope) { @@ -668,6 +681,8 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) { if (def.scope.parent_scope.find_variable(sym.name)) return false; redef = scope.def_variable(sym); scope.to_mangle.push(redef); + } else if (redef.mangled_name) { + return false; } redefined.push(def); def.references.forEach(reference); diff --git a/test/compress/const.js b/test/compress/const.js index fce6ebea..d7731005 100644 --- a/test/compress/const.js +++ b/test/compress/const.js @@ -1535,3 +1535,56 @@ issue_4848: { } expect_stdout: "PASS" } + +issue_4954_1: { + rename = true + input: { + (function() { + { + const a = "foo"; + console.log(a); + } + { + const a = "bar"; + console.log(a); + } + })(); + } + expect: { + (function() { + { + const a = "foo"; + console.log(a); + } + { + const a = "bar"; + console.log(a); + } + })(); + } + expect_stdout: true +} + +issue_4954_2: { + mangle = {} + input: { + "use strict"; + const a = null; + (function(b) { + for (const a in null); + for (const a in b) + console.log("PASS"); + })([ null ]); + } + expect: { + "use strict"; + const a = null; + (function(o) { + for (const n in null); + for (const n in o) + console.log("PASS"); + })([ null ]); + } + expect_stdout: "PASS" + node_version: ">=4" +} diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 5ca29b04..015dec92 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -3080,11 +3080,9 @@ issue_4235: { })(); } expect: { - (function() { - f = console.log(f), - void 0; - var f; - })(); + void function() { + var f = console.log(f); + }(); } expect_stdout: "undefined" } diff --git a/test/compress/varify.js b/test/compress/varify.js index 75d50e6d..d8b2bb49 100644 --- a/test/compress/varify.js +++ b/test/compress/varify.js @@ -575,3 +575,41 @@ issue_4933_2: { } expect_stdout: "undefined" } + +issue_4954: { + options = { + functions: true, + reduce_vars: true, + unused: true, + varify: true, + } + input: { + "use strict"; + (function() { + { + let a = console; + console.log(typeof a); + } + { + let a = function() {}; + a && console.log(typeof a); + } + })(); + } + expect: { + "use strict"; + (function() { + var a = console; + console.log(typeof a); + { + let a = function() {}; + a && console.log(typeof a); + } + })(); + } + expect_stdout: [ + "object", + "function", + ] + node_version: ">=4" +}