fix corner case in `hoist_props` (#4307)
authorAlex Lam S.L <alexlamsl@gmail.com>
Thu, 19 Nov 2020 16:02:25 +0000 (16:02 +0000)
committerGitHub <noreply@github.com>
Thu, 19 Nov 2020 16:02:25 +0000 (00:02 +0800)
lib/compress.js
test/compress/hoist_props.js

index 23eb5e4..9048880 100644 (file)
@@ -4611,12 +4611,7 @@ merge(Compressor.prototype, {
                 if (len < self.argnames.length && !compressor.drop_fargs(self, compressor.parent())) {
                     if (!compressor.drop_fargs(fn, call)) break;
                     do {
-                        var argname = make_node(AST_SymbolFunarg, fn, {
-                            name: fn.make_var_name("argument_" + len),
-                            scope: fn
-                        });
-                        fn.argnames.push(argname);
-                        fn.enclosed.push(fn.def_variable(argname));
+                        fn.argnames.push(fn.make_var(AST_SymbolFunarg, fn, "argument_" + len));
                     } while (++len < self.argnames.length);
                 }
                 return call.expression;
@@ -5984,13 +5979,31 @@ merge(Compressor.prototype, {
         return var_names;
     });
 
-    AST_Scope.DEFMETHOD("make_var_name", function(prefix) {
-        var var_names = this.var_names();
+    AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) {
+        var scopes = [ this ];
+        if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) {
+            var s = ref.scope;
+            if (member(s, scopes)) return;
+            do {
+                push_uniq(scopes, s);
+                s = s.parent_scope;
+            } while (s && s !== this);
+        });
         prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
         var name = prefix;
-        for (var i = 0; var_names[name]; i++) name = prefix + "$" + i;
-        var_names[name] = true;
-        return name;
+        for (var i = 0; !all(scopes, function(scope) {
+            return !scope.var_names()[name];
+        }); i++) name = prefix + "$" + i;
+        var sym = make_node(type, orig, {
+            name: name,
+            scope: this,
+        });
+        var def = this.def_variable(sym);
+        scopes.forEach(function(scope) {
+            scope.enclosed.push(def);
+            scope.var_names()[name] = true;
+        });
+        return sym;
     });
 
     AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
@@ -6049,13 +6062,8 @@ merge(Compressor.prototype, {
             }
 
             function make_sym(sym, key) {
-                var new_var = make_node(AST_SymbolVar, sym, {
-                    name: self.make_var_name(sym.name + "_" + key),
-                    scope: self
-                });
-                var def = self.def_variable(new_var);
-                defs.set(key, def);
-                self.enclosed.push(def);
+                var new_var = self.make_var(AST_SymbolVar, sym, sym.name + "_" + key);
+                defs.set(key, new_var.definition());
                 return new_var;
             }
         }));
@@ -9623,12 +9631,8 @@ merge(Compressor.prototype, {
                 }
             } else if (!argname && index < fn.argnames.length + 5 && compressor.drop_fargs(fn, fn_parent)) {
                 while (index >= fn.argnames.length) {
-                    argname = make_node(AST_SymbolFunarg, fn, {
-                        name: fn.make_var_name("argument_" + fn.argnames.length),
-                        scope: fn
-                    });
+                    argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
                     fn.argnames.push(argname);
-                    fn.enclosed.push(fn.def_variable(argname));
                 }
             }
             if (argname && find_if(function(node) {
index 09137cc..dbee9b4 100644 (file)
@@ -297,6 +297,33 @@ name_collision_3: {
     expect_stdout: "true 4 6"
 }
 
+name_collision_4: {
+    options = {
+        hoist_props: true,
+        reduce_vars: true,
+    }
+    input: {
+        console.log(function() {
+            var o = {
+                p: 0,
+                q: "PASS",
+            };
+            return function(o_p) {
+                if (!o.p) return o_p;
+            }(o.q);
+        }());
+    }
+    expect: {
+        console.log(function() {
+            var o_p$0 = 0, o_q = "PASS";
+            return function(o_p) {
+                if (!o_p$0) return o_p;
+            }(o_q);
+        }());
+    }
+    expect_stdout: "PASS"
+}
+
 contains_this_1: {
     options = {
         evaluate: true,