fix corner case in `hoist_props` (#3452)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sun, 6 Oct 2019 02:29:13 +0000 (10:29 +0800)
committerGitHub <noreply@github.com>
Sun, 6 Oct 2019 02:29:13 +0000 (10:29 +0800)
fixes #3440

lib/compress.js
test/compress/hoist_props.js

index ade1913..5ea5d00 100644 (file)
@@ -4062,10 +4062,11 @@ merge(Compressor.prototype, {
         var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
         var defs_by_id = Object.create(null);
         self.transform(new TreeTransformer(function(node, descend) {
-            if (node instanceof AST_Assign
-                && node.operator == "="
-                && node.write_only
-                && can_hoist(node.left, node.right, 1)) {
+            if (node instanceof AST_Assign) {
+                if (node.operator != "=") return;
+                if (!node.write_only) return;
+                if (node.left.scope !== self) return;
+                if (!can_hoist(node.left, node.right, 1)) return;
                 descend(node, this);
                 var defs = new Dictionary();
                 var assignments = [];
@@ -4094,17 +4095,9 @@ merge(Compressor.prototype, {
                 }));
                 return make_sequence(node, assignments);
             }
-            if (node instanceof AST_Unary
-                && !unary_side_effects[node.operator]
-                && node.expression instanceof AST_SymbolRef
-                && node.expression.definition().id in defs_by_id) {
-                node = node.clone();
-                node.expression = make_node(AST_Object, node, {
-                    properties: []
-                });
-                return node;
-            }
-            if (node instanceof AST_VarDef && can_hoist(node.name, node.value, 0)) {
+            if (node instanceof AST_Scope) return node === self ? undefined : node;
+            if (node instanceof AST_VarDef) {
+                if (!can_hoist(node.name, node.value, 0)) return;
                 descend(node, this);
                 var defs = new Dictionary();
                 var var_defs = [];
@@ -4117,32 +4110,6 @@ merge(Compressor.prototype, {
                 defs_by_id[node.name.definition().id] = defs;
                 return MAP.splice(var_defs);
             }
-            if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
-                var defs = defs_by_id[node.expression.definition().id];
-                if (defs) {
-                    var def = defs.get(node.getProperty());
-                    var sym = make_node(AST_SymbolRef, node, {
-                        name: def.name,
-                        scope: node.expression.scope,
-                        thedef: def
-                    });
-                    sym.reference({});
-                    return sym;
-                }
-            }
-
-            function can_hoist(sym, right, count) {
-                if (sym.scope !== self) return;
-                var def = sym.definition();
-                if (def.assignments != count) return;
-                if (def.direct_access) return;
-                if (def.escaped.depth == 1) return;
-                if (def.references.length == count) return;
-                if (def.single_use) return;
-                if (top_retain(def)) return;
-                if (sym.fixed_value() !== right) return;
-                return right instanceof AST_Object;
-            }
 
             function make_sym(sym, key) {
                 var new_var = make_node(AST_SymbolVar, sym, {
@@ -4155,6 +4122,43 @@ merge(Compressor.prototype, {
                 return new_var;
             }
         }));
+        self.transform(new TreeTransformer(function(node, descend) {
+            if (node instanceof AST_PropAccess) {
+                if (!(node.expression instanceof AST_SymbolRef)) return;
+                var defs = defs_by_id[node.expression.definition().id];
+                if (!defs) return;
+                var def = defs.get(node.getProperty());
+                var sym = make_node(AST_SymbolRef, node, {
+                    name: def.name,
+                    scope: node.expression.scope,
+                    thedef: def
+                });
+                sym.reference({});
+                return sym;
+            }
+            if (node instanceof AST_Unary) {
+                if (unary_side_effects[node.operator]) return;
+                if (!(node.expression instanceof AST_SymbolRef)) return;
+                if (!(node.expression.definition().id in defs_by_id)) return;
+                var opt = node.clone();
+                opt.expression = make_node(AST_Object, node, {
+                    properties: []
+                });
+                return opt;
+            }
+        }));
+
+        function can_hoist(sym, right, count) {
+            var def = sym.definition();
+            if (def.assignments != count) return;
+            if (def.direct_access) return;
+            if (def.escaped.depth == 1) return;
+            if (def.references.length == count) return;
+            if (def.single_use) return;
+            if (top_retain(def)) return;
+            if (sym.fixed_value() !== right) return;
+            return right instanceof AST_Object;
+        }
     });
 
     // drop_side_effect_free()
index b055ae7..0d8e771 100644 (file)
@@ -886,3 +886,31 @@ issue_3411: {
     }
     expect_stdout: "PASS"
 }
+
+issue_3440: {
+    options = {
+        hoist_props: true,
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        (function() {
+            function f() {
+                console.log(o.p);
+            }
+            var o = {
+                p: "PASS",
+            };
+            return f;
+        })()();
+    }
+    expect: {
+        (function() {
+            var o_p = "PASS";
+            return function() {
+                console.log(o_p);
+            };
+        })()();
+    }
+    expect_stdout: "PASS"
+}