diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2021-01-19 23:27:32 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-20 07:27:32 +0800 |
commit | d37ee4d41c70aa309550e7337f75126ef2932c62 (patch) | |
tree | f92d069caa23a7bb6824014a6139b68c8596811c | |
parent | 7793c6c389930dd10fdce5a12b24c95d5bb93933 (diff) | |
download | tracifyjs-d37ee4d41c70aa309550e7337f75126ef2932c62.tar.gz tracifyjs-d37ee4d41c70aa309550e7337f75126ef2932c62.zip |
support asynchronous test cases properly (#4529)
-rw-r--r-- | .github/workflows/ufuzz.yml | 2 | ||||
-rw-r--r-- | lib/compress.js | 9 | ||||
-rw-r--r-- | test/compress.js | 11 | ||||
-rw-r--r-- | test/compress/awaits.js | 30 | ||||
-rw-r--r-- | test/compress/sandbox.js | 49 | ||||
-rw-r--r-- | test/mocha/cli.js | 6 | ||||
-rw-r--r-- | test/reduce.js | 24 | ||||
-rw-r--r-- | test/sandbox.js | 281 | ||||
-rw-r--r-- | test/ufuzz/index.js | 67 | ||||
-rw-r--r-- | test/ufuzz/job.js | 2 |
10 files changed, 347 insertions, 134 deletions
diff --git a/.github/workflows/ufuzz.yml b/.github/workflows/ufuzz.yml index 48a3ba6c..beefd96a 100644 --- a/.github/workflows/ufuzz.yml +++ b/.github/workflows/ufuzz.yml @@ -32,6 +32,8 @@ jobs: - uses: actions/checkout@v2 - name: Install GNU Core Utilities if: ${{ startsWith(matrix.os, 'macos') }} + env: + HOMEBREW_NO_INSTALL_CLEANUP: 1 shell: bash run: | brew install coreutils diff --git a/lib/compress.js b/lib/compress.js index 21072f0c..4710b69f 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -382,6 +382,7 @@ merge(Compressor.prototype, { }); } + var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/; (function(def) { def(AST_Node, noop); @@ -603,7 +604,7 @@ merge(Compressor.prototype, { if (!is_arguments(def)) return; var key = node.property; if (key.is_constant()) key = key.value; - if (!(key instanceof AST_Node) && !/^[1-9]*[0-9]$/.test(key)) return; + if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return; def.reassigned = true; (key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) { if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false; @@ -8160,7 +8161,7 @@ merge(Compressor.prototype, { try { var code = "n(function(" + self.args.slice(0, -1).map(function(arg) { return arg.value; - }).join(",") + "){" + self.args[self.args.length - 1].value + "})"; + }).join() + "){" + self.args[self.args.length - 1].value + "})"; var ast = parse(code); var mangle = { ie8: compressor.option("ie8") }; ast.figure_out_scope(mangle); @@ -8183,7 +8184,7 @@ merge(Compressor.prototype, { make_node(AST_String, self, { value: fun.argnames.map(function(arg) { return arg.print_to_string(); - }).join(",") + }).join(), }), make_node(AST_String, self.args[self.args.length - 1], { value: code.get().replace(/^\{|\}$/g, "") @@ -10846,7 +10847,7 @@ merge(Compressor.prototype, { flush(); values.push(prop); } - if (found && !generated && typeof key == "string" && /^[1-9]*[0-9]$/.test(key)) { + if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) { generated = true; if (keys.has(key)) prop = keys.get(key)[0]; prop.key = make_node(AST_Number, prop, { diff --git a/test/compress.js b/test/compress.js index 29ea5a4f..b4dbaba6 100644 --- a/test/compress.js +++ b/test/compress.js @@ -209,7 +209,7 @@ function reminify(orig_options, input_code, input_formatted, stdout) { } else { var toplevel = sandbox.has_toplevel(options); var expected = stdout[toplevel ? 1 : 0]; - var actual = run_code(result.code, toplevel); + var actual = sandbox.run_code(result.code, toplevel); if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) { actual = expected; } @@ -244,11 +244,6 @@ function reminify(orig_options, input_code, input_formatted, stdout) { return true; } -function run_code(code, toplevel) { - var result = sandbox.run_code(code, toplevel); - return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result; -} - function test_case(test) { log(" Running test [{name}]", { name: test.name }); U.AST_Node.enable_validation(); @@ -380,7 +375,7 @@ function test_case(test) { } } if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) { - var stdout = [ run_code(input_code), run_code(input_code, true) ]; + var stdout = [ sandbox.run_code(input_code), sandbox.run_code(input_code, true) ]; var toplevel = sandbox.has_toplevel({ compress: test.options, mangle: test.mangle @@ -409,7 +404,7 @@ function test_case(test) { }); return false; } - actual = run_code(output_code, toplevel); + actual = sandbox.run_code(output_code, toplevel); if (!sandbox.same_stdout(test.expect_stdout, actual)) { log([ "!!! failed", diff --git a/test/compress/awaits.js b/test/compress/awaits.js index 89d11bb9..4a90ca5e 100644 --- a/test/compress/awaits.js +++ b/test/compress/awaits.js @@ -612,22 +612,32 @@ issue_4340: { call_expression: { input: { console.log(typeof async function(log) { - (await log)("FAIL"); + (await log)("foo"); }(console.log).then); + console.log("bar"); } - expect_exact: 'console.log(typeof async function(log){(await log)("FAIL")}(console.log).then);' - expect_stdout: "function" + expect_exact: 'console.log(typeof async function(log){(await log)("foo")}(console.log).then);console.log("bar");' + expect_stdout: [ + "function", + "bar", + "foo", + ] node_version: ">=8" } property_access_expression: { input: { console.log(typeof async function(con) { - (await con).log("FAIL"); + (await con).log("foo"); }(console).then); + console.log("bar"); } - expect_exact: 'console.log(typeof async function(con){(await con).log("FAIL")}(console).then);' - expect_stdout: "function" + expect_exact: 'console.log(typeof async function(con){(await con).log("foo")}(console).then);console.log("bar");' + expect_stdout: [ + "function", + "bar", + "foo", + ] node_version: ">=8" } @@ -685,20 +695,18 @@ reduce_iife_3: { input: { var a = "foo"; (async function() { - console.log(a); - console.log(await a); + console.log(a, await a, a, await a); })(); a = "bar"; } expect: { var a = "foo"; (async function() { - console.log(a); - console.log(await a); + console.log(a, await a, a, await a); })(); a = "bar"; } - expect_stdout: "foo" + expect_stdout: "foo foo bar bar" node_version: ">=8" } diff --git a/test/compress/sandbox.js b/test/compress/sandbox.js index 9356f6f3..338afd11 100644 --- a/test/compress/sandbox.js +++ b/test/compress/sandbox.js @@ -13,6 +13,23 @@ console_log: { ] } +console_log_console: { + input: { + var log = console.log; + log(console); + log(typeof console.log); + } + expect: { + var log = console.log; + log(console); + log(typeof console.log); + } + expect_stdout: [ + "{ log: 'function(){}' }", + "function", + ] +} + typeof_arguments: { options = { evaluate: true, @@ -81,6 +98,38 @@ log_global: { expect_stdout: "[object global]" } +timers: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var count = 0, interval = 1000, duration = 3210; + var timer = setInterval(function() { + console.log(++count); + }, interval); + setTimeout(function() { + clearInterval(timer); + }, duration); + } + expect: { + var count = 0; + var timer = setInterval(function() { + console.log(++count); + }, 1000); + setTimeout(function() { + clearInterval(timer); + }, 3210); + } + expect_stdout: [ + "1", + "2", + "3", + ] + node_version: ">=0.12" +} + issue_4054: { input: { console.log({ diff --git a/test/mocha/cli.js b/test/mocha/cli.js index c050b717..475eae65 100644 --- a/test/mocha/cli.js +++ b/test/mocha/cli.js @@ -56,7 +56,7 @@ describe("bin/uglifyjs", function() { "--source-map", [ "names=true", "url=inline", - ].join(","), + ].join(), ].join(" "), function(err, stdout) { if (err) throw err; var expected = [ @@ -84,7 +84,7 @@ describe("bin/uglifyjs", function() { "--source-map", [ "names=false", "url=inline", - ].join(","), + ].join(), ].join(" "), function(err, stdout) { if (err) throw err; var expected = [ @@ -171,7 +171,7 @@ describe("bin/uglifyjs", function() { "content=" + mapFile, "includeSources", "url=inline", - ].join(","), + ].join(), ].join(" "); var child = exec(command, function(err, stdout) { diff --git a/test/reduce.js b/test/reduce.js index 91df8bce..9d5f053c 100644 --- a/test/reduce.js +++ b/test/reduce.js @@ -70,8 +70,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options) } else if (differs.error) { differs.warnings = warnings; return differs; - } else if (is_error(differs.unminified_result) - && is_error(differs.minified_result) + } else if (sandbox.is_error(differs.unminified_result) + && sandbox.is_error(differs.minified_result) && differs.unminified_result.name == differs.minified_result.name) { return { code: [ @@ -558,8 +558,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options) log(code); log(diff.error.stack); log("*** Discarding permutation and continuing."); - } else if (is_error(diff.unminified_result) - && is_error(diff.minified_result) + } else if (sandbox.is_error(diff.unminified_result) + && sandbox.is_error(diff.minified_result) && diff.unminified_result.name == diff.minified_result.name) { // ignore difference in error messages caused by minification diff_error_message = testcase; @@ -600,10 +600,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options) } var lines = [ "" ]; if (isNaN(max_timeout)) { - lines.push("// minify error: " + to_comment(strip_color_codes(differs.minified_result.stack))); + lines.push("// minify error: " + to_comment(differs.minified_result.stack)); } else { - var unminified_result = strip_color_codes(differs.unminified_result); - var minified_result = strip_color_codes(differs.minified_result); + var unminified_result = differs.unminified_result; + var minified_result = differs.minified_result; if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) { lines.push( "// (stringified)", @@ -624,10 +624,6 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options) } }; -function strip_color_codes(value) { - return ("" + value).replace(/\u001b\[\d+m/g, ""); -} - function to_comment(value) { return ("" + value).replace(/\n/g, "\n// "); } @@ -665,12 +661,8 @@ function has_loopcontrol(body, loop, label) { return found; } -function is_error(result) { - return result && typeof result.name == "string" && typeof result.message == "string"; -} - function is_timed_out(result) { - return is_error(result) && /timed out/.test(result.message); + return sandbox.is_error(result) && /timed out/.test(result.message); } function is_statement(node) { diff --git a/test/sandbox.js b/test/sandbox.js index 78a3435e..540b2339 100644 --- a/test/sandbox.js +++ b/test/sandbox.js @@ -1,45 +1,174 @@ +var execSync = require("child_process").execSync; var semver = require("semver"); var vm = require("vm"); -var setupContext = new vm.Script([ - "[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {", - " f.toString = Function.prototype.toString;", - "});", - "Function.prototype.toString = function() {", - " var id = 100000;", - " return function() {", - " var n = this.name;", - " if (!/^F[0-9]{6}N$/.test(n)) {", - ' n = "F" + ++id + "N";', -].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [ - ' Object.defineProperty(this, "name", {', - " get: function() {", - " return n;", - " }", - " });", -] : [], [ - " }", - ' return "function(){}";', - " };", - "}();", - "this;", -]).join("\n")); +setup_log(); +var setup_code = "(" + setup + ")(this, " + setup_log + ", " + find_builtins() + ");"; +exports.has_toplevel = function(options) { + return options.toplevel + || options.mangle && options.mangle.toplevel + || options.compress && options.compress.toplevel; +}; +exports.is_error = is_error; +exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, toplevel, timeout) { + var stdout = run_code_vm(code, toplevel, timeout); + if (typeof stdout != "string" || !/arguments/.test(code)) return stdout; + do { + var prev = stdout; + stdout = run_code_vm(code, toplevel, timeout); + } while (prev !== stdout); + return stdout; +} : semver.satisfies(process.version, "<0.12") ? run_code_vm : function(code, toplevel, timeout) { + if (/\basync([ \t]+[^\s()[\]{},.&|!~=*%/+-]+|[ \t]*\([\s\S]*?\))[ \t]*=>|\b(async[ \t]+function|setInterval|setTimeout)\b/.test(code)) { + return run_code_exec(code, toplevel, timeout); + } else { + return run_code_vm(code, toplevel, timeout); + } +}; +exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) { + if (typeof expected != typeof actual) return false; + if (is_error(expected)) { + if (expected.name !== actual.name) return false; + if (typeof actual.message != "string") return false; + expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1); + actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1); + } + return strip_func_ids(expected) == strip_func_ids(actual); +} : function(expected, actual) { + return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual); +}; + +function is_error(result) { + return result && typeof result.name == "string" && typeof result.message == "string"; +} + +function strip_color_codes(value) { + return value.replace(/\u001b\[\d+m/g, ""); +} -function createContext() { - var ctx = vm.createContext(Object.defineProperties({}, { - console: { value: { log: log } }, +function strip_func_ids(text) { + return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>"); +} + +function setup_log() { + var inspect = require("util").inspect; + if (inspect.defaultOptions) { + var log_options = { + breakLength: Infinity, + colors: false, + compact: true, + customInspect: false, + depth: Infinity, + maxArrayLength: Infinity, + maxStringLength: Infinity, + showHidden: false, + }; + for (var name in log_options) { + if (name in inspect.defaultOptions) inspect.defaultOptions[name] = log_options[name]; + } + } + return inspect; +} + +function find_builtins() { + setup_code = "console.log(Object.keys(this));"; + var builtins = run_code_vm(""); + if (semver.satisfies(process.version, ">=0.12")) builtins += ".concat(" + run_code_exec("") + ")"; + return builtins; +} + +function setup(global, setup_log, builtins) { + [ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) { + f.toString = Function.prototype.toString; + }); + Function.prototype.toString = function() { + var configurable = Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable; + var id = 100000; + return function() { + var n = this.name; + if (!/^F[0-9]{6}N$/.test(n)) { + n = "F" + ++id + "N"; + if (configurable) Object.defineProperty(this, "name", { + get: function() { + return n; + } + }); + } + return "function(){}"; + }; + }(); + var process = global.process; + if (process) { + var inspect = setup_log(); + process.on("uncaughtException", function(ex) { + var value = ex; + if (value instanceof Error) { + value = {}; + for (var name in ex) value[name] = ex[name]; + } + process.stderr.write(inspect(value) + "\n\n-----===== UNCAUGHT EXCEPTION =====-----\n\n"); + throw ex; + }).on("unhandledRejection", function() {}); + } + var log = console.log; + var safe_console = { + log: function(msg) { + if (arguments.length == 1 && typeof msg == "string") return log("%s", msg); + return log.apply(null, [].map.call(arguments, function(arg) { + return safe_log(arg, { + level: 3, + original: [], + replaced: [], + }); + })); + }, + }; + var props = { + // for Node.js v8 + console: { + get: function() { + return safe_console; + }, + }, global: { get: self }, self: { get: self }, window: { get: self }, - })); - var global = setupContext.runInContext(ctx); - return ctx; + }; + [ + // for Node.js v0.12 + "Buffer", + "clearInterval", + "clearTimeout", + // for Node.js v0.12 + "DTRACE_NET_STREAM_END", + // for Node.js v8 + "process", + "setInterval", + "setTimeout", + ].forEach(function(name) { + var value = global[name]; + props[name] = { + get: function() { + return value; + }, + }; + }); + builtins.forEach(function(name) { + try { + delete global[name]; + } catch (e) {} + }); + Object.defineProperties(global, props); + // for Node.js v8+ + global.toString = function() { + return "[object global]"; + }; function self() { return this; } - function safe_log(arg, level) { + function safe_log(arg, cache) { if (arg) switch (typeof arg) { case "function": return arg.toString(); @@ -48,23 +177,27 @@ function createContext() { if (/Error$/.test(arg.name)) return arg.toString(); if (typeof arg.then == "function") return "[object Promise]"; arg.constructor.toString(); - if (level--) for (var key in arg) { + var index = cache.original.indexOf(arg); + if (index >= 0) return cache.replaced[index]; + if (--cache.level < 0) break; + var value = {}; + cache.original.push(arg); + cache.replaced.push(value); + for (var key in arg) { var desc = Object.getOwnPropertyDescriptor(arg, key); - if (!desc || !desc.get && !desc.set) arg[key] = safe_log(arg[key], level); + if (desc && (desc.get || desc.set)) { + Object.defineProperty(value, key, desc); + } else { + value[key] = safe_log(arg[key], cache); + } } + return value; } return arg; } - - function log(msg) { - if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg); - return console.log.apply(console, [].map.call(arguments, function(arg) { - return safe_log(arg, 3); - })); - } } -function run_code(code, toplevel, timeout) { +function run_code_vm(code, toplevel, timeout) { timeout = timeout || 5000; var stdout = ""; var original_write = process.stdout.write; @@ -72,8 +205,11 @@ function run_code(code, toplevel, timeout) { stdout += chunk; }; try { - vm.runInContext(toplevel ? "(function(){" + code + "})()" : code, createContext(), { timeout: timeout }); - return stdout; + var ctx = vm.createContext({ console: console }); + // for Node.js v6 + vm.runInContext(setup_code, ctx); + vm.runInContext(toplevel ? "(function(){" + code + "})();" : code, ctx, { timeout: timeout }); + return strip_color_codes(stdout); } catch (ex) { return ex; } finally { @@ -81,34 +217,35 @@ function run_code(code, toplevel, timeout) { } } -exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, toplevel, timeout) { - var stdout = run_code(code, toplevel, timeout); - if (typeof stdout != "string" || !/arguments/.test(code)) return stdout; - do { - var prev = stdout; - stdout = run_code(code, toplevel, timeout); - } while (prev !== stdout); - return stdout; -} : run_code; - -function strip_func_ids(text) { - return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>"); -} - -exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) { - if (typeof expected != typeof actual) return false; - if (typeof expected == "object" && typeof expected.name == "string" && typeof expected.message == "string") { - if (expected.name !== actual.name) return false; - if (typeof actual.message != "string") return false; - expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1); - actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1); +function run_code_exec(code, toplevel, timeout) { + if (toplevel) { + code = setup_code + "(function(){" + code + "})();"; + } else { + code = code.replace(/^((["'])[^"']*\2(;|$))?/, function(directive) { + return directive + setup_code; + }); } - return strip_func_ids(expected) == strip_func_ids(actual); -} : function(expected, actual) { - return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual); -}; -exports.has_toplevel = function(options) { - return options.toplevel - || options.mangle && options.mangle.toplevel - || options.compress && options.compress.toplevel; -}; + try { + return execSync('"' + process.argv[0] + '" --max-old-space-size=4096', { + encoding: "utf8", + input: code, + stdio: "pipe", + timeout: timeout || 5000, + }); + } catch (ex) { + var msg = ex.message.replace(/\r\n/g, "\n"); + if (/ETIMEDOUT/.test(msg)) return new Error("Script execution timed out."); + var value = msg.slice(msg.indexOf("\n") + 1, msg.indexOf("\n\n-----===== UNCAUGHT EXCEPTION =====-----\n\n")); + try { + value = vm.runInNewContext("(" + value.replace(/<([1-9][0-9]*) empty items?>/g, function(match, count) { + return new Array(+count).join(); + }) + ")"); + } catch (e) {} + var match = /\n([^:\s]*Error)(?:: ([\s\S]+?))?\n( at [\s\S]+)\n$/.exec(msg); + if (!match) return value; + ex = new global[match[1]](match[2]); + ex.stack = ex.stack.slice(0, ex.stack.indexOf(" at ")) + match[3]; + for (var name in value) ex[name] = value[name]; + return ex; + } +} diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index cb5da18d..a5275e5f 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -317,7 +317,6 @@ var VAR_NAMES = [ "NaN", "Infinity", "arguments", - "Math", "parseInt", "async", "await", @@ -1020,9 +1019,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) { case 0: return [ "[ ", - new Array(rng(3)).join(","), + new Array(rng(3)).join(), getVarName(NO_CONST), - new Array(rng(3)).join(","), + new Array(rng(3)).join(), " ] = ", createArrayLiteral(recurmax, stmtDepth, canThrow), ].join(""); @@ -1743,24 +1742,49 @@ function log(options) { } function sort_globals(code) { - var globals = sandbox.run_code("throw Object.keys(this).sort();" + code); + var globals = sandbox.run_code("throw Object.keys(this).sort(" + function(global) { + return function(m, n) { + return (n == "toString") - (m == "toString") + || (typeof global[n] == "function") - (typeof global[m] == "function") + || (m < n ? -1 : m > n ? 1 : 0); + }; + } + "(this));" + code); + if (!Array.isArray(globals)) { + errorln(); + errorln(); + errorln("//-------------------------------------------------------------"); + errorln("// !!! sort_globals() failed !!!"); + errorln("// expected Array, got:"); + try { + errorln("// " + JSON.stringify(globals)); + } catch (e) { + errorln("// " + globals); + } + errorln("//"); + errorln(code); + errorln(); + return code; + } return globals.length ? "var " + globals.map(function(name) { return name + "=" + name; - }).join(",") + ";" + code : code; + }).join() + ";" + code : code; } function fuzzy_match(original, uglified) { - uglified = uglified.split(" "); - var i = uglified.length; - original = original.split(" ", i); - while (--i >= 0) { - if (original[i] === uglified[i]) continue; - var a = +original[i]; - var b = +uglified[i]; - if (Math.abs((b - a) / a) < 1e-10) continue; - return false; + var m = [], n = []; + if (collect(original, m) !== collect(uglified, n)) return false; + for (var i = 0; i < m.length; i++) { + var a = m[i]; + var b = n[i]; + if (Math.abs((b - a) / a) > 1e-10) return false; } return true; + + function collect(input, nums) { + return input.replace(/-?([1-9][0-9]*(\.[0-9]+)?|0\.[0-9]+)(e-?[1-9][0-9]*)?/ig, function(num) { + return "<|" + nums.push(+num) + "|>"; + }); + } } function is_error_in(ex) { @@ -1768,7 +1792,7 @@ function is_error_in(ex) { } function is_error_spread(ex) { - return ex.name == "TypeError" && /Found non-callable @@iterator|is not iterable|undefined is not a function/.test(ex.message); + return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| is not a function/.test(ex.message); } function is_error_recursion(ex) { @@ -1782,6 +1806,7 @@ function patch_try_catch(orig, toplevel) { offset: 0, tries: [], } ]; + var tail_throw = '\nif (typeof UFUZZ_ERROR == "object") throw UFUZZ_ERROR;\n'; var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^)[{]+)\)|}\s*finally)\s*(?={)/g; while (stack.length) { var code = stack[0].code; @@ -1798,7 +1823,7 @@ function patch_try_catch(orig, toplevel) { var insert; if (/}\s*finally\s*$/.test(match[0])) { tries.shift(); - insert = 'if (typeof UFUZZ_ERROR == "object") throw UFUZZ_ERROR;'; + insert = tail_throw; } else { while (tries.length && tries[0].catch) tries.shift(); tries[0].catch = index - offset; @@ -1812,7 +1837,7 @@ function patch_try_catch(orig, toplevel) { "throw " + match[1] + ";", ].join("\n"); } - var new_code = code.slice(0, index) + insert + code.slice(index); + var new_code = code.slice(0, index) + insert + code.slice(index) + tail_throw; var result = sandbox.run_code(new_code, toplevel); if (typeof result != "object" || typeof result.name != "string" || typeof result.message != "string") { if (!stack.filled && match[1]) stack.push({ @@ -1904,8 +1929,12 @@ for (var round = 1; round <= num_iterations; round++) { ok = sandbox.same_stdout(sandbox.run_code(sort_globals(original_code)), sandbox.run_code(sort_globals(uglify_code))); } // ignore numerical imprecision caused by `unsafe_math` - if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == "string" && typeof uglify_result == "string") { - ok = fuzzy_match(original_result, uglify_result); + if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == typeof uglify_result) { + if (typeof original_result == "string") { + ok = fuzzy_match(original_result, uglify_result); + } else if (sandbox.is_error(original_result)) { + ok = original_result.name == uglify_result.name && fuzzy_match(original_result.message, uglify_result.message); + } if (!ok) { var fuzzy_result = sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"), toplevel); ok = sandbox.same_stdout(fuzzy_result, uglify_result); diff --git a/test/ufuzz/job.js b/test/ufuzz/job.js index 5cef23e9..6edfba53 100644 --- a/test/ufuzz/job.js +++ b/test/ufuzz/job.js @@ -2,7 +2,7 @@ var actions = require("./actions"); var child_process = require("child_process"); var args = [ - "--max-old-space-size=2048", + "--max-old-space-size=4096", "test/ufuzz", ]; var iterations; |