From 7052ce5aefefffbf8745a3ac13040448196b4b6c Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 18 Feb 2020 19:35:37 +0000 Subject: [PATCH] fix corner case in `evaluate` (#3728) - augment `ufuzz` for further `RegExp` testing --- lib/compress.js | 5 +- test/compress/regexp.js | 168 ++++++++++++++++++++++++++++++++++++++++ test/ufuzz/index.js | 2 + 3 files changed, 174 insertions(+), 1 deletion(-) diff --git a/lib/compress.js b/lib/compress.js index e8a71791..23c73048 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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; diff --git a/test/compress/regexp.js b/test/compress/regexp.js index defc0079..e8b2da73 100644 --- a/test/compress/regexp.js +++ b/test/compress/regexp.js @@ -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", + ] +} diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index 7032fc3c..2083a90e 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -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()[" + -- 2.34.1