fix corner case in `evaluate` (#3728)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 18 Feb 2020 19:35:37 +0000 (19:35 +0000)
committerGitHub <noreply@github.com>
Tue, 18 Feb 2020 19:35:37 +0000 (19:35 +0000)
- augment `ufuzz` for further `RegExp` testing

lib/compress.js
test/compress/regexp.js
test/ufuzz/index.js

index e8a7179..23c7304 100644 (file)
@@ -315,7 +315,7 @@ merge(Compressor.prototype, {
         if (value instanceof AST_Array) return native_fns.Array[name];
         if (value instanceof AST_Function) return native_fns.Function[name];
         if (value instanceof AST_Object) return native_fns.Object[name];
-        if (value instanceof AST_RegExp) return native_fns.RegExp[name];
+        if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global;
     }
 
     function is_modified(compressor, tw, node, value, level, immutable) {
@@ -3003,6 +3003,7 @@ merge(Compressor.prototype, {
         ].concat(object_fns),
         Object: object_fns,
         RegExp: [
+            "exec",
             "test",
         ].concat(object_fns),
         String: [
@@ -3085,6 +3086,7 @@ merge(Compressor.prototype, {
             cached.forEach(function(node) {
                 delete node._eval;
             });
+            if (ignore_side_effects) return val;
             if (!val || val instanceof RegExp) return val;
             if (typeof val == "function" || typeof val == "object") return this;
             return val;
@@ -3400,6 +3402,7 @@ merge(Compressor.prototype, {
                     if (val == null || val === e) return this;
                     var native_fn = native_fns[val.constructor.name];
                     if (!native_fn || !native_fn[key]) return this;
+                    if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this;
                 }
                 var args = eval_args(this.args);
                 if (!args) return this;
index defc007..e8b2da7 100644 (file)
@@ -255,3 +255,171 @@ issue_3434_4: {
         "false true",
     ]
 }
+
+exec: {
+    options = {
+        evaluate: true,
+        loops: true,
+        unsafe: true,
+    }
+    input: {
+        while (/a/.exec("AAA"))
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect: {
+        for (;null;)
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+}
+
+exec_global: {
+    options = {
+        evaluate: true,
+        loops: true,
+        unsafe: true,
+    }
+    input: {
+        while (/a/g.exec("AAA"))
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect: {
+        for (;null;)
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+}
+
+test: {
+    options = {
+        evaluate: true,
+        unsafe: true,
+    }
+    input: {
+        while (/a/.test("AAA"))
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect: {
+        while (false)
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+}
+
+test_global: {
+    options = {
+        evaluate: true,
+        unsafe: true,
+    }
+    input: {
+        while (/a/g.test("AAA"))
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect: {
+        while (false)
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+}
+
+var_exec: {
+    options = {
+        evaluate: true,
+        loops: true,
+        reduce_vars: true,
+        toplevel: true,
+        unsafe: true,
+    }
+    input: {
+        var r = /a/;
+        while (r.exec("AAA"))
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect: {
+        var r = /a/;
+        for (;null;)
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+}
+
+var_exec_global: {
+    options = {
+        evaluate: true,
+        loops: true,
+        reduce_vars: true,
+        toplevel: true,
+        unsafe: true,
+    }
+    input: {
+        var r = /a/g;
+        while (r.exec("aaa"))
+            console.log("PASS");
+    }
+    expect: {
+        var r = /a/g;
+        for (;r.exec("aaa");)
+            console.log("PASS");
+    }
+    expect_stdout: [
+        "PASS",
+        "PASS",
+        "PASS",
+    ]
+}
+
+var_test: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unsafe: true,
+    }
+    input: {
+        var r = /a/;
+        while (r.test("AAA"))
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect: {
+        var r = /a/;
+        while (false)
+            console.log("FAIL");
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+}
+
+var_test_global: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unsafe: true,
+    }
+    input: {
+        var r = /a/g;
+        while (r.test("aaa"))
+            console.log("PASS");
+    }
+    expect: {
+        var r = /a/g;
+        while (r.test("aaa"))
+            console.log("PASS");
+    }
+    expect_stdout: [
+        "PASS",
+        "PASS",
+        "PASS",
+    ]
+}
index 7032fc3..2083a90 100644 (file)
@@ -742,6 +742,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
         return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || a || 3).toString() ";
       case p++:
         return " /[abc4]/.test(((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || b || 5).toString()) ";
+      case p++:
+        return " /[abc4]/g.exec(((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || b || 5).toString()) ";
       case p++:
         return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) +
             ") || " + rng(10) + ").toString()[" +