aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2021-01-19 23:27:32 +0000
committerGitHub <noreply@github.com>2021-01-20 07:27:32 +0800
commitd37ee4d41c70aa309550e7337f75126ef2932c62 (patch)
treef92d069caa23a7bb6824014a6139b68c8596811c
parent7793c6c389930dd10fdce5a12b24c95d5bb93933 (diff)
downloadtracifyjs-d37ee4d41c70aa309550e7337f75126ef2932c62.tar.gz
tracifyjs-d37ee4d41c70aa309550e7337f75126ef2932c62.zip
support asynchronous test cases properly (#4529)
-rw-r--r--.github/workflows/ufuzz.yml2
-rw-r--r--lib/compress.js9
-rw-r--r--test/compress.js11
-rw-r--r--test/compress/awaits.js30
-rw-r--r--test/compress/sandbox.js49
-rw-r--r--test/mocha/cli.js6
-rw-r--r--test/reduce.js24
-rw-r--r--test/sandbox.js281
-rw-r--r--test/ufuzz/index.js67
-rw-r--r--test/ufuzz/job.js2
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;