enhance `varify` (#4279)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 17 Nov 2020 04:35:00 +0000 (04:35 +0000)
committerGitHub <noreply@github.com>
Tue, 17 Nov 2020 04:35:00 +0000 (12:35 +0800)
lib/compress.js
test/compress/varify.js
test/ufuzz/index.js

index d9446e8..7297938 100644 (file)
@@ -6551,6 +6551,19 @@ merge(Compressor.prototype, {
         return if_break_in_loop(self, compressor);
     });
 
+    OPT(AST_ForIn, function(self, compressor) {
+        if (compressor.option("varify") && (self.init instanceof AST_Const || self.init instanceof AST_Let)) {
+            var name = self.init.definitions[0].name;
+            if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
+                && !name.match_symbol(function(node) {
+                    if (node instanceof AST_SymbolDeclaration) return may_overlap(compressor, node.definition());
+                })) {
+                self.init = to_var(self.init);
+            }
+        }
+        return self;
+    });
+
     function mark_locally_defined(condition, consequent, alternative) {
         if (!(condition instanceof AST_Binary)) return;
         if (!(condition.left instanceof AST_String)) {
@@ -7051,21 +7064,18 @@ merge(Compressor.prototype, {
         return make_sequence(this, assignments);
     });
 
-    function varify(self, compressor) {
-        return compressor.option("varify") && all(self.definitions, function(defn) {
-            return !defn.name.match_symbol(function(node) {
-                if (!(node instanceof AST_SymbolDeclaration)) return;
-                if (!node.fixed_value()) return true;
-                var def = node.definition();
-                if (compressor.exposed(def)) return true;
-                var scope = def.scope.resolve();
-                for (var s = def.scope; s !== scope;) {
-                    s = s.parent_scope;
-                    if (s.var_names()[node.name]) return true;
-                }
-            });
-        }) ? make_node(AST_Var, self, {
-            definitions: self.definitions.map(function(defn) {
+    function may_overlap(compressor, def) {
+        if (compressor.exposed(def)) return true;
+        var scope = def.scope.resolve();
+        for (var s = def.scope; s !== scope;) {
+            s = s.parent_scope;
+            if (s.var_names()[def.name]) return true;
+        }
+    }
+
+    function to_var(stat) {
+        return make_node(AST_Var, stat, {
+            definitions: stat.definitions.map(function(defn) {
                 return make_node(AST_VarDef, defn, {
                     name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) {
                         var def = name.definition();
@@ -7076,7 +7086,17 @@ merge(Compressor.prototype, {
                     value: defn.value
                 });
             })
-        }) : self;
+        });
+    }
+
+    function varify(self, compressor) {
+        return compressor.option("varify") && all(self.definitions, function(defn) {
+            return !defn.name.match_symbol(function(node) {
+                if (node instanceof AST_SymbolDeclaration) {
+                    return !node.fixed_value() || may_overlap(compressor, node.definition());
+                }
+            });
+        }) ? to_var(self) : self;
     }
 
     OPT(AST_Const, varify);
index 1286fc6..d4894d2 100644 (file)
@@ -234,3 +234,122 @@ issue_4191_let: {
     expect_stdout: "function undefined"
     node_version: ">=4"
 }
+
+forin_const_1: {
+    options = {
+        join_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+        varify: true,
+    }
+    input: {
+        const o = {
+            foo: 42,
+            bar: "PASS",
+        };
+        for (const k in o)
+            console.log(k, o[k]);
+    }
+    expect: {
+        var o = {
+            foo: 42,
+            bar: "PASS",
+        };
+        for (const k in o)
+            console.log(k, o[k]);
+    }
+    expect_stdout: true
+    node_version: ">=4"
+}
+
+forin_const_2: {
+    options = {
+        join_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+        varify: true,
+    }
+    input: {
+        const o = {
+            p: 42,
+            q: "PASS",
+        };
+        for (const [ k ] in o)
+            console.log(k, o[k]);
+    }
+    expect: {
+        var o = {
+            p: 42,
+            q: "PASS",
+        }, k;
+        for ([ k ] in o)
+            console.log(k, o[k]);
+    }
+    expect_stdout: [
+        "p 42",
+        "q PASS",
+    ]
+    node_version: ">=6"
+}
+
+forin_let_1: {
+    options = {
+        join_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+        varify: true,
+    }
+    input: {
+        "use strict";
+        let o = {
+            foo: 42,
+            bar: "PASS",
+        };
+        for (let k in o)
+            console.log(k, o[k]);
+    }
+    expect: {
+        "use strict";
+        var o = {
+            foo: 42,
+            bar: "PASS",
+        }, k;
+        for (k in o)
+            console.log(k, o[k]);
+    }
+    expect_stdout: [
+        "foo 42",
+        "bar PASS",
+    ]
+    node_version: ">=4"
+}
+
+forin_let_2: {
+    options = {
+        join_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+        varify: true,
+    }
+    input: {
+        let o = {
+            p: 42,
+            q: "PASS",
+        };
+        for (let [ k ] in o)
+            console.log(k, o[k]);
+    }
+    expect: {
+        var o = {
+            p: 42,
+            q: "PASS",
+        }, k;
+        for ([ k ] in o)
+            console.log(k, o[k]);
+    }
+    expect_stdout: [
+        "p 42",
+        "q PASS",
+    ]
+    node_version: ">=6"
+}
index 1c4af3c..7004196 100644 (file)
@@ -708,7 +708,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
             "{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ",
             label.target + " for (",
             !/^key/.test(key) ? rng(10) ? "" : "var " : rng(10) ? "var " : rng(2) ? "let " : "const ",
-            rng(20) ? key : "{ length: " + key + " }",
+            rng(10) ? key : rng(5) ? "[ " + key + " ]" : "{ length: " + key + " }",
             " in expr" + loop + ") {",
             rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "",
             createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),