fix corner case in `global_defs` (#3218)
authorAlex Lam S.L <alexlamsl@gmail.com>
Thu, 19 Jul 2018 10:14:36 +0000 (18:14 +0800)
committerGitHub <noreply@github.com>
Thu, 19 Jul 2018 10:14:36 +0000 (18:14 +0800)
fixes #3217

lib/compress.js
lib/output.js
test/compress/global_defs.js

index 364bd36..ca868a9 100644 (file)
@@ -59,7 +59,7 @@ function Compressor(options, false_by_default) {
         drop_debugger : !false_by_default,
         evaluate      : !false_by_default,
         expression    : false,
-        global_defs   : {},
+        global_defs   : false,
         hoist_funs    : false,
         hoist_props   : !false_by_default,
         hoist_vars    : false,
@@ -149,6 +149,7 @@ merge(Compressor.prototype, {
         return false;
     },
     compress: function(node) {
+        node = node.resolve_defines(this);
         if (this.option("expression")) {
             node.process_expression(true);
         }
@@ -2410,22 +2411,6 @@ merge(Compressor.prototype, {
     }
 
     (function(def) {
-        AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
-            if (!compressor.option("global_defs")) return;
-            var def = this._find_defs(compressor, "");
-            if (def) {
-                var node, parent = this, level = 0;
-                do {
-                    node = parent;
-                    parent = compressor.parent(level++);
-                } while (parent instanceof AST_PropAccess && parent.expression === node);
-                if (is_lhs(node, parent)) {
-                    compressor.warn('global_defs ' + this.print_to_string() + ' redefined [{file}:{line},{col}]', this.start);
-                } else {
-                    return def;
-                }
-            }
-        });
         function to_node(value, orig) {
             if (value instanceof AST_Node) return make_node(value.CTOR, orig, value);
             if (Array.isArray(value)) return make_node(AST_Array, orig, {
@@ -2447,25 +2432,43 @@ merge(Compressor.prototype, {
             }
             return make_node_from_constant(value, orig);
         }
+
+        function warn(compressor, node) {
+            compressor.warn("global_defs " + node.print_to_string() + " redefined [{file}:{line},{col}]", node.start);
+        }
+
+        AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
+            if (!compressor.option("global_defs")) return this;
+            this.figure_out_scope({ ie8: compressor.option("ie8") });
+            return this.transform(new TreeTransformer(function(node) {
+                var def = node._find_defs(compressor, "");
+                if (!def) return;
+                var level = 0, child = node, parent;
+                while (parent = this.parent(level++)) {
+                    if (!(parent instanceof AST_PropAccess)) break;
+                    if (parent.expression !== child) break;
+                    child = parent;
+                }
+                if (is_lhs(child, parent)) {
+                    warn(compressor, node);
+                    return;
+                }
+                return def;
+            }));
+        });
         def(AST_Node, noop);
         def(AST_Dot, function(compressor, suffix) {
             return this.expression._find_defs(compressor, "." + this.property + suffix);
         });
+        def(AST_SymbolDeclaration, function(compressor) {
+            if (!this.global()) return;
+            if (HOP(compressor.option("global_defs"), this.name)) warn(compressor, this);
+        });
         def(AST_SymbolRef, function(compressor, suffix) {
             if (!this.global()) return;
-            var name;
             var defines = compressor.option("global_defs");
-            if (defines && HOP(defines, (name = this.name + suffix))) {
-                var node = to_node(defines[name], this);
-                var top = compressor.find_parent(AST_Toplevel);
-                node.walk(new TreeWalker(function(node) {
-                    if (node instanceof AST_SymbolRef) {
-                        node.scope = top;
-                        node.thedef = top.def_global(node);
-                    }
-                }));
-                return node;
-            }
+            var name = this.name + suffix;
+            if (HOP(defines, name)) return to_node(defines[name], this);
         });
     })(function(node, func) {
         node.DEFMETHOD("_find_defs", func);
@@ -5718,10 +5721,6 @@ merge(Compressor.prototype, {
     }
 
     OPT(AST_SymbolRef, function(self, compressor) {
-        var def = self.resolve_defines(compressor);
-        if (def) {
-            return def.optimize(compressor);
-        }
         if (!compressor.option("ie8")
             && is_undeclared_ref(self)
             // testing against `self.scope.uses_with` is an optimization
@@ -6405,10 +6404,6 @@ merge(Compressor.prototype, {
                 col: self.start.col
             });
         }
-        var def = self.resolve_defines(compressor);
-        if (def) {
-            return def.optimize(compressor);
-        }
         if (is_lhs(self, compressor.parent())) return self;
         if (compressor.option("unsafe_proto")
             && self.expression instanceof AST_Dot
@@ -6464,14 +6459,6 @@ merge(Compressor.prototype, {
         }
         return self;
     });
-
-    OPT(AST_VarDef, function(self, compressor) {
-        var defines = compressor.option("global_defs");
-        if (defines && HOP(defines, self.name.name)) {
-            compressor.warn('global_defs ' + self.name.name + ' redefined [{file}:{line},{col}]', self.start);
-        }
-        return self;
-    });
 })(function(node, optimizer) {
     node.DEFMETHOD("optimize", function(compressor) {
         var self = this;
index 37536c0..f3feabe 100644 (file)
@@ -1161,7 +1161,7 @@ function OutputStream(options) {
             def.print(output);
         });
         var p = output.parent();
-        if (p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon();
+        if (p && p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon();
     });
 
     function parenthesize_for_noin(node, output, noin) {
index 98fa3e9..f1ef81d 100644 (file)
@@ -142,7 +142,6 @@ mixed: {
     }
     expect_warnings: [
         "WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:4,22]",
-        "WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:5,22]",
         "WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:7,8]",
     ]
 }
@@ -197,3 +196,23 @@ issue_2167: {
         doWork();
     }
 }
+
+issue_3217: {
+    options = {
+        collapse_vars: true,
+        global_defs: {
+            "@o": "{fn:function(){var a=42;console.log(a)}}",
+        },
+        inline: true,
+        properties: true,
+        reduce_vars: true,
+        side_effects: true,
+        unused: true,
+    }
+    input: {
+        o.fn();
+    }
+    expect: {
+        console.log(42);
+    }
+}