From: Alex Lam S.L Date: Sun, 27 Oct 2019 06:17:35 +0000 (+0800) Subject: improve ufuzz resilience (#3533) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=ebbf3d4a515359ef5d0e653c0744a4a040116b56;p=UglifyJS.git improve ufuzz resilience (#3533) --- diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index 8f86e49c..c71dbb39 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -48,76 +48,80 @@ var catch_redef = require.main === module; var generate_directive = require.main === module; for (var i = 2; i < process.argv.length; ++i) { switch (process.argv[i]) { - case '-v': + case "-v": verbose = true; break; - case '-V': + case "-V": verbose_interval = true; break; - case '-t': + case "-t": MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i]; - if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run'); + if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error("Must generate at least one toplevel per run"); break; - case '-r': + case "-r": MAX_GENERATION_RECURSION_DEPTH = +process.argv[++i]; - if (!MAX_GENERATION_RECURSION_DEPTH) throw new Error('Recursion depth must be at least 1'); + if (!MAX_GENERATION_RECURSION_DEPTH) throw new Error("Recursion depth must be at least 1"); break; - case '-s1': + case "-s1": var name = process.argv[++i]; STMT_FIRST_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name]; - if (!(STMT_FIRST_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list'); + if (!(STMT_FIRST_LEVEL_OVERRIDE >= 0)) throw new Error("Unknown statement name; use -? to get a list"); break; - case '-s2': + case "-s2": var name = process.argv[++i]; STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name]; - if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list'); + if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error("Unknown statement name; use -? to get a list"); break; - case '--no-catch-redef': + case "--no-catch-redef": catch_redef = false; break; - case '--no-directive': + case "--no-directive": generate_directive = false; break; - case '--use-strict': + case "--use-strict": use_strict = true; break; - case '--stmt-depth-from-func': + case "--stmt-depth-from-func": STMT_COUNT_FROM_GLOBAL = false; break; - case '--only-stmt': - STMTS_TO_USE = process.argv[++i].split(',').map(function(name){ return STMT_ARG_TO_ID[name]; }); + case "--only-stmt": + STMTS_TO_USE = process.argv[++i].split(",").map(function(name) { + return STMT_ARG_TO_ID[name]; + }); break; - case '--without-stmt': + case "--without-stmt": // meh. it runs once it's fine. - process.argv[++i].split(',').forEach(function(name){ + process.argv[++i].split(",").forEach(function(name) { var omit = STMT_ARG_TO_ID[name]; - STMTS_TO_USE = STMTS_TO_USE.filter(function(id){ return id !== omit; }) + STMTS_TO_USE = STMTS_TO_USE.filter(function(id) { + return id !== omit; + }); }); break; - case '--help': - case '-h': - case '-?': - println('** UglifyJS fuzzer help **'); - println('Valid options (optional):'); - println(': generate this many cases (if used must be first arg)'); - println('-v: print every generated test case'); - println('-V: print every 100th generated test case'); - println('-t : generate this many toplevels per run (more take longer)'); - println('-r : maximum recursion depth for generator (higher takes longer)'); - println('-s1 : force the first level statement to be this one (see list below)'); - println('-s2 : force the second level statement to be this one (see list below)'); - println('--no-catch-redef: do not redefine catch variables'); - println('--no-directive: do not generate directives'); + case "--help": + case "-h": + case "-?": + println("** UglifyJS fuzzer help **"); + println("Valid options (optional):"); + println(": generate this many cases (if used must be first arg)"); + println("-v: print every generated test case"); + println("-V: print every 100th generated test case"); + println("-t : generate this many toplevels per run (more take longer)"); + println("-r : maximum recursion depth for generator (higher takes longer)"); + println("-s1 : force the first level statement to be this one (see list below)"); + println("-s2 : force the second level statement to be this one (see list below)"); + println("--no-catch-redef: do not redefine catch variables"); + println("--no-directive: do not generate directives"); println('--use-strict: generate "use strict"'); - println('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise'); - println('--only-stmt : a comma delimited white list of statements that may be generated'); - println('--without-stmt : a comma delimited black list of statements never to generate'); - println('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID)); - println('** UglifyJS fuzzer exiting **'); + println("--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise"); + println("--only-stmt : a comma delimited white list of statements that may be generated"); + println("--without-stmt : a comma delimited black list of statements never to generate"); + println("List of accepted statement names: " + Object.keys(STMT_ARG_TO_ID)); + println("** UglifyJS fuzzer exiting **"); return 0; default: // first arg may be a number. - if (i > 2 || !parseInt(process.argv[i], 10)) throw new Error('Unknown argument[' + process.argv[i] + ']; see -h for help'); + if (i > 2 || !parseInt(process.argv[i], 10)) throw new Error("Unknown argument[" + process.argv[i] + "]; see -h for help"); } } @@ -126,126 +130,126 @@ var VALUES = [ '"b"', '"c"', '""', - 'true', - 'false', - ' /[a2][^e]+$/ ', - '(-1)', - '(-2)', - '(-3)', - '(-4)', - '(-5)', - '0', - '1', - '2', - '3', - '4', - '5', - '22', - '-0', // 0/-0 !== 0 - '23..toString()', - '24 .toString()', - '25. ', - '0x26.toString()', - 'NaN', - 'undefined', - 'Infinity', - 'null', - '[]', - '[,0][1]', // an array with elisions... but this is always false - '([,0].length === 2)', // an array with elisions... this is always true - '({})', // wrapped the object causes too many syntax errors in statements + "true", + "false", + " /[a2][^e]+$/ ", + "(-1)", + "(-2)", + "(-3)", + "(-4)", + "(-5)", + "0", + "1", + "2", + "3", + "4", + "5", + "22", + "-0", // 0/-0 !== 0 + "23..toString()", + "24 .toString()", + "25. ", + "0x26.toString()", + "NaN", + "undefined", + "Infinity", + "null", + "[]", + "[,0][1]", // an array with elisions... but this is always false + "([,0].length === 2)", // an array with elisions... this is always true + "({})", // wrapped the object causes too many syntax errors in statements '"foo"', '"bar"', '"undefined"', '"object"', '"number"', '"function"', - 'this', + "this", ]; var BINARY_OPS_NO_COMMA = [ - ' + ', // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors) - ' - ', - '/', - '*', - '&', - '|', - '^', - '<', - '<=', - '>', - '>=', - '==', - '===', - '!=', - '!==', - '<<', - '>>', - '>>>', - '%', - '&&', - '||', - '^' ]; - -var BINARY_OPS = [','].concat(BINARY_OPS_NO_COMMA); + " + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors) + " - ", + "/", + "*", + "&", + "|", + "^", + "<", + "<=", + ">", + ">=", + "==", + "===", + "!=", + "!==", + "<<", + ">>", + ">>>", + "%", + "&&", + "||", + "^" ]; + +var BINARY_OPS = [","].concat(BINARY_OPS_NO_COMMA); var ASSIGNMENTS = [ - '=', - '=', - '=', - '=', - '=', - '=', - '=', - '=', - '=', - '=', - - '=', - '=', - '=', - '=', - '=', - '=', - '=', - '=', - '=', - '=', - - '+=', - '+=', - '+=', - '+=', - '+=', - '+=', - '+=', - '+=', - '+=', - '+=', - - '-=', - '*=', - '/=', - '&=', - '|=', - '^=', - '<<=', - '>>=', - '>>>=', - '%=', + "=", + "=", + "=", + "=", + "=", + "=", + "=", + "=", + "=", + "=", + + "=", + "=", + "=", + "=", + "=", + "=", + "=", + "=", + "=", + "=", + + "+=", + "+=", + "+=", + "+=", + "+=", + "+=", + "+=", + "+=", + "+=", + "+=", + + "-=", + "*=", + "/=", + "&=", + "|=", + "^=", + "<<=", + ">>=", + ">>>=", + "%=", ]; var UNARY_SAFE = [ - '+', - '-', - '~', - '!', - 'void ', - 'delete ', + "+", + "-", + "~", + "!", + "void ", + "delete ", ]; var UNARY_POSTFIX = [ - '++', - '--', + "++", + "--", ]; var UNARY_PREFIX = UNARY_POSTFIX.concat(UNARY_SAFE); @@ -266,39 +270,40 @@ var DEFUN_OK = true; var DONT_STORE = true; var VAR_NAMES = [ - 'a', - 'a', - 'a', - 'a', - 'b', - 'b', - 'b', - 'b', - 'c', // prevent redeclaring this, avoid assigning to this - 'foo', - 'foo', - 'bar', - 'bar', - 'undefined', - 'NaN', - 'Infinity', - 'arguments', - 'Math', - 'parseInt', + "a", + "a", + "a", + "a", + "b", + "b", + "b", + "b", + "c", // prevent redeclaring this, avoid assigning to this + "foo", + "foo", + "bar", + "bar", + "undefined", + "NaN", + "Infinity", + "arguments", + "Math", + "parseInt", ]; var INITIAL_NAMES_LEN = VAR_NAMES.length; var TYPEOF_OUTCOMES = [ - 'function', - 'undefined', - 'string', - 'number', - 'object', - 'boolean', - 'special', - 'unknown', - 'symbol', - 'crap' ]; + "function", + "undefined", + "string", + "number", + "object", + "boolean", + "special", + "unknown", + "symbol", + "crap", +]; var unique_vars = []; var loops = 0; @@ -312,7 +317,7 @@ function rng(max) { } function strictMode() { - return use_strict && rng(4) == 0 ? '"use strict";' : ''; + return use_strict && rng(4) == 0 ? '"use strict";' : ""; } function createTopLevelCode() { @@ -323,20 +328,20 @@ function createTopLevelCode() { called = Object.create(null); return [ strictMode(), - 'var _calls_ = 10, a = 100, b = 10, c = 0;', + "var _calls_ = 10, a = 100, b = 10, c = 0;", rng(2) == 0 ? createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0) : createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0), // preceding `null` makes for a cleaner output (empty string still shows up etc) - 'console.log(null, a, b, c, Infinity, NaN, undefined);' - ].join('\n'); + "console.log(null, a, b, c, Infinity, NaN, undefined);" + ].join("\n"); } function createFunctions(n, recurmax, allowDefun, canThrow, stmtDepth) { - if (--recurmax < 0) { return ';'; } - var s = ''; + if (--recurmax < 0) { return ";"; } + var s = ""; while (n-- > 0) { - s += createFunction(recurmax, allowDefun, canThrow, stmtDepth) + '\n'; + s += createFunction(recurmax, allowDefun, canThrow, stmtDepth) + "\n"; } return s; } @@ -346,7 +351,7 @@ function createParams() { for (var n = rng(4); --n >= 0;) { params.push(createVarName(MANDATORY)); } - return params.join(', '); + return params.join(", "); } function createArgs(recurmax, stmtDepth, canThrow) { @@ -354,28 +359,28 @@ function createArgs(recurmax, stmtDepth, canThrow) { for (var n = rng(4); --n >= 0;) { args.push(rng(2) ? createValue() : createExpression(recurmax - 1, COMMA_OK, stmtDepth, canThrow)); } - return args.join(', '); + return args.join(", "); } function filterDirective(s) { - if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ';' + s[2]; + if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ";" + s[2]; return s; } function createFunction(recurmax, allowDefun, canThrow, stmtDepth) { - if (--recurmax < 0) { return ';'; } + if (--recurmax < 0) { return ";"; } if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0; var namesLenBefore = VAR_NAMES.length; var name; if (allowDefun || rng(5) > 0) { - name = 'f' + funcs++; + name = "f" + funcs++; } else { - unique_vars.push('a', 'b', 'c'); + unique_vars.push("a", "b", "c"); name = createVarName(MANDATORY, !allowDefun); unique_vars.length -= 3; } var s = [ - 'function ' + name + '(' + createParams() + '){', + "function " + name + "(" + createParams() + "){", strictMode() ]; if (rng(5) === 0) { @@ -385,28 +390,28 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) { // functions with statements s.push(createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth)); } - s.push('}', ''); - s = filterDirective(s).join('\n'); + s.push("}", ""); + s = filterDirective(s).join("\n"); VAR_NAMES.length = namesLenBefore; if (!allowDefun) { // avoid "function statements" (decl inside statements) - s = 'var ' + createVarName(MANDATORY) + ' = ' + s; - s += '(' + createArgs(recurmax, stmtDepth, canThrow) + ')'; + s = "var " + createVarName(MANDATORY) + " = " + s; + s += "(" + createArgs(recurmax, stmtDepth, canThrow) + ")"; } else if (!(name in called) || rng(3) > 0) { - s += 'var ' + createVarName(MANDATORY) + ' = ' + name; - s += '(' + createArgs(recurmax, stmtDepth, canThrow) + ')'; + s += "var " + createVarName(MANDATORY) + " = " + name; + s += "(" + createArgs(recurmax, stmtDepth, canThrow) + ")"; } - return s + ';'; + return s + ";"; } function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) { - if (--recurmax < 0) { return ';'; } - var s = ''; + if (--recurmax < 0) { return ";"; } + var s = ""; while (--n > 0) { - s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '\n'; + s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "\n"; } return s; } @@ -449,7 +454,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn ++stmtDepth; var loop = ++loops; if (--recurmax < 0) { - return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ';'; + return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ";"; } // allow to forcefully generate certain structures at first or second recursion level @@ -462,61 +467,61 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn switch (target) { case STMT_BLOCK: var label = createLabel(canBreak); - return label.target + '{' + createStatements(rng(5) + 1, recurmax, canThrow, label.break, canContinue, cannotReturn, stmtDepth) + '}'; + return label.target + "{" + createStatements(rng(5) + 1, recurmax, canThrow, label.break, canContinue, cannotReturn, stmtDepth) + "}"; case STMT_IF_ELSE: - 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) : ''); + 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: var label = createLabel(canBreak, canContinue); canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); - return '{var brake' + loop + ' = 5; ' + label.target + 'do {' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '} while ((' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && --brake' + loop + ' > 0);}'; + return "{var brake" + loop + " = 5; " + label.target + "do {" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "} while ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") && --brake" + loop + " > 0);}"; case STMT_WHILE: var label = createLabel(canBreak, canContinue); canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); - return '{var brake' + loop + ' = 5; ' + label.target + 'while ((' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && --brake' + loop + ' > 0)' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '}'; + return "{var brake" + loop + " = 5; " + label.target + "while ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") && --brake" + loop + " > 0)" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}"; case STMT_FOR_LOOP: var label = createLabel(canBreak, canContinue); canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); - return label.target + 'for (var brake' + loop + ' = 5; (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') && brake' + loop + ' > 0; --brake' + loop + ')' + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth); + return label.target + "for (var brake" + loop + " = 5; (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") && brake" + loop + " > 0; --brake" + loop + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth); case STMT_FOR_IN: var label = createLabel(canBreak, canContinue); canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); - var optElementVar = ''; + var optElementVar = ""; if (rng(5) > 1) { - optElementVar = 'c = 1 + c; var ' + createVarName(MANDATORY) + ' = expr' + loop + '[key' + loop + ']; '; + optElementVar = "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[key" + loop + "]; "; } - return '{var expr' + loop + ' = ' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '; ' + label.target + ' for (var key' + loop + ' in expr' + loop + ') {' + optElementVar + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '}}'; + return "{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; " + label.target + " for (var key" + loop + " in expr" + loop + ") {" + optElementVar + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}}"; case STMT_SEMI: - return use_strict && rng(20) === 0 ? '"use strict";' : ';'; + return use_strict && rng(20) === 0 ? '"use strict";' : ";"; case STMT_EXPR: - return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ';'; + return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ";"; case STMT_SWITCH: // note: case args are actual expressions // note: default does not _need_ to be last - return 'switch (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ') { ' + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + '}'; + return "switch (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") { " + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}"; case STMT_VAR: switch (rng(3)) { case 0: - unique_vars.push('c'); + unique_vars.push("c"); var name = createVarName(MANDATORY); unique_vars.pop(); - return 'var ' + name + ';'; + return "var " + name + ";"; case 1: // initializer can only have one expression - unique_vars.push('c'); + unique_vars.push("c"); var name = createVarName(MANDATORY); unique_vars.pop(); - return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; + return "var " + name + " = " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";"; default: // initializer can only have one expression - unique_vars.push('c'); + unique_vars.push("c"); var n1 = createVarName(MANDATORY); var n2 = createVarName(MANDATORY); unique_vars.pop(); - 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: switch (rng(8)) { @@ -524,32 +529,32 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn case 1: case 2: case 3: - if (canBreak && rng(5) === 0) return 'break' + getLabel(canBreak) + ';'; - if (canContinue && rng(5) === 0) return 'continue' + getLabel(canContinue) + ';'; - if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; - if (rng(3) == 0) return '/*3*/return;'; - return 'return ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; + if (canBreak && rng(5) === 0) return "break" + getLabel(canBreak) + ";"; + if (canContinue && rng(5) === 0) return "continue" + getLabel(canContinue) + ";"; + if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";"; + if (rng(3) == 0) return "/*3*/return;"; + return "return " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";"; case 4: // 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 // 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) + ';'; - return '{ /*2*/ return\n' + 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) + "}"; default: // must wrap in curlies to prevent orphaned `else` statement - if (canThrow && rng(5) === 0) return '{ throw ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}'; - if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; - return '{ return ' + 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) + ";"; + return "{ return " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "}"; } case STMT_FUNC_EXPR: // "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) - return '{' + createFunction(recurmax, NO_DEFUN, canThrow, stmtDepth) + '}'; + return "{" + createFunction(recurmax, NO_DEFUN, canThrow, stmtDepth) + "}"; case STMT_TRY: // catch var could cause some problems // note: the "blocks" are syntactically mandatory for try/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) + ' }'; + var s = "try {" + createStatement(recurmax, n === 1 ? CANNOT_THROW : CAN_THROW, canBreak, canContinue, cannotReturn, stmtDepth) + " }"; if (n !== 1) { // the catch var should only be accessible in the catch clause... // we have to do go through some trouble here to prevent leaking it @@ -557,77 +562,78 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn var catchName = createVarName(MANDATORY); var freshCatchName = VAR_NAMES.length !== nameLenBefore; if (!catch_redef) unique_vars.push(catchName); - s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }'; + s += " catch (" + catchName + ") { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }"; // remove catch name if (!catch_redef) unique_vars.pop(); if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1); } - if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }'; + if (n !== 0) s += " finally { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }"; return s; case STMT_C: - return 'c = c + 1;'; + return "c = c + 1;"; default: - throw 'no'; + throw "no"; } } function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) { var hadDefault = false; - var s = ['']; + var s = [""]; canBreak = enableLoopControl(canBreak, CAN_BREAK); while (n-- > 0) { //hadDefault = n > 0; // disables weird `default` clause positioning (use when handling destabilizes) if (hadDefault || rng(5) > 0) { s.push( - 'case ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ':', + "case " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ":", createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth), - rng(10) > 0 ? ' break;' : '/* fall-through */', - '' + rng(10) > 0 ? " break;" : "/* fall-through */", + "" ); } else { hadDefault = true; s.push( - 'default:', + "default:", createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth), - '' + "" ); } } - return s.join('\n'); + return s.join("\n"); } function createExpression(recurmax, noComma, stmtDepth, canThrow) { if (--recurmax < 0) { - return '(c = 1 + c, ' + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; // note: should return a simple non-recursing expression value! + return "(c = 1 + c, " + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; // note: should return a simple non-recursing expression value! } // since `a` and `b` are our canaries we want them more frequently than other expressions (1/3rd chance of a canary) switch (rng(6)) { case 0: - return '(a++ + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))'; + return "(a++ + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; case 1: - return '((--b) + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))'; + return "((--b) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; case 2: - return '((c = c + 1) + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))'; // c only gets incremented + return "((c = c + 1) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; // c only gets incremented default: - return '(' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ')'; + return "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")"; } } + function _createExpression(recurmax, noComma, stmtDepth, canThrow) { var p = 0; switch (rng(_createExpression.N)) { case p++: case p++: - return createUnaryPrefix() + (rng(2) === 1 ? 'a' : 'b'); + return createUnaryPrefix() + (rng(2) === 1 ? "a" : "b"); case p++: case p++: - return (rng(2) === 1 ? 'a' : 'b') + createUnaryPostfix(); + return (rng(2) === 1 ? "a" : "b") + createUnaryPostfix(); case p++: case p++: // 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 p++: case p++: - return rng(2) + ' === 1 ? a : b'; + return rng(2) + " === 1 ? a : b"; case p++: case p++: return createValue(); @@ -639,65 +645,65 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) { case p++: return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); case p++: - 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 p++: case p++: var nameLenBefore = VAR_NAMES.length; - unique_vars.push('c'); + unique_vars.push("c"); var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that. unique_vars.pop(); var s = []; switch (rng(5)) { case 0: s.push( - '(function ' + name + '(){', + "(function " + name + "(){", strictMode(), createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), - rng(2) == 0 ? '})' : '})()' + rng(2) == 0 ? "})" : "})()" ); break; case 1: s.push( - '+function ' + name + '(){', + "+function " + name + "(){", strictMode(), createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), - '}()' + "}()" ); break; case 2: s.push( - '!function ' + name + '(){', + "!function " + name + "(){", strictMode(), createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), - '}()' + "}()" ); break; case 3: s.push( - 'void function ' + name + '(){', + "void function " + name + "(){", strictMode(), createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), - '}()' + "}()" ); break; default: - var instantiate = rng(4) ? 'new ' : ''; + var instantiate = rng(4) ? "new " : ""; s.push( - instantiate + 'function ' + name + '(){', + instantiate + "function " + name + "(){", strictMode() ); if (instantiate) for (var i = rng(4); --i >= 0;) { - if (rng(2)) s.push('this.' + getDotKey(true) + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';'); - else s.push('this[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';'); + if (rng(2)) s.push("this." + getDotKey(true) + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ";"); + else s.push("this[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]" + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ";"); } s.push( createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), - rng(2) == 0 ? '}' : '}()' + rng(2) == 0 ? "}" : "}()" ); break; } VAR_NAMES.length = nameLenBefore; - return filterDirective(s).join('\n'); + return filterDirective(s).join("\n"); case p++: case p++: return createTypeofExpr(recurmax, stmtDepth, canThrow); @@ -708,29 +714,29 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) { // for prefix ops we need parens to prevent accidental syntax errors. switch (rng(6)) { case 0: - return 'a/* ignore */++'; + return "a/* ignore */++"; case 1: - return 'b/* ignore */--'; + return "b/* ignore */--"; case 2: - return '++/* ignore */a'; + return "++/* ignore */a"; case 3: - return '--/* ignore */b'; + return "--/* ignore */b"; case 4: // 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... - return '--(b)'; + return "--(b)"; case 5: // 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: - return '--/* ignore */b'; + return "--/* ignore */b"; } case p++: case p++: return createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow); case p++: case p++: - return createUnarySafePrefix() + '(' + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + return createUnarySafePrefix() + "(" + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; case p++: return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || a || 3).toString() "; case p++: @@ -744,28 +750,28 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) { case p++: return createObjectLiteral(recurmax, stmtDepth, canThrow); case p++: - return createArrayLiteral(recurmax, stmtDepth, canThrow) + '[' + - createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; + return createArrayLiteral(recurmax, stmtDepth, canThrow) + "[" + + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; case p++: - return createObjectLiteral(recurmax, stmtDepth, canThrow) + '[' + - createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; + return createObjectLiteral(recurmax, stmtDepth, canThrow) + "[" + + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; case p++: - return createArrayLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey(); + return createArrayLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey(); case p++: - return createObjectLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey(); + return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey(); case p++: var name = getVarName(); - return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; + return name + " && " + name + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; case p++: var name = getVarName(); - return name + ' && ' + name + '.' + getDotKey(); + return name + " && " + name + "." + getDotKey(); case p++: case p++: case p++: case p++: - var name = rng(3) == 0 ? getVarName() : 'f' + rng(funcs + 2); + var name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2); called[name] = true; - return 'typeof ' + name + ' == "function" && --_calls_ >= 0 && ' + name + '(' + createArgs(recurmax, stmtDepth, canThrow) + ')'; + return "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")"; } _createExpression.N = p; return _createExpression(recurmax, noComma, stmtDepth, canThrow); @@ -818,11 +824,11 @@ function createAccessor(recurmax, stmtDepth, canThrow) { var prop1 = getDotKey(); if (rng(2) == 0) { s = [ - 'get ' + prop1 + '(){', + "get " + prop1 + "(){", strictMode(), createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC), - '},' + "}," ]; } else { var prop2; @@ -830,30 +836,30 @@ function createAccessor(recurmax, stmtDepth, canThrow) { prop2 = getDotKey(); } while (prop1 == prop2); s = [ - 'set ' + prop1 + '(' + createVarName(MANDATORY) + '){', + "set " + prop1 + "(" + createVarName(MANDATORY) + "){", strictMode(), createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), - 'this.' + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ';', - '},' + "this." + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";", + "}," ]; } VAR_NAMES.length = namesLenBefore; - return filterDirective(s).join('\n'); + return filterDirective(s).join("\n"); } function createObjectLiteral(recurmax, stmtDepth, canThrow) { recurmax--; - var obj = ['({']; + var obj = ["({"]; for (var i = rng(6); --i >= 0;) { if (rng(20) == 0) { obj.push(createAccessor(recurmax, stmtDepth, canThrow)); } else { var key = KEYS[rng(KEYS.length)]; - obj.push(key + ':(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '),'); + obj.push(key + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "),"); } } - obj.push('})'); - return obj.join('\n'); + obj.push("})"); + return obj.join("\n"); } function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { @@ -861,8 +867,8 @@ function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { return _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow); } function _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { - return '(' + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) - + createBinaryOp(noComma) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + return "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + + createBinaryOp(noComma) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; } function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { // intentionally generate more hardcore ops @@ -870,22 +876,22 @@ function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { var assignee, expr; switch (rng(30)) { case 0: - return '(c = c + 1, ' + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + return "(c = c + 1, " + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; case 1: - return '(' + createUnarySafePrefix() + '(' + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + '))'; + return "(" + createUnarySafePrefix() + "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + "))"; case 2: assignee = getVarName(); - return '(' + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + return "(" + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; case 3: assignee = getVarName(); - expr = '(' + assignee + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) - + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; - return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')'; + expr = "(" + assignee + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + + "]" + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; + return canThrow && rng(10) == 0 ? expr : "(" + assignee + " && " + expr + ")"; case 4: assignee = getVarName(); - expr = '(' + assignee + '.' + getDotKey(true) + createAssignment() - + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; - return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')'; + expr = "(" + assignee + "." + getDotKey(true) + createAssignment() + + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; + return canThrow && rng(10) == 0 ? expr : "(" + assignee + " && " + expr + ")"; default: return _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow); } @@ -894,17 +900,17 @@ function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { function createTypeofExpr(recurmax, stmtDepth, canThrow) { switch (rng(8)) { case 0: - return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ' === "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; + return "(typeof " + createVarName(MANDATORY, DONT_STORE) + ' === "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; case 1: - return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ' !== "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; + return "(typeof " + createVarName(MANDATORY, DONT_STORE) + ' !== "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; case 2: - return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ' == "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; + return "(typeof " + createVarName(MANDATORY, DONT_STORE) + ' == "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; case 3: - return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ' != "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; + return "(typeof " + createVarName(MANDATORY, DONT_STORE) + ' != "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; case 4: - return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ')'; + return "(typeof " + createVarName(MANDATORY, DONT_STORE) + ")"; default: - return '(typeof ' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')'; + return "(typeof " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ")"; } } @@ -935,7 +941,7 @@ function createUnaryPostfix() { function getVarName() { // try to get a generated name reachable from current scope. default to just `a` - return VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)] || 'a'; + return VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)] || "a"; } function createVarName(maybe, dontStore) { @@ -944,12 +950,12 @@ function createVarName(maybe, dontStore) { var name; do { name = VAR_NAMES[rng(VAR_NAMES.length)]; - if (suffix) name += '_' + suffix; + if (suffix) name += "_" + suffix; } while (unique_vars.indexOf(name) >= 0); if (suffix && !dontStore) VAR_NAMES.push(name); return name; } - return ''; + return ""; } if (require.main !== module) { @@ -1045,7 +1051,7 @@ function log_rename(options) { } function log(options) { - if (!ok) errorln('\n\n\n\n\n\n!!!!!!!!!!\n\n\n'); + if (!ok) errorln("\n\n\n\n\n\n!!!!!!!!!!\n\n\n"); errorln("//============================================================="); if (!ok) errorln("// !!!!!! Failed... round " + round); errorln("// original code"); @@ -1096,11 +1102,13 @@ for (var round = 1; round <= num_iterations; round++) { original_code = createTopLevelCode(); var orig_result = [ sandbox.run_code(original_code) ]; errored = typeof orig_result[0] != "string"; - if (!errored) orig_result.push(sandbox.run_code(original_code, true)); + if (!errored) { + orig_result.push(sandbox.run_code(original_code, true), sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"))); + } (errored ? fallback_options : minify_options).forEach(function(options) { var o = JSON.parse(options); uglify_code = UglifyJS.minify(original_code, o); - original_result = orig_result[o.toplevel ? 1 : 0]; + original_result = orig_result[o.compress.unsafe_math ? 2 : o.toplevel ? 1 : 0]; if (!uglify_code.error) { uglify_code = uglify_code.code; uglify_result = sandbox.run_code(uglify_code, o.toplevel);