From: Alex Lam S.L Date: Fri, 24 Mar 2017 17:46:12 +0000 (+0800) Subject: introduce ufuzz.js (#1655) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=b1abe92e1aface2ec3d1c3666f8674e120f3b487;p=UglifyJS.git introduce ufuzz.js (#1655) closes #1647 --- diff --git a/test/ufuzz.js b/test/ufuzz.js new file mode 100644 index 00000000..ac2ded7c --- /dev/null +++ b/test/ufuzz.js @@ -0,0 +1,225 @@ +// ufuzz.js +// derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee +"use strict"; + +// workaround for tty output truncation upon process.exit() +[process.stdout, process.stderr].forEach(function(stream){ + if (stream._handle && stream._handle.setBlocking) + stream._handle.setBlocking(true); +}); + +var vm = require("vm"); +var minify = require("..").minify; + +function run_code(code) { + var stdout = ""; + var original_write = process.stdout.write; + process.stdout.write = function(chunk) { + stdout += chunk; + }; + try { + new vm.Script(code).runInNewContext({ console: console }, { timeout: 5000 }); + return stdout; + } catch (ex) { + return ex; + } finally { + process.stdout.write = original_write; + } +} + +function rng(max) { + return Math.floor(max * Math.random()); +} + +function createFunctionDecls(n, recurmax) { + if (--recurmax < 0) { return ';'; } + var s = ''; + while (--n > 0) { + s += createFunctionDecl(recurmax) + '\n'; + } + return s; +} + +var funcs = 0; +function createFunctionDecl(recurmax) { + if (--recurmax < 0) { return ';'; } + var func = funcs++; + return 'function f' + func + '(){' + createStatements(3, recurmax) + '}\nf' + func + '();'; +} + +function createStatements(n, recurmax) { + if (--recurmax < 0) { return ';'; } + var s = ''; + while (--n > 0) { + s += createStatement(recurmax); + } + return s; +} + +var loops = 0; +function createStatement(recurmax) { + var loop = ++loops; + if (--recurmax < 0) { return ';'; } + switch (rng(7)) { + case 0: + return '{' + createStatement(recurmax) + '}'; + case 1: + return 'if (' + createExpression(recurmax) + ')' + createStatement(recurmax); + case 2: + return '{var brake' + loop + ' = 5; do {' + createStatement(recurmax) + '} while ((' + createExpression(recurmax) + ') && --brake' + loop + ' > 0);}'; + case 3: + return '{var brake' + loop + ' = 5; while ((' + createExpression(recurmax) + ') && --brake' + loop + ' > 0)' + createStatement(recurmax) + '}'; + case 4: + return 'for (var brake' + loop + ' = 5; (' + createExpression(recurmax) + ') && brake' + loop + ' > 0; --brake' + loop + ')' + createStatement(recurmax); + case 5: + return ';'; + case 6: + return createExpression() + ';'; + } +} + +function createExpression(recurmax) { + if (--recurmax < 0) { return '0'; } + switch (rng(8)) { + case 0: + return '(' + createUnaryOp() + 'a)'; + case 1: + return '(a' + (Math.random() > 0.5 ? '++' : '--') + ')'; + case 2: + return '(b ' + createAssignment() + ' a)'; + case 3: + return '(' + Math.random() + ' > 0.5 ? a : b)'; + case 4: + return createExpression(recurmax) + createBinaryOp() + createExpression(recurmax); + case 5: + return createValue(); + case 6: + return '(' + createExpression(recurmax) + ')'; + case 7: + return createExpression(recurmax) + '?(' + createExpression(recurmax) + '):(' + createExpression(recurmax) + ')'; + } +} + +function createValue() { + var values = [ + 'true', + 'false', + '22', + '0', + '(-1)', + 'NaN', + 'undefined', + 'null', + '"foo"', + '"bar"' ]; + return values[rng(values.length)]; +} + +function createBinaryOp() { + switch (rng(6)) { + case 0: + return '+'; + case 1: + return '-'; + case 2: + return ','; + case 3: + return '&&'; + case 4: + return '||'; + case 5: + return '^'; + } +} + +function createAssignment() { + switch (rng(4)) { + case 0: + return '='; + case 1: + return '-='; + case 2: + return '^='; + case 3: + return '+='; + } +} + +function createUnaryOp() { + switch (rng(4)) { + case 0: + return '--'; + case 1: + return '++'; + case 2: + return '~'; + case 3: + return '!'; + } +} + +function log() { + console.log("//============================================================="); + console.log("// original code"); + console.log("//"); + console.log(original_code); + console.log(); + console.log(); + console.log("//-------------------------------------------------------------"); + console.log("// original code (beautify'd)"); + console.log("//"); + console.log(beautify_code); + console.log(); + console.log(); + console.log("//-------------------------------------------------------------"); + console.log("// uglified code"); + console.log("//"); + console.log(uglify_code); + console.log(); + console.log(); + console.log("original result:"); + console.log(original_result); + console.log("beautified result:"); + console.log(beautify_result); + console.log("uglified result:"); + console.log(uglify_result); +} + +var num_iterations = +process.argv[2] || 1/0; +var verbose = !!process.argv[3]; +for (var round = 0; round < num_iterations; round++) { + process.stdout.write(round + " of " + num_iterations + "\r"); + var original_code = [ + "var a = 100, b = 10;", + createFunctionDecls(rng(3) + 1, 10), + "console.log(a, b);" + ].join("\n"); + var beautify_code = minify(original_code, { + fromString: true, + mangle: false, + compress: false, + output: { + beautify: true, + bracketize: true, + }, + }).code; + + var uglify_code = minify(beautify_code, { + fromString: true, + mangle: false, + compress: { + passes: 3, + }, + output: { + beautify: true, + bracketize: true, + }, + }).code; + + var original_result = run_code(original_code); + var beautify_result = run_code(beautify_code); + var uglify_result = run_code(uglify_code); + var ok = original_result == beautify_result && original_result == uglify_result; + if (verbose || !ok) log(); + if (!ok) process.exit(1); +}