implement test/sandbox.js (#1749)
- `test/run-tests.js` and `test/ufuzz.js` now shares the same `run_code()` and `same_stdout()` - re-enable fuzzer to generate top-level `NaN`, `Infinity` & `undefined` - attempt to show beautified output only when `run_code()` output is preserved
This commit is contained in:
@@ -4,22 +4,11 @@ var U = require("../tools/node");
|
|||||||
var path = require("path");
|
var path = require("path");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var assert = require("assert");
|
var assert = require("assert");
|
||||||
var vm = require("vm");
|
var sandbox = require("./sandbox");
|
||||||
|
|
||||||
var tests_dir = path.dirname(module.filename);
|
var tests_dir = path.dirname(module.filename);
|
||||||
var failures = 0;
|
var failures = 0;
|
||||||
var failed_files = {};
|
var failed_files = {};
|
||||||
var same_stdout = ~process.version.lastIndexOf("v0.12.", 0) ? function(expected, actual) {
|
|
||||||
if (typeof expected != typeof actual) return false;
|
|
||||||
if (typeof expected != "string") {
|
|
||||||
if (expected.name != actual.name) return false;
|
|
||||||
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
|
|
||||||
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
|
|
||||||
}
|
|
||||||
return expected == actual;
|
|
||||||
} : function(expected, actual) {
|
|
||||||
return typeof expected == typeof actual && expected.toString() == actual.toString();
|
|
||||||
};
|
|
||||||
|
|
||||||
run_compress_tests();
|
run_compress_tests();
|
||||||
if (failures) {
|
if (failures) {
|
||||||
@@ -182,11 +171,11 @@ function run_compress_tests() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (test.expect_stdout) {
|
if (test.expect_stdout) {
|
||||||
var stdout = run_code(input_code);
|
var stdout = sandbox.run_code(input_code);
|
||||||
if (test.expect_stdout === true) {
|
if (test.expect_stdout === true) {
|
||||||
test.expect_stdout = stdout;
|
test.expect_stdout = stdout;
|
||||||
}
|
}
|
||||||
if (!same_stdout(test.expect_stdout, stdout)) {
|
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
|
||||||
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
||||||
input: input_formatted,
|
input: input_formatted,
|
||||||
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
|
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
|
||||||
@@ -197,8 +186,8 @@ function run_compress_tests() {
|
|||||||
failures++;
|
failures++;
|
||||||
failed_files[file] = 1;
|
failed_files[file] = 1;
|
||||||
} else {
|
} else {
|
||||||
stdout = run_code(output);
|
stdout = sandbox.run_code(output);
|
||||||
if (!same_stdout(test.expect_stdout, stdout)) {
|
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
|
||||||
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
||||||
input: input_formatted,
|
input: input_formatted,
|
||||||
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
|
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
|
||||||
@@ -330,19 +319,3 @@ function evaluate(code) {
|
|||||||
code = make_code(code, { beautify: true });
|
code = make_code(code, { beautify: true });
|
||||||
return new Function("return(" + code + ")")();
|
return new Function("return(" + code + ")")();
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
50
test/sandbox.js
Normal file
50
test/sandbox.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
var vm = require("vm");
|
||||||
|
|
||||||
|
var FUNC_TOSTRING = [
|
||||||
|
"Function.prototype.toString = Function.prototype.valueOf = function() {",
|
||||||
|
" var ids = [];",
|
||||||
|
" return function() {",
|
||||||
|
" var i = ids.indexOf(this);",
|
||||||
|
" if (i < 0) {",
|
||||||
|
" i = ids.length;",
|
||||||
|
" ids.push(this);",
|
||||||
|
" }",
|
||||||
|
' return "[Function: __func_" + i + "__]";',
|
||||||
|
" }",
|
||||||
|
"}();",
|
||||||
|
""
|
||||||
|
].join("\n");
|
||||||
|
exports.run_code = function(code) {
|
||||||
|
var stdout = "";
|
||||||
|
var original_write = process.stdout.write;
|
||||||
|
process.stdout.write = function(chunk) {
|
||||||
|
stdout += chunk;
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
new vm.Script(FUNC_TOSTRING + code).runInNewContext({
|
||||||
|
console: {
|
||||||
|
log: function() {
|
||||||
|
return console.log.apply(console, [].map.call(arguments, function(arg) {
|
||||||
|
return typeof arg == "function" ? arg.toString() : arg;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { timeout: 30000 });
|
||||||
|
return stdout;
|
||||||
|
} catch (ex) {
|
||||||
|
return ex;
|
||||||
|
} finally {
|
||||||
|
process.stdout.write = original_write;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.same_stdout = ~process.version.lastIndexOf("v0.12.", 0) ? function(expected, actual) {
|
||||||
|
if (typeof expected != typeof actual) return false;
|
||||||
|
if (typeof expected != "string") {
|
||||||
|
if (expected.name != actual.name) return false;
|
||||||
|
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
|
||||||
|
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
|
||||||
|
}
|
||||||
|
return expected == actual;
|
||||||
|
} : function(expected, actual) {
|
||||||
|
return typeof expected == typeof actual && expected.toString() == actual.toString();
|
||||||
|
};
|
||||||
634
test/ufuzz.js
634
test/ufuzz.js
@@ -12,8 +12,8 @@
|
|||||||
stream._handle.setBlocking(true);
|
stream._handle.setBlocking(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
var vm = require("vm");
|
|
||||||
var minify = require("..").minify;
|
var minify = require("..").minify;
|
||||||
|
var sandbox = require("./sandbox");
|
||||||
|
|
||||||
var MAX_GENERATED_TOPLEVELS_PER_RUN = 3;
|
var MAX_GENERATED_TOPLEVELS_PER_RUN = 3;
|
||||||
var MAX_GENERATION_RECURSION_DEPTH = 12;
|
var MAX_GENERATION_RECURSION_DEPTH = 12;
|
||||||
@@ -70,69 +70,65 @@ var STMT_COUNT_FROM_GLOBAL = true; // count statement depth from nearest functio
|
|||||||
var num_iterations = +process.argv[2] || 1/0;
|
var num_iterations = +process.argv[2] || 1/0;
|
||||||
var verbose = false; // log every generated test
|
var verbose = false; // log every generated test
|
||||||
var verbose_interval = false; // log every 100 generated tests
|
var verbose_interval = false; // log every 100 generated tests
|
||||||
var enable_beautifier = false; // run beautifier as well?
|
|
||||||
for (var i = 2; i < process.argv.length; ++i) {
|
for (var i = 2; i < process.argv.length; ++i) {
|
||||||
switch (process.argv[i]) {
|
switch (process.argv[i]) {
|
||||||
case '-v':
|
case '-v':
|
||||||
verbose = true;
|
verbose = true;
|
||||||
break;
|
break;
|
||||||
case '-V':
|
case '-V':
|
||||||
verbose_interval = true;
|
verbose_interval = true;
|
||||||
break;
|
break;
|
||||||
case '-b':
|
case '-t':
|
||||||
enable_beautifier = true;
|
MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
|
||||||
break;
|
if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run');
|
||||||
case '-t':
|
break;
|
||||||
MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
|
case '-r':
|
||||||
if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run');
|
MAX_GENERATION_RECURSION_DEPTH = +process.argv[++i];
|
||||||
break;
|
if (!MAX_GENERATION_RECURSION_DEPTH) throw new Error('Recursion depth must be at least 1');
|
||||||
case '-r':
|
break;
|
||||||
MAX_GENERATION_RECURSION_DEPTH = +process.argv[++i];
|
case '-s1':
|
||||||
if (!MAX_GENERATION_RECURSION_DEPTH) throw new Error('Recursion depth must be at least 1');
|
var name = process.argv[++i];
|
||||||
break;
|
STMT_FIRST_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
|
||||||
case '-s1':
|
if (!(STMT_FIRST_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
|
||||||
var name = process.argv[++i];
|
break;
|
||||||
STMT_FIRST_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
|
case '-s2':
|
||||||
if (!(STMT_FIRST_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
|
var name = process.argv[++i];
|
||||||
break;
|
STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
|
||||||
case '-s2':
|
if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
|
||||||
var name = process.argv[++i];
|
break;
|
||||||
STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
|
case '--stmt-depth-from-func':
|
||||||
if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
|
STMT_COUNT_FROM_GLOBAL = false;
|
||||||
break;
|
break;
|
||||||
case '--stmt-depth-from-func':
|
case '--only-stmt':
|
||||||
STMT_COUNT_FROM_GLOBAL = false;
|
STMTS_TO_USE = process.argv[++i].split(',').map(function(name){ return STMT_ARG_TO_ID[name]; });
|
||||||
break;
|
break;
|
||||||
case '--only-stmt':
|
case '--without-stmt':
|
||||||
STMTS_TO_USE = process.argv[++i].split(',').map(function(name){ return STMT_ARG_TO_ID[name]; });
|
// meh. it runs once it's fine.
|
||||||
break;
|
process.argv[++i].split(',').forEach(function(name){
|
||||||
case '--without-stmt':
|
var omit = STMT_ARG_TO_ID[name];
|
||||||
// meh. it runs once it's fine.
|
STMTS_TO_USE = STMTS_TO_USE.filter(function(id){ return id !== omit; })
|
||||||
process.argv[++i].split(',').forEach(function(name){
|
});
|
||||||
var omit = STMT_ARG_TO_ID[name];
|
break;
|
||||||
STMTS_TO_USE = STMTS_TO_USE.filter(function(id){ return id !== omit; })
|
case '-?':
|
||||||
});
|
console.log('** UglifyJS fuzzer help **');
|
||||||
break;
|
console.log('Valid options (optional):');
|
||||||
case '-?':
|
console.log('<number>: generate this many cases (if used must be first arg)');
|
||||||
console.log('** UglifyJS fuzzer help **');
|
console.log('-v: print every generated test case');
|
||||||
console.log('Valid options (optional):');
|
console.log('-V: print every 100th generated test case');
|
||||||
console.log('<number>: generate this many cases (if used must be first arg)');
|
console.log('-b: also run beautifier');
|
||||||
console.log('-v: print every generated test case');
|
console.log('-t <int>: generate this many toplevels per run (more take longer)');
|
||||||
console.log('-V: print every 100th generated test case');
|
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
|
||||||
console.log('-b: also run beautifier');
|
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
|
||||||
console.log('-t <int>: generate this many toplevels per run (more take longer)');
|
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
|
||||||
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
|
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
|
||||||
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
|
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
|
||||||
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
|
console.log('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
|
||||||
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
|
console.log('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
|
||||||
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
|
console.log('** UglifyJS fuzzer exiting **');
|
||||||
console.log('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
|
return 0;
|
||||||
console.log('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
|
default:
|
||||||
console.log('** UglifyJS fuzzer exiting **');
|
// first arg may be a number.
|
||||||
return 0;
|
if (i > 2 || !parseInt(process.argv[i], 10)) throw new Error('Unknown argument[' + process.argv[i] + ']; see -? for help');
|
||||||
default:
|
|
||||||
// first arg may be a number.
|
|
||||||
if (i > 2 || !parseInt(process.argv[i], 10)) throw new Error('Unknown argument[' + process.argv[i] + ']; see -? for help');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,47 +267,9 @@ var TYPEOF_OUTCOMES = [
|
|||||||
'symbol',
|
'symbol',
|
||||||
'crap' ];
|
'crap' ];
|
||||||
|
|
||||||
var FUNC_TOSTRING = [
|
|
||||||
"Function.prototype.toString = Function.prototype.valueOf = function() {",
|
|
||||||
" var ids = [];",
|
|
||||||
" return function() {",
|
|
||||||
" var i = ids.indexOf(this);",
|
|
||||||
" if (i < 0) {",
|
|
||||||
" i = ids.length;",
|
|
||||||
" ids.push(this);",
|
|
||||||
" }",
|
|
||||||
' return "[Function: __func_" + i + "__]";',
|
|
||||||
" }",
|
|
||||||
"}();",
|
|
||||||
""
|
|
||||||
].join("\n");
|
|
||||||
var loops = 0;
|
var loops = 0;
|
||||||
var funcs = 0;
|
var funcs = 0;
|
||||||
|
|
||||||
function run_code(code) {
|
|
||||||
var stdout = "";
|
|
||||||
var original_write = process.stdout.write;
|
|
||||||
process.stdout.write = function(chunk) {
|
|
||||||
stdout += chunk;
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
new vm.Script(FUNC_TOSTRING + code).runInNewContext({
|
|
||||||
console: {
|
|
||||||
log: function() {
|
|
||||||
return console.log.apply(console, [].map.call(arguments, function(arg) {
|
|
||||||
return typeof arg == "function" ? arg.toString() : arg;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, { timeout: 30000 });
|
|
||||||
return stdout;
|
|
||||||
} catch (ex) {
|
|
||||||
return ex;
|
|
||||||
} finally {
|
|
||||||
process.stdout.write = original_write;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function rng(max) {
|
function rng(max) {
|
||||||
return Math.floor(max * Math.random());
|
return Math.floor(max * Math.random());
|
||||||
}
|
}
|
||||||
@@ -327,7 +285,7 @@ function createTopLevelCodes(n) {
|
|||||||
function createTopLevelCode() {
|
function createTopLevelCode() {
|
||||||
var r = rng(3);
|
var r = rng(3);
|
||||||
if (r > 0) return createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, IN_GLOBAL, ANY_TYPE, CANNOT_THROW, 0);
|
if (r > 0) return createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, IN_GLOBAL, ANY_TYPE, CANNOT_THROW, 0);
|
||||||
return createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0, IN_GLOBAL);
|
return createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFunctions(n, recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
function createFunctions(n, recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
||||||
@@ -344,15 +302,15 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
|||||||
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
||||||
var func = funcs++;
|
var func = funcs++;
|
||||||
var namesLenBefore = VAR_NAMES.length;
|
var namesLenBefore = VAR_NAMES.length;
|
||||||
var name = (inGlobal || rng(5) > 0) ? 'f' + func : createVarName(MANDATORY, inGlobal, noDecl);
|
var name = (inGlobal || rng(5) > 0) ? 'f' + func : createVarName(MANDATORY, noDecl);
|
||||||
if (name === 'a' || name === 'b' || name === 'c') name = 'f' + func; // quick hack to prevent assignment to func names of being called
|
if (name === 'a' || name === 'b' || name === 'c') name = 'f' + func; // quick hack to prevent assignment to func names of being called
|
||||||
var s = '';
|
var s = '';
|
||||||
if (rng(5) === 1) {
|
if (rng(5) === 1) {
|
||||||
// functions with functions. lower the recursion to prevent a mess.
|
// functions with functions. lower the recursion to prevent a mess.
|
||||||
s = 'function ' + name + '(' + createVarName(MANDATORY, inGlobal) + '){' + createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth) + '}\n';
|
s = 'function ' + name + '(' + createVarName(MANDATORY) + '){' + createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth) + '}\n';
|
||||||
} else {
|
} else {
|
||||||
// functions with statements
|
// functions with statements
|
||||||
s = 'function ' + name + '(' + createVarName(MANDATORY, inGlobal) + '){' + createStatements(3, recurmax, canThrow, CANNOT_THROW, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, inGlobal, NOT_GLOBAL) + '}\n';
|
s = 'function ' + name + '(' + createVarName(MANDATORY) + '){' + createStatements(3, recurmax, canThrow, CANNOT_THROW, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '}\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
VAR_NAMES.length = namesLenBefore;
|
VAR_NAMES.length = namesLenBefore;
|
||||||
@@ -365,16 +323,16 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) {
|
function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
||||||
if (--recurmax < 0) { return ';'; }
|
if (--recurmax < 0) { return ';'; }
|
||||||
var s = '';
|
var s = '';
|
||||||
while (--n > 0) {
|
while (--n > 0) {
|
||||||
s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) + '\n';
|
s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '\n';
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) {
|
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
||||||
++stmtDepth;
|
++stmtDepth;
|
||||||
var loop = ++loops;
|
var loop = ++loops;
|
||||||
if (--recurmax < 0) {
|
if (--recurmax < 0) {
|
||||||
@@ -388,90 +346,90 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
|
else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
|
||||||
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case STMT_BLOCK:
|
case STMT_BLOCK:
|
||||||
return '{' + createStatements(rng(5) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) + '}';
|
return '{' + createStatements(rng(5) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '}';
|
||||||
case STMT_IF_ELSE:
|
case STMT_IF_ELSE:
|
||||||
return 'if (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) + (rng(2) === 1 ? ' else ' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) : '');
|
return 'if (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + (rng(2) === 1 ? ' else ' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) : '');
|
||||||
case STMT_DO_WHILE:
|
case STMT_DO_WHILE:
|
||||||
return '{var brake' + loop + ' = 5; do {' + createStatement(recurmax, canThrow, CAN_BREAK, CAN_CONTINUE, cannotReturn, stmtDepth, inGlobal) + '} while ((' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && --brake' + loop + ' > 0);}';
|
return '{var brake' + loop + ' = 5; do {' + createStatement(recurmax, canThrow, CAN_BREAK, CAN_CONTINUE, cannotReturn, stmtDepth) + '} while ((' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && --brake' + loop + ' > 0);}';
|
||||||
case STMT_WHILE:
|
case STMT_WHILE:
|
||||||
return '{var brake' + loop + ' = 5; while ((' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && --brake' + loop + ' > 0)' + createStatement(recurmax, canThrow, CAN_BREAK, CAN_CONTINUE, cannotReturn, stmtDepth, inGlobal) + '}';
|
return '{var brake' + loop + ' = 5; while ((' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && --brake' + loop + ' > 0)' + createStatement(recurmax, canThrow, CAN_BREAK, CAN_CONTINUE, cannotReturn, stmtDepth) + '}';
|
||||||
case STMT_FOR_LOOP:
|
case STMT_FOR_LOOP:
|
||||||
return 'for (var brake' + loop + ' = 5; (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && brake' + loop + ' > 0; --brake' + loop + ')' + createStatement(recurmax, canThrow, CAN_BREAK, CAN_CONTINUE, cannotReturn, stmtDepth, inGlobal);
|
return 'for (var brake' + loop + ' = 5; (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && brake' + loop + ' > 0; --brake' + loop + ')' + createStatement(recurmax, canThrow, CAN_BREAK, CAN_CONTINUE, cannotReturn, stmtDepth);
|
||||||
case STMT_SEMI:
|
case STMT_SEMI:
|
||||||
return ';';
|
return ';';
|
||||||
case STMT_EXPR:
|
case STMT_EXPR:
|
||||||
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ';';
|
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ';';
|
||||||
case STMT_SWITCH:
|
case STMT_SWITCH:
|
||||||
// note: case args are actual expressions
|
// note: case args are actual expressions
|
||||||
// note: default does not _need_ to be last
|
// note: default does not _need_ to be last
|
||||||
return 'switch (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') { ' + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) + '}';
|
return 'switch (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') { ' + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '}';
|
||||||
case STMT_VAR:
|
case STMT_VAR:
|
||||||
switch (rng(3)) {
|
switch (rng(3)) {
|
||||||
case 0:
|
case 0:
|
||||||
var name = createVarName(MANDATORY, inGlobal);
|
var name = createVarName(MANDATORY);
|
||||||
if (name === 'c') name = 'a';
|
if (name === 'c') name = 'a';
|
||||||
return 'var ' + name + ';';
|
return 'var ' + name + ';';
|
||||||
case 1:
|
case 1:
|
||||||
// initializer can only have one expression
|
// initializer can only have one expression
|
||||||
var name = createVarName(MANDATORY, inGlobal);
|
var name = createVarName(MANDATORY);
|
||||||
if (name === 'c') name = 'b';
|
if (name === 'c') name = 'b';
|
||||||
return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
default:
|
default:
|
||||||
// initializer can only have one expression
|
// initializer can only have one expression
|
||||||
var n1 = createVarName(MANDATORY, inGlobal);
|
var n1 = createVarName(MANDATORY);
|
||||||
if (n1 === 'c') n1 = 'b';
|
if (n1 === 'c') n1 = 'b';
|
||||||
var n2 = createVarName(MANDATORY, inGlobal);
|
var n2 = createVarName(MANDATORY);
|
||||||
if (n2 === 'c') n2 = 'b';
|
if (n2 === 'c') n2 = 'b';
|
||||||
return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
}
|
}
|
||||||
case STMT_RETURN_ETC:
|
case STMT_RETURN_ETC:
|
||||||
switch (rng(3)) {
|
switch (rng(3)) {
|
||||||
case 1:
|
case 1:
|
||||||
if (canBreak && rng(5) === 0) return 'break;';
|
if (canBreak && rng(5) === 0) return 'break;';
|
||||||
if (canContinue && rng(5) === 0) return 'continue;';
|
if (canContinue && rng(5) === 0) return 'continue;';
|
||||||
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
return '/*3*/return;';
|
return '/*3*/return;';
|
||||||
case 2:
|
case 2:
|
||||||
// must wrap in curlies to prevent orphaned `else` statement
|
// must wrap in curlies to prevent orphaned `else` statement
|
||||||
if (canThrow && rng(5) === 0) return '{ throw ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
if (canThrow && rng(5) === 0) return '{ throw ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
||||||
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
return '{ /*1*/ return ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
return '{ /*1*/ return ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
||||||
default:
|
default:
|
||||||
// this is actually more like a parser test, but perhaps it hits some dead code elimination traps
|
// this is actually more like a parser test, but perhaps it hits some dead code elimination traps
|
||||||
// must wrap in curlies to prevent orphaned `else` statement
|
// must wrap in curlies to prevent orphaned `else` statement
|
||||||
// note: you can't `throw` without an expression so don't put a `throw` option in this case
|
// note: you can't `throw` without an expression so don't put a `throw` option in this case
|
||||||
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
|
||||||
return '{ /*2*/ return\n' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
return '{ /*2*/ return\n' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}';
|
||||||
}
|
}
|
||||||
case STMT_FUNC_EXPR:
|
case STMT_FUNC_EXPR:
|
||||||
// "In non-strict mode code, functions can only be declared at top level, inside a block, or ..."
|
// "In non-strict mode code, functions can only be declared at top level, inside a block, or ..."
|
||||||
// (dont both with func decls in `if`; it's only a parser thing because you cant call them without a block)
|
// (dont both with func decls in `if`; it's only a parser thing because you cant call them without a block)
|
||||||
return '{' + createFunction(recurmax, NOT_GLOBAL, NO_DECL, canThrow, stmtDepth) + '}';
|
return '{' + createFunction(recurmax, NOT_GLOBAL, NO_DECL, canThrow, stmtDepth) + '}';
|
||||||
case STMT_TRY:
|
case STMT_TRY:
|
||||||
// catch var could cause some problems
|
// catch var could cause some problems
|
||||||
// note: the "blocks" are syntactically mandatory for try/catch/finally
|
// note: the "blocks" are syntactically mandatory for try/catch/finally
|
||||||
var n = rng(3); // 0=only catch, 1=only finally, 2=catch+finally
|
var n = rng(3); // 0=only catch, 1=only finally, 2=catch+finally
|
||||||
var s = 'try {' + createStatement(recurmax, n === 1 ? CANNOT_THROW : CAN_THROW, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) + ' }';
|
var s = 'try {' + createStatement(recurmax, n === 1 ? CANNOT_THROW : CAN_THROW, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
|
||||||
if (n !== 1) {
|
if (n !== 1) {
|
||||||
// the catch var should only be accessible in the catch clause...
|
// the catch var should only be accessible in the catch clause...
|
||||||
// we have to do go through some trouble here to prevent leaking it
|
// we have to do go through some trouble here to prevent leaking it
|
||||||
var nameLenBefore = VAR_NAMES.length;
|
var nameLenBefore = VAR_NAMES.length;
|
||||||
var catchName = createVarName(MANDATORY);
|
var catchName = createVarName(MANDATORY);
|
||||||
var freshCatchName = VAR_NAMES.length !== nameLenBefore;
|
var freshCatchName = VAR_NAMES.length !== nameLenBefore;
|
||||||
s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) + ' }';
|
s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
|
||||||
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1); // remove catch name
|
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1); // remove catch name
|
||||||
}
|
}
|
||||||
if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) + ' }';
|
if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
|
||||||
return s;
|
return s;
|
||||||
case STMT_C:
|
case STMT_C:
|
||||||
return 'c = c + 1;';
|
return 'c = c + 1;';
|
||||||
default:
|
default:
|
||||||
throw 'no';
|
throw 'no';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, inGlobal) {
|
function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
||||||
var hadDefault = false;
|
var hadDefault = false;
|
||||||
var s = '';
|
var s = '';
|
||||||
while (n-- > 0) {
|
while (n-- > 0) {
|
||||||
@@ -479,7 +437,7 @@ function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotR
|
|||||||
if (hadDefault || rng(5) > 0) {
|
if (hadDefault || rng(5) > 0) {
|
||||||
s += '' +
|
s += '' +
|
||||||
'case ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ':\n' +
|
'case ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ':\n' +
|
||||||
createStatements(rng(3) + 1, recurmax, canThrow, CAN_BREAK, canContinue, cannotReturn, stmtDepth, inGlobal) +
|
createStatements(rng(3) + 1, recurmax, canThrow, CAN_BREAK, canContinue, cannotReturn, stmtDepth) +
|
||||||
'\n' +
|
'\n' +
|
||||||
(rng(10) > 0 ? ' break;' : '/* fall-through */') +
|
(rng(10) > 0 ? ' break;' : '/* fall-through */') +
|
||||||
'\n';
|
'\n';
|
||||||
@@ -487,7 +445,7 @@ function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotR
|
|||||||
hadDefault = true;
|
hadDefault = true;
|
||||||
s += '' +
|
s += '' +
|
||||||
'default:\n' +
|
'default:\n' +
|
||||||
createStatements(rng(3) + 1, recurmax, canThrow, CAN_BREAK, canContinue, cannotReturn, stmtDepth, inGlobal) +
|
createStatements(rng(3) + 1, recurmax, canThrow, CAN_BREAK, canContinue, cannotReturn, stmtDepth) +
|
||||||
'\n';
|
'\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -508,79 +466,79 @@ function createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
}
|
}
|
||||||
function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||||
switch (rng(13)) {
|
switch (rng(13)) {
|
||||||
case 0:
|
case 0:
|
||||||
return createUnaryOp() + (rng(2) === 1 ? 'a' : 'b');
|
return createUnaryOp() + (rng(2) === 1 ? 'a' : 'b');
|
||||||
case 1:
|
case 1:
|
||||||
return 'a' + (rng(2) == 1 ? '++' : '--');
|
return 'a' + (rng(2) == 1 ? '++' : '--');
|
||||||
case 2:
|
case 2:
|
||||||
// parens needed because assignments aren't valid unless they're the left-most op(s) in an expression
|
// parens needed because assignments aren't valid unless they're the left-most op(s) in an expression
|
||||||
return '(b ' + createAssignment() + ' a)';
|
return '(b ' + createAssignment() + ' a)';
|
||||||
case 3:
|
case 3:
|
||||||
return rng(2) + ' === 1 ? a : b';
|
return rng(2) + ' === 1 ? a : b';
|
||||||
case 4:
|
case 4:
|
||||||
return createNestedBinaryExpr(recurmax, noComma) + createBinaryOp(noComma) + createExpression(recurmax, noComma, stmtDepth, canThrow);
|
return createNestedBinaryExpr(recurmax, noComma) + createBinaryOp(noComma) + createExpression(recurmax, noComma, stmtDepth, canThrow);
|
||||||
case 5:
|
case 5:
|
||||||
return createValue();
|
return createValue();
|
||||||
case 6:
|
case 6:
|
||||||
return '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')';
|
return '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')';
|
||||||
case 7:
|
case 7:
|
||||||
return createExpression(recurmax, noComma, stmtDepth, canThrow) + '?' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ':' + createExpression(recurmax, noComma, stmtDepth, canThrow);
|
return createExpression(recurmax, noComma, stmtDepth, canThrow) + '?' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ':' + createExpression(recurmax, noComma, stmtDepth, canThrow);
|
||||||
case 8:
|
case 8:
|
||||||
var nameLenBefore = VAR_NAMES.length;
|
var nameLenBefore = VAR_NAMES.length;
|
||||||
var name = createVarName(MAYBE, NOT_GLOBAL); // note: this name is only accessible from _within_ the function. and immutable at that.
|
var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
|
||||||
if (name === 'c') name = 'a';
|
if (name === 'c') name = 'a';
|
||||||
var s = '';
|
var s = '';
|
||||||
switch(rng(4)) {
|
switch(rng(4)) {
|
||||||
case 0:
|
case 0:
|
||||||
s = '(function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, NOT_GLOBAL) + '})()';
|
s = '(function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '})()';
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
s = '+function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, NOT_GLOBAL) + '}()';
|
s = '+function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '}()';
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
s = '!function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, NOT_GLOBAL) + '}()';
|
s = '!function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '}()';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
s = 'void function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, NOT_GLOBAL) + '}()';
|
s = 'void function ' + name + '(){' + createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '}()';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
VAR_NAMES.length = nameLenBefore;
|
VAR_NAMES.length = nameLenBefore;
|
||||||
return s;
|
return s;
|
||||||
case 9:
|
case 9:
|
||||||
return createTypeofExpr();
|
return createTypeofExpr();
|
||||||
case 10:
|
case 10:
|
||||||
// you could statically infer that this is just `Math`, regardless of the other expression
|
// you could statically infer that this is just `Math`, regardless of the other expression
|
||||||
// I don't think Uglify does this at this time...
|
// I don't think Uglify does this at this time...
|
||||||
return ''+
|
return ''+
|
||||||
'new function(){ \n' +
|
'new function(){ \n' +
|
||||||
(rng(2) === 1 ? createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '\n' : '') +
|
(rng(2) === 1 ? createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '\n' : '') +
|
||||||
'return Math;\n' +
|
'return Math;\n' +
|
||||||
'}';
|
'}';
|
||||||
case 11:
|
case 11:
|
||||||
// more like a parser test but perhaps comment nodes mess up the analysis?
|
// more like a parser test but perhaps comment nodes mess up the analysis?
|
||||||
// note: parens not needed for post-fix (since that's the default when ambiguous)
|
// note: parens not needed for post-fix (since that's the default when ambiguous)
|
||||||
// for prefix ops we need parens to prevent accidental syntax errors.
|
// for prefix ops we need parens to prevent accidental syntax errors.
|
||||||
switch (rng(6)) {
|
switch (rng(6)) {
|
||||||
case 0:
|
case 0:
|
||||||
return 'a/* ignore */++';
|
return 'a/* ignore */++';
|
||||||
case 1:
|
case 1:
|
||||||
return 'b/* ignore */--';
|
return 'b/* ignore */--';
|
||||||
case 2:
|
case 2:
|
||||||
return '(++/* ignore */a)';
|
return '(++/* ignore */a)';
|
||||||
case 3:
|
case 3:
|
||||||
return '(--/* ignore */b)';
|
return '(--/* ignore */b)';
|
||||||
case 4:
|
case 4:
|
||||||
// only groups that wrap a single variable return a "Reference", so this is still valid.
|
// only groups that wrap a single variable return a "Reference", so this is still valid.
|
||||||
// may just be a parser edge case that is invisible to uglify...
|
// may just be a parser edge case that is invisible to uglify...
|
||||||
return '(--(b))';
|
return '(--(b))';
|
||||||
case 5:
|
case 5:
|
||||||
// classic 0.3-0.1 case; 1-0.1-0.1-0.1 is not 0.7 :)
|
// classic 0.3-0.1 case; 1-0.1-0.1-0.1 is not 0.7 :)
|
||||||
return 'b + 1-0.1-0.1-0.1';
|
return 'b + 1-0.1-0.1-0.1';
|
||||||
default:
|
default:
|
||||||
return '(--/* ignore */b)';
|
return '(--/* ignore */b)';
|
||||||
}
|
}
|
||||||
case 12:
|
case 12:
|
||||||
return createNestedBinaryExpr(recurmax, noComma);
|
return createNestedBinaryExpr(recurmax, noComma);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,16 +562,16 @@ function _createSimpleBinaryExpr(recurmax, noComma) {
|
|||||||
|
|
||||||
function createTypeofExpr() {
|
function createTypeofExpr() {
|
||||||
switch (rng(5)) {
|
switch (rng(5)) {
|
||||||
case 0:
|
case 0:
|
||||||
return 'typeof ' + createVarName(MANDATORY, NOT_GLOBAL, DONT_STORE) + ' === "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
return 'typeof ' + createVarName(MANDATORY, DONT_STORE) + ' === "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
||||||
case 1:
|
case 1:
|
||||||
return 'typeof ' + createVarName(MANDATORY, NOT_GLOBAL, DONT_STORE) + ' !== "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
return 'typeof ' + createVarName(MANDATORY, DONT_STORE) + ' !== "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
||||||
case 2:
|
case 2:
|
||||||
return 'typeof ' + createVarName(MANDATORY, NOT_GLOBAL, DONT_STORE) + ' == "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
return 'typeof ' + createVarName(MANDATORY, DONT_STORE) + ' == "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
||||||
case 3:
|
case 3:
|
||||||
return 'typeof ' + createVarName(MANDATORY, NOT_GLOBAL, DONT_STORE) + ' != "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
return 'typeof ' + createVarName(MANDATORY, DONT_STORE) + ' != "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '"';
|
||||||
case 4:
|
case 4:
|
||||||
return 'typeof ' + createVarName(MANDATORY, NOT_GLOBAL, DONT_STORE);
|
return 'typeof ' + createVarName(MANDATORY, DONT_STORE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,55 +592,67 @@ function createUnaryOp() {
|
|||||||
return UNARY_OPS[rng(UNARY_OPS.length)];
|
return UNARY_OPS[rng(UNARY_OPS.length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVarName(maybe, inGlobal, dontStore) {
|
function createVarName(maybe, dontStore) {
|
||||||
if (!maybe || rng(2) === 1) {
|
if (!maybe || rng(2) === 1) {
|
||||||
do {
|
var r = rng(VAR_NAMES.length);
|
||||||
var r = rng(VAR_NAMES.length);
|
var suffixed = rng(5) > 0;
|
||||||
var suffixed = rng(5) > 0;
|
var name = VAR_NAMES[r] + (suffixed ? '_' + (++loops) : '');
|
||||||
var name = VAR_NAMES[r] + (suffixed ? '_' + (++loops) : '');
|
|
||||||
} while (inGlobal && (name === 'NaN' || name === 'undefined' || name === 'Infinity')); // prevent nodejs module strict mode problems
|
|
||||||
if (!dontStore && suffixed) VAR_NAMES.push(name);
|
if (!dontStore && suffixed) VAR_NAMES.push(name);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function log(ok) {
|
function try_beautify(code, result) {
|
||||||
|
try {
|
||||||
|
var beautified = minify(code, {
|
||||||
|
fromString: true,
|
||||||
|
compress: false,
|
||||||
|
mangle: false,
|
||||||
|
output: {
|
||||||
|
beautify: true,
|
||||||
|
bracketize: true,
|
||||||
|
},
|
||||||
|
}).code;
|
||||||
|
if (sandbox.same_stdout(sandbox.run_code(beautified), result)) {
|
||||||
|
console.log("// (beautified)");
|
||||||
|
console.log(beautified);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log("// !!! beautify failed !!!");
|
||||||
|
console.log(e.stack);
|
||||||
|
}
|
||||||
|
console.log("//");
|
||||||
|
console.log(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
function log() {
|
||||||
if (!ok) console.log('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
|
if (!ok) console.log('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
|
||||||
console.log("//=============================================================");
|
console.log("//=============================================================");
|
||||||
if (!ok) console.log("// !!!!!! Failed... round", round);
|
if (!ok) console.log("// !!!!!! Failed... round", round);
|
||||||
console.log("// original code");
|
console.log("// original code");
|
||||||
console.log("//");
|
try_beautify(original_code, original_result);
|
||||||
console.log(original_code);
|
|
||||||
console.log();
|
console.log();
|
||||||
console.log();
|
console.log();
|
||||||
if (enable_beautifier) {
|
|
||||||
console.log("//-------------------------------------------------------------");
|
|
||||||
console.log("// original code (beautify'd)");
|
|
||||||
console.log("//");
|
|
||||||
console.log(beautify_code);
|
|
||||||
console.log();
|
|
||||||
console.log();
|
|
||||||
}
|
|
||||||
console.log("//-------------------------------------------------------------");
|
console.log("//-------------------------------------------------------------");
|
||||||
console.log("// uglified code");
|
if (typeof uglify_code == "string") {
|
||||||
console.log("//");
|
console.log("// uglified code");
|
||||||
console.log(uglify_code);
|
try_beautify(uglify_code, uglify_result);
|
||||||
console.log();
|
console.log();
|
||||||
console.log();
|
console.log();
|
||||||
console.log("original result:");
|
console.log("original result:");
|
||||||
console.log(original_result);
|
console.log(original_result);
|
||||||
if (enable_beautifier) {
|
console.log("uglified result:");
|
||||||
console.log("beautified result:");
|
console.log(uglify_result);
|
||||||
console.log(beautify_result);
|
} else {
|
||||||
|
console.log("// !!! uglify failed !!!");
|
||||||
|
console.log(uglify_code.stack);
|
||||||
}
|
}
|
||||||
console.log("uglified result:");
|
|
||||||
console.log(uglify_result);
|
|
||||||
if (!ok) console.log("!!!!!! Failed... round", round);
|
if (!ok) console.log("!!!!!! Failed... round", round);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var round = 0; round < num_iterations; round++) {
|
for (var round = 0; round < num_iterations; round++) {
|
||||||
var parse_error = false;
|
|
||||||
process.stdout.write(round + " of " + num_iterations + "\r");
|
process.stdout.write(round + " of " + num_iterations + "\r");
|
||||||
|
|
||||||
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
|
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
|
||||||
@@ -691,51 +661,25 @@ for (var round = 0; round < num_iterations; round++) {
|
|||||||
|
|
||||||
var original_code = [
|
var original_code = [
|
||||||
"var a = 100, b = 10, c = 0;",
|
"var a = 100, b = 10, c = 0;",
|
||||||
createTopLevelCodes(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1) +
|
createTopLevelCodes(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1),
|
||||||
"console.log(null, a, b, c);" // the array makes for a cleaner output (empty string still shows up etc)
|
"console.log(null, a, b, c);" // preceding `null` makes for a cleaner output (empty string still shows up etc)
|
||||||
].join("\n");
|
].join("\n");
|
||||||
var original_result = run_code(original_code);
|
|
||||||
|
|
||||||
if (enable_beautifier) {
|
|
||||||
try {
|
|
||||||
var beautify_code = minify(original_code, {
|
|
||||||
fromString: true,
|
|
||||||
mangle: false,
|
|
||||||
compress: false,
|
|
||||||
output: {
|
|
||||||
beautify: true,
|
|
||||||
bracketize: true,
|
|
||||||
},
|
|
||||||
}).code;
|
|
||||||
} catch(e) {
|
|
||||||
parse_error = 1;
|
|
||||||
}
|
|
||||||
var beautify_result = run_code(beautify_code);
|
|
||||||
} else {
|
|
||||||
var beautify_result = original_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var uglify_code;
|
||||||
try {
|
try {
|
||||||
var uglify_code = minify(original_code, {
|
uglify_code = minify(original_code, {
|
||||||
fromString: true,
|
fromString: true,
|
||||||
mangle: true,
|
}).code;
|
||||||
compress: {
|
} catch (e) {
|
||||||
passes: 3,
|
uglify_code = e;
|
||||||
},
|
|
||||||
output: {
|
|
||||||
//beautify: true,
|
|
||||||
//bracketize: true,
|
|
||||||
},
|
|
||||||
}).code;
|
|
||||||
} catch(e) {
|
|
||||||
parse_error = 2;
|
|
||||||
}
|
}
|
||||||
var uglify_result = run_code(uglify_code);
|
|
||||||
|
|
||||||
var ok = !parse_error && original_result == beautify_result && original_result == uglify_result;
|
var ok = typeof uglify_code == "string";
|
||||||
//if (!ok && typeof original_result === 'string' && original_result.indexOf('[Function:') >= 0) ok = true;
|
if (ok) {
|
||||||
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(ok);
|
var original_result = sandbox.run_code(original_code);
|
||||||
if (parse_error === 1) console.log('Parse error while beautifying');
|
var uglify_result = sandbox.run_code(uglify_code);
|
||||||
if (parse_error === 2) console.log('Parse error while uglifying');
|
ok = sandbox.same_stdout(original_result, uglify_result);
|
||||||
if (!ok) break;
|
}
|
||||||
|
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log();
|
||||||
|
if (!ok) process.exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user