226 lines
5.7 KiB
JavaScript
226 lines
5.7 KiB
JavaScript
// 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);
|
|
}
|