enhance `evaluate` (#3714)
authorAlex Lam S.L <alexlamsl@gmail.com>
Wed, 12 Feb 2020 01:01:17 +0000 (01:01 +0000)
committerGitHub <noreply@github.com>
Wed, 12 Feb 2020 01:01:17 +0000 (01:01 +0000)
lib/compress.js
test/compress/reduce_vars.js

index 0fcd199..55e40ef 100644 (file)
@@ -3073,10 +3073,10 @@ merge(Compressor.prototype, {
         // is returned.
         // They can be distinguished as constant value is never a
         // descendant of AST_Node.
-        AST_Node.DEFMETHOD("evaluate", function(compressor) {
+        AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) {
             if (!compressor.option("evaluate")) return this;
             var cached = [];
-            var val = this._eval(compressor, cached, 1);
+            var val = this._eval(compressor, ignore_side_effects, cached, 1);
             cached.forEach(function(node) {
                 delete node._eval;
             });
@@ -3104,6 +3104,19 @@ merge(Compressor.prototype, {
         def(AST_Constant, function() {
             return this.value;
         });
+        def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) {
+            if (!ignore_side_effects) return this;
+            if (this.operator != "=") return this;
+            var node = this.right;
+            var value = node._eval(compressor, ignore_side_effects, cached, depth);
+            return value === node ? this : value;
+        });
+        def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
+            if (!ignore_side_effects) return this;
+            var node = this.tail_node();
+            var value = node._eval(compressor, ignore_side_effects, cached, depth);
+            return value === node ? this : value;
+        });
         def(AST_Function, function(compressor) {
             if (compressor.option("unsafe")) {
                 var fn = function() {};
@@ -3115,12 +3128,12 @@ merge(Compressor.prototype, {
             }
             return this;
         });
-        def(AST_Array, function(compressor, cached, depth) {
+        def(AST_Array, function(compressor, ignore_side_effects, cached, depth) {
             if (compressor.option("unsafe")) {
                 var elements = [];
                 for (var i = 0; i < this.elements.length; i++) {
                     var element = this.elements[i];
-                    var value = element._eval(compressor, cached, depth);
+                    var value = element._eval(compressor, ignore_side_effects, cached, depth);
                     if (element === value) return this;
                     elements.push(value);
                 }
@@ -3128,7 +3141,7 @@ merge(Compressor.prototype, {
             }
             return this;
         });
-        def(AST_Object, function(compressor, cached, depth) {
+        def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
             if (compressor.option("unsafe")) {
                 var val = {};
                 for (var i = 0; i < this.properties.length; i++) {
@@ -3137,14 +3150,14 @@ merge(Compressor.prototype, {
                     if (key instanceof AST_Symbol) {
                         key = key.name;
                     } else if (key instanceof AST_Node) {
-                        key = key._eval(compressor, cached, depth);
+                        key = key._eval(compressor, ignore_side_effects, cached, depth);
                         if (key === prop.key) return this;
                     }
                     if (typeof Object.prototype[key] === 'function') {
                         return this;
                     }
                     if (prop.value instanceof AST_Function) continue;
-                    val[key] = prop.value._eval(compressor, cached, depth);
+                    val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
                     if (val[key] === prop.value) return this;
                 }
                 return val;
@@ -3152,7 +3165,7 @@ merge(Compressor.prototype, {
             return this;
         });
         var non_converting_unary = makePredicate("! typeof void");
-        def(AST_UnaryPrefix, function(compressor, cached, depth) {
+        def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) {
             var e = this.expression;
             // Function would be evaluated to an array and so typeof would
             // incorrectly return 'object'. Hence making is a special case.
@@ -3164,7 +3177,7 @@ merge(Compressor.prototype, {
                 return typeof function(){};
             }
             if (!non_converting_unary[this.operator]) depth++;
-            var v = e._eval(compressor, cached, depth);
+            var v = e._eval(compressor, ignore_side_effects, cached, depth);
             if (v === this.expression) return this;
             switch (this.operator) {
               case "!": return !v;
@@ -3187,12 +3200,12 @@ merge(Compressor.prototype, {
             return this;
         });
         var non_converting_binary = makePredicate("&& || === !==");
-        def(AST_Binary, function(compressor, cached, depth) {
+        def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) {
             if (!non_converting_binary[this.operator]) depth++;
-            var left = this.left._eval(compressor, cached, depth);
+            var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
             if (left === this.left) return this;
             if (this.operator == (left ? "||" : "&&")) return left;
-            var right = this.right._eval(compressor, cached, depth);
+            var right = this.right._eval(compressor, ignore_side_effects, cached, depth);
             if (right === this.right) return this;
             var result;
             switch (this.operator) {
@@ -3235,14 +3248,14 @@ merge(Compressor.prototype, {
                 return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
             }
         });
-        def(AST_Conditional, function(compressor, cached, depth) {
-            var condition = this.condition._eval(compressor, cached, depth);
+        def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) {
+            var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth);
             if (condition === this.condition) return this;
             var node = condition ? this.consequent : this.alternative;
-            var value = node._eval(compressor, cached, depth);
+            var value = node._eval(compressor, ignore_side_effects, cached, depth);
             return value === node ? this : value;
         });
-        def(AST_SymbolRef, function(compressor, cached, depth) {
+        def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) {
             var fixed = this.fixed_value();
             if (!fixed) return this;
             var value;
@@ -3250,7 +3263,7 @@ merge(Compressor.prototype, {
                 value = fixed._eval();
             } else {
                 this._eval = return_this;
-                value = fixed._eval(compressor, cached, depth);
+                value = fixed._eval(compressor, ignore_side_effects, cached, depth);
                 delete this._eval;
                 if (value === fixed) return this;
                 fixed._eval = function() {
@@ -3307,11 +3320,11 @@ merge(Compressor.prototype, {
             ],
         });
         var regexp_props = makePredicate("global ignoreCase multiline source");
-        def(AST_PropAccess, function(compressor, cached, depth) {
+        def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) {
             if (compressor.option("unsafe")) {
                 var key = this.property;
                 if (key instanceof AST_Node) {
-                    key = key._eval(compressor, cached, depth);
+                    key = key._eval(compressor, ignore_side_effects, cached, depth);
                     if (key === this.property) return this;
                 }
                 var exp = this.expression;
@@ -3321,7 +3334,7 @@ merge(Compressor.prototype, {
                     if (!static_value || !static_value[key]) return this;
                     val = global_objs[exp.name];
                 } else {
-                    val = exp._eval(compressor, cached, depth + 1);
+                    val = exp._eval(compressor, ignore_side_effects, cached, depth + 1);
                     if (val == null || val === exp) return this;
                     if (val instanceof RegExp) {
                         if (!regexp_props[key]) return this;
@@ -3340,7 +3353,7 @@ merge(Compressor.prototype, {
             }
             return this;
         });
-        def(AST_Call, function(compressor, cached, depth) {
+        def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
             var exp = this.expression;
             var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
             if (fn instanceof AST_Lambda) {
@@ -3361,14 +3374,14 @@ merge(Compressor.prototype, {
                     });
                 });
                 fn.evaluating = true;
-                var val = stat.value._eval(compressor, cached, depth);
+                var val = stat.value._eval(compressor, ignore_side_effects, cached, depth);
                 delete fn.evaluating;
                 if (val === stat.value) return this;
                 return val;
             } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
                 var key = exp.property;
                 if (key instanceof AST_Node) {
-                    key = key._eval(compressor, cached, depth);
+                    key = key._eval(compressor, ignore_side_effects, cached, depth);
                     if (key === exp.property) return this;
                 }
                 var val;
@@ -3378,7 +3391,7 @@ merge(Compressor.prototype, {
                     if (!static_fn || !static_fn[key]) return this;
                     val = global_objs[e.name];
                 } else {
-                    val = e._eval(compressor, cached, depth + 1);
+                    val = e._eval(compressor, ignore_side_effects, cached, depth + 1);
                     if (val == null || val === e) return this;
                     var native_fn = native_fns[val.constructor.name];
                     if (!native_fn || !native_fn[key]) return this;
@@ -3403,7 +3416,7 @@ merge(Compressor.prototype, {
                 var values = [];
                 for (var i = 0; i < args.length; i++) {
                     var arg = args[i];
-                    var value = arg._eval(compressor, cached, depth);
+                    var value = arg._eval(compressor, ignore_side_effects, cached, depth);
                     if (arg === value) return;
                     values.push(value);
                 }
@@ -4478,7 +4491,7 @@ merge(Compressor.prototype, {
             node.in_bool = true;
             var value = node.value;
             if (value) {
-                var ev = value.is_truthy() || value.tail_node().evaluate(compressor);
+                var ev = value.is_truthy() || value.evaluate(compressor, true);
                 if (!ev) {
                     value = value.drop_side_effect_free(compressor);
                     node.value = value ? make_sequence(node.value, [
@@ -4878,7 +4891,7 @@ merge(Compressor.prototype, {
 
     OPT(AST_Do, function(self, compressor) {
         if (!compressor.option("loops")) return self;
-        var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
+        var cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
         if (!(cond instanceof AST_Node)) {
             if (cond) return make_node(AST_For, self, {
                 body: make_node(AST_BlockStatement, self.body, {
@@ -5008,16 +5021,14 @@ merge(Compressor.prototype, {
         }
         if (self.condition) {
             var cond = self.condition.evaluate(compressor);
-            if (!(cond instanceof AST_Node)) {
-                if (cond) self.condition = null;
-                else if (!compressor.option("dead_code")) {
-                    var orig = self.condition;
-                    self.condition = make_node_from_constant(cond, self.condition);
-                    self.condition = best_of_expression(self.condition.transform(compressor), orig);
-                }
-            }
             if (cond instanceof AST_Node) {
-                cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
+                cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
+            } else if (cond) {
+                self.condition = null;
+            } else if (!compressor.option("dead_code")) {
+                var orig = self.condition;
+                self.condition = make_node_from_constant(cond, self.condition);
+                self.condition = best_of_expression(self.condition.transform(compressor), orig);
             }
             if (!cond) {
                 if (compressor.option("dead_code")) {
@@ -5109,7 +5120,7 @@ merge(Compressor.prototype, {
         }
         if (compressor.option("dead_code")) {
             if (cond instanceof AST_Node) {
-                cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
+                cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
             }
             if (!cond) {
                 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
@@ -5255,7 +5266,7 @@ merge(Compressor.prototype, {
         }
         if (!compressor.option("dead_code")) return self;
         if (value instanceof AST_Node) {
-            value = self.expression.tail_node().evaluate(compressor);
+            value = self.expression.evaluate(compressor, true);
         }
         var decl = [];
         var body = [];
@@ -5277,7 +5288,7 @@ merge(Compressor.prototype, {
                     eliminate_branch(branch, body[body.length - 1]);
                     continue;
                 }
-                if (exp instanceof AST_Node) exp = branch.expression.tail_node().evaluate(compressor);
+                if (exp instanceof AST_Node) exp = branch.expression.evaluate(compressor, true);
                 if (exp === value) {
                     exact_match = branch;
                     if (default_branch) {
@@ -6461,7 +6472,7 @@ merge(Compressor.prototype, {
                 }
                 // (x || false) && y => x ? y : false
                 if (self.left.operator == "||") {
-                    var lr = self.left.right.tail_node().evaluate(compressor);
+                    var lr = self.left.right.evaluate(compressor, true);
                     if (!lr) return make_node(AST_Conditional, self, {
                         condition: self.left.left,
                         consequent: self.right,
@@ -6495,7 +6506,7 @@ merge(Compressor.prototype, {
                 }
                 // x && true || y => x ? true : y
                 if (self.left.operator == "&&") {
-                    var lr = self.left.right.is_truthy() || self.left.right.tail_node().evaluate(compressor);
+                    var lr = self.left.right.is_truthy() || self.left.right.evaluate(compressor, true);
                     if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
                         condition: self.left.left,
                         consequent: self.left.right,
index 3331732..6db0a47 100644 (file)
@@ -2071,13 +2071,8 @@ issue_1670_6: {
     }
     expect: {
         (function(a) {
-            switch (1) {
-              case a = 1:
-                console.log(a);
-                break;
-              default:
-                console.log(2);
-            }
+            a = 1;
+            console.log(a);
         })(1);
     }
     expect_stdout: "1"