enhance `evaluate` (#3995)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sat, 13 Jun 2020 18:50:26 +0000 (19:50 +0100)
committerGitHub <noreply@github.com>
Sat, 13 Jun 2020 18:50:26 +0000 (02:50 +0800)
lib/compress.js
test/compress/evaluate.js
test/ufuzz/index.js

index 860eae1..cf28618 100644 (file)
@@ -1485,7 +1485,7 @@ merge(Compressor.prototype, {
             }
 
             function is_last_node(node, parent) {
-                if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right.tail_node());
+                if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right);
                 if (node instanceof AST_Call) {
                     var def, fn = node.expression;
                     if (fn instanceof AST_SymbolRef) {
@@ -3369,10 +3369,10 @@ merge(Compressor.prototype, {
                 var elements = [];
                 for (var i = 0; i < this.elements.length; i++) {
                     var element = this.elements[i];
-                    if (element instanceof AST_Hole) continue;
+                    if (element instanceof AST_Hole) return this;
                     var value = element._eval(compressor, ignore_side_effects, cached, depth);
                     if (element === value) return this;
-                    elements[i] = value;
+                    elements.push(value);
                 }
                 return elements;
             }
@@ -3384,16 +3384,11 @@ merge(Compressor.prototype, {
                 for (var i = 0; i < this.properties.length; i++) {
                     var prop = this.properties[i];
                     var key = prop.key;
-                    if (key instanceof AST_Symbol) {
-                        key = key.name;
-                    } else if (key instanceof AST_Node) {
-                        key = key._eval(compressor, ignore_side_effects, cached, depth);
-                        if (key === prop.key) return this;
-                    }
-                    if (typeof Object.prototype[key] === "function") {
-                        return this;
+                    if (key instanceof AST_Symbol) key = key.name;
+                    if (prop.value instanceof AST_Function) {
+                        if (typeof Object.prototype[key] == "function") return this;
+                        continue;
                     }
-                    if (prop.value instanceof AST_Function) continue;
                     val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
                     if (val[key] === prop.value) return this;
                 }
@@ -3480,10 +3475,10 @@ merge(Compressor.prototype, {
               case "&"  : result = left &   right; break;
               case "^"  : result = left ^   right; break;
               case "+"  : result = left +   right; break;
+              case "-"  : result = left -   right; break;
               case "*"  : result = left *   right; break;
               case "/"  : result = left /   right; break;
               case "%"  : result = left %   right; break;
-              case "-"  : result = left -   right; break;
               case "<<" : result = left <<  right; break;
               case ">>" : result = left >>  right; break;
               case ">>>": result = left >>> right; break;
@@ -3495,7 +3490,13 @@ merge(Compressor.prototype, {
               case "<=" : result = left <=  right; break;
               case ">"  : result = left >   right; break;
               case ">=" : result = left >=  right; break;
-              default   : return this;
+              case "in":
+                if (right && typeof right == "object" && HOP(right, left)) {
+                    result = true;
+                    break;
+                }
+              default:
+                return this;
             }
             if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result;
             if (compressor.option("unsafe_math")
@@ -3849,7 +3850,7 @@ merge(Compressor.prototype, {
         def(AST_Binary, function(compressor) {
             return this.left.has_side_effects(compressor)
                 || this.right.has_side_effects(compressor)
-                || this.operator == "in" && !is_object(this.right.tail_node());
+                || this.operator == "in" && !is_object(this.right);
         });
         def(AST_Block, function(compressor) {
             return any(this.body, compressor);
@@ -3962,7 +3963,7 @@ merge(Compressor.prototype, {
         def(AST_Binary, function(compressor) {
             return this.left.may_throw(compressor)
                 || this.right.may_throw(compressor)
-                || this.operator == "in" && !is_object(this.right.tail_node());
+                || this.operator == "in" && !is_object(this.right);
         });
         def(AST_Block, function(compressor) {
             return any(this.body, compressor);
@@ -4057,7 +4058,7 @@ merge(Compressor.prototype, {
         def(AST_Binary, function() {
             return this.left.is_constant_expression()
                 && this.right.is_constant_expression()
-                && (this.operator != "in" || is_object(this.right.tail_node()));
+                && (this.operator != "in" || is_object(this.right));
         });
         def(AST_Constant, return_true);
         def(AST_Lambda, function(scope) {
@@ -5163,7 +5164,7 @@ merge(Compressor.prototype, {
             return this;
         });
         def(AST_Binary, function(compressor, first_in_statement) {
-            if (this.operator == "in" && !is_object(this.right.tail_node())) {
+            if (this.operator == "in" && !is_object(this.right)) {
                 var left = this.left.drop_side_effect_free(compressor, first_in_statement);
                 if (left === this.left) return this;
                 var node = this.clone();
@@ -6921,10 +6922,9 @@ merge(Compressor.prototype, {
     var indexFns = makePredicate("indexOf lastIndexOf");
     var commutativeOperators = makePredicate("== === != !== * & | ^");
     function is_object(node) {
-        while (node instanceof AST_SymbolRef) {
+        while ((node = node.tail_node()) instanceof AST_SymbolRef) {
             node = node.fixed_value();
             if (!node) return false;
-            node = node.tail_node();
         }
         return node instanceof AST_Array
             || node instanceof AST_Lambda
index e5ecc6d..4fecd70 100644 (file)
@@ -560,6 +560,8 @@ unsafe_array: {
     input: {
         var a = "PASS";
         Array.prototype[1] = a;
+        console.log([, ].length);
+        console.log("" + [, , ]);
         console.log([1, , 3][1]);
         console.log([1, 2, 3, a] + 1);
         console.log([1, 2, 3, 4] + 1);
@@ -574,6 +576,8 @@ unsafe_array: {
     expect: {
         var a = "PASS";
         Array.prototype[1] = a;
+        console.log([, ].length);
+        console.log("" + [, , ]);
         console.log([1, , 3][1]);
         console.log([1, 2, 3, a] + 1);
         console.log("1,2,3,41");
@@ -586,6 +590,8 @@ unsafe_array: {
         console.log([[1, 2], , [3, 4]][1][1] + 1);
     }
     expect_stdout: [
+        "1",
+        ",PASS",
         "PASS",
         "1,2,3,PASS1",
         "1,2,3,41",
@@ -2770,3 +2776,39 @@ issue_3988: {
     }
     expect_stdout: "0"
 }
+
+operator_in: {
+    options = {
+        evaluate: true,
+        unsafe: true,
+    }
+    input: {
+        Object.prototype.PASS = 0;
+        console.log(0 in [ 1 ]);
+        console.log(0 in [ , ]);
+        console.log(0 / 0 in { NaN: 2 });
+        console.log("PASS" in { });
+        console.log("FAIL" in { });
+        console.log("toString" in { });
+        console.log("toString" in { toString: 3 });
+    }
+    expect: {
+        Object.prototype.PASS = 0;
+        console.log(true);
+        console.log(0 in [ , ]);
+        console.log(true);
+        console.log("PASS" in { });
+        console.log("FAIL" in { });
+        console.log("toString" in { });
+        console.log(true);
+    }
+    expect_stdout: [
+        "true",
+        "false",
+        "true",
+        "true",
+        "false",
+        "true",
+        "true",
+    ]
+}
index 006b4ca..1a8eaff 100644 (file)
@@ -767,6 +767,10 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
         return createArrayLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
       case p++:
         return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
+      case p++:
+        return createValue() + " in " + createArrayLiteral(recurmax, stmtDepth, canThrow);
+      case p++:
+        return createValue() + " in " + createObjectLiteral(recurmax, stmtDepth, canThrow);
       case p++:
         var name = getVarName();
         var s = name + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";