extend `hoist_props` (#3073)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 10 Apr 2018 18:48:15 +0000 (02:48 +0800)
committerGitHub <noreply@github.com>
Tue, 10 Apr 2018 18:48:15 +0000 (02:48 +0800)
- handle `AST_Assign` the same way as `AST_VarDef`
- inject `AST_Var` as succeeding statement

fixes #3071

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

index 7660e67..66dc10c 100644 (file)
@@ -3639,28 +3639,47 @@ merge(Compressor.prototype, {
         var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
         var defs_by_id = Object.create(null);
         return self.transform(new TreeTransformer(function(node, descend) {
-            if (node instanceof AST_VarDef) {
-                var sym = node.name, def, value;
-                if (sym.scope === self
-                    && (def = sym.definition()).escaped != 1
-                    && !def.assignments
-                    && !def.direct_access
-                    && !def.single_use
-                    && !top_retain(def)
-                    && (value = sym.fixed_value()) === node.value
-                    && value instanceof AST_Object) {
-                    descend(node, this);
-                    var defs = new Dictionary();
-                    var assignments = [];
-                    value.properties.forEach(function(prop) {
-                        assignments.push(make_node(AST_VarDef, node, {
-                            name: make_sym(prop.key),
-                            value: prop.value
-                        }));
+            if (node instanceof AST_Assign && node.operator == "=" && can_hoist(node.left, node.right, 1)) {
+                descend(node, this);
+                var defs = new Dictionary();
+                var assignments = [];
+                var decls = [];
+                node.right.properties.forEach(function(prop) {
+                    var decl = make_sym(node.left, prop.key);
+                    decls.push(make_node(AST_VarDef, node, {
+                        name: decl,
+                        value: null
+                    }));
+                    var sym = make_node(AST_SymbolRef, node, {
+                        name: decl.name,
+                        scope: self,
+                        thedef: decl.definition()
                     });
-                    defs_by_id[def.id] = defs;
-                    return MAP.splice(assignments);
-                }
+                    sym.reference({});
+                    assignments.push(make_node(AST_Assign, node, {
+                        operator: "=",
+                        left: sym,
+                        right: prop.value
+                    }));
+                });
+                defs_by_id[node.left.definition().id] = defs;
+                self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
+                    definitions: decls
+                }));
+                return make_sequence(node, assignments);
+            }
+            if (node instanceof AST_VarDef && can_hoist(node.name, node.value, 0)) {
+                descend(node, this);
+                var defs = new Dictionary();
+                var var_defs = [];
+                node.value.properties.forEach(function(prop) {
+                    var_defs.push(make_node(AST_VarDef, node, {
+                        name: make_sym(node.name, prop.key),
+                        value: prop.value
+                    }));
+                });
+                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];
@@ -3676,8 +3695,20 @@ merge(Compressor.prototype, {
                 }
             }
 
-            function make_sym(key) {
-                var new_var = make_node(sym.CTOR, 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 == 1) 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, {
                     name: self.make_var_name(sym.name + "_" + key),
                     scope: self
                 });
index b16f742..0e39916 100644 (file)
@@ -742,3 +742,85 @@ issue_3046: {
     }
     expect_stdout: "1"
 }
+
+issue_3071_1: {
+    options = {
+        evaluate: true,
+        inline: true,
+        join_vars: true,
+        hoist_props: true,
+        passes: 3,
+        reduce_vars: true,
+        sequences: true,
+        side_effects: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        (function() {
+            var obj = {};
+            obj.one = 1;
+            obj.two = 2;
+            console.log(obj.one);
+        })();
+    }
+    expect: {
+        console.log(1);
+    }
+    expect_stdout: "1"
+}
+
+issue_3071_2: {
+    options = {
+        evaluate: true,
+        inline: true,
+        join_vars: true,
+        hoist_props: true,
+        passes: 3,
+        reduce_vars: true,
+        sequences: true,
+        side_effects: true,
+        unused: true,
+    }
+    input: {
+        (function() {
+            obj = {};
+            obj.one = 1;
+            obj.two = 2;
+            console.log(obj.one);
+            var obj;
+        })();
+    }
+    expect: {
+        console.log(1);
+    }
+    expect_stdout: "1"
+}
+
+issue_3071_2_toplevel: {
+    options = {
+        evaluate: true,
+        inline: true,
+        join_vars: true,
+        hoist_props: true,
+        passes: 3,
+        reduce_vars: true,
+        sequences: true,
+        side_effects: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        (function() {
+            obj = {};
+            obj.one = 1;
+            obj.two = 2;
+            console.log(obj.one);
+            var obj;
+        })();
+    }
+    expect: {
+        console.log(1);
+    }
+    expect_stdout: "1"
+}