diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2017-04-07 18:47:30 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-07 18:47:30 +0800 |
commit | a1532eb076d506fbf87a04c0ec4f26e1929aa902 (patch) | |
tree | 77a0c257450c87b2f9852111ccf31aa84c3ba6b3 /test/ufuzz.js | |
parent | c2a1bceb773aab8875e0ffabf9f6b5199462f091 (diff) | |
download | tracifyjs-a1532eb076d506fbf87a04c0ec4f26e1929aa902.tar.gz tracifyjs-a1532eb076d506fbf87a04c0ec4f26e1929aa902.zip |
extend ufuzz generator (#1783)
- property access
- property assignment
- allow bare expression within try-block
- normalise `Error` in `console.log()`
- generate more unary expressions
- add parenthesis to enforce precedence
- adjust variable reuse/creation
- add parameters to function declaration & expression
- add return expression
- add trivial arguments to function call
Diffstat (limited to 'test/ufuzz.js')
-rw-r--r-- | test/ufuzz.js | 292 |
1 files changed, 179 insertions, 113 deletions
diff --git a/test/ufuzz.js b/test/ufuzz.js index 1bb3f695..78943ca4 100644 --- a/test/ufuzz.js +++ b/test/ufuzz.js @@ -222,15 +222,19 @@ var ASSIGNMENTS = [ '>>>=', '%=' ]; -var UNARY_OPS = [ - '--', - '++', +var UNARY_SAFE = [ + '+', + '-', '~', '!', 'void ', - 'delete ', // should be safe, even `delete foo` and `delete f()` shouldn't crash - ' - ', - ' + ' ]; + 'delete ', +]; +var UNARY_POSTFIX = [ + '++', + '--', +]; +var UNARY_PREFIX = UNARY_POSTFIX.concat(UNARY_SAFE); var NO_COMMA = true; var COMMA_OK = false; @@ -251,26 +255,26 @@ var NO_DECL = true; var DONT_STORE = true; var VAR_NAMES = [ - 'foo', - 'bar', 'a', + 'a', + 'a', + 'a', + 'b', + 'b', + 'b', 'b', 'c', // prevent redeclaring this, avoid assigning to this - 'undefined', // fun! - 'eval', // mmmm, ok, also fun! - 'NaN', // mmmm, ok, also fun! - 'Infinity', // the fun never ends! - 'arguments', // this one is just creepy - 'Math', // since Math is assumed to be a non-constructor/function it may trip certain cases + 'foo', + 'foo', + 'bar', + 'bar', + 'undefined', + 'NaN', + 'Infinity', + 'arguments', + 'Math', 'parseInt', - 'parseFloat', - 'isNaN', - 'isFinite', - 'decodeURI', - 'decodeURIComponent', - 'encodeURI', - 'encodeURIComponent', - 'Object']; +]; var INITIAL_NAMES_LEN = VAR_NAMES.length; var TYPEOF_OUTCOMES = [ @@ -307,6 +311,22 @@ function createFunctions(n, recurmax, inGlobal, noDecl, canThrow, stmtDepth) { return s; } +function createParams() { + var params = []; + for (var n = rng(4); --n >= 0;) { + params.push(createVarName(MANDATORY)); + } + return params.join(', '); +} + +function createArgs() { + var args = []; + for (var n = rng(4); --n >= 0;) { + args.push(createValue()); + } + return args.join(', '); +} + function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) { if (--recurmax < 0) { return ';'; } if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0; @@ -317,17 +337,17 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) { var s = ''; if (rng(5) === 0) { // functions with functions. lower the recursion to prevent a mess. - s = 'function ' + name + '(' + createVarName(MANDATORY) + '){' + createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth) + '}\n'; + s = 'function ' + name + '(' + createParams() + '){' + createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth) + '}\n'; } else { // functions with statements - s = 'function ' + name + '(' + createVarName(MANDATORY) + '){' + createStatements(3, recurmax, canThrow, CANNOT_THROW, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '}\n'; + s = 'function ' + name + '(' + createParams() + '){' + createStatements(3, recurmax, canThrow, CANNOT_THROW, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + '}\n'; } VAR_NAMES.length = namesLenBefore; - if (noDecl) s = '!' + s + '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')'; + if (noDecl) s = 'var ' + createVarName(MANDATORY) + ' = ' + s + '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ');'; // avoid "function statements" (decl inside statements) - else if (inGlobal || rng(10) > 0) s += name + '();' + else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');'; return s; @@ -399,7 +419,8 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn if (canBreak && rng(5) === 0) return 'break;'; if (canContinue && rng(5) === 0) return 'continue;'; if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; - return '/*3*/return;'; + if (rng(3) == 0) return '/*3*/return;'; + return '/*4*/return ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; case 2: // must wrap in curlies to prevent orphaned `else` statement if (canThrow && rng(5) === 0) return '{ throw ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + '}'; @@ -464,42 +485,44 @@ function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotR function createExpression(recurmax, noComma, stmtDepth, canThrow) { if (--recurmax < 0) { - return '(c = 1 + c, ' + createNestedBinaryExpr(recurmax, noComma) + ')'; // 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) - var r = rng(6); - if (r < 1) return 'a++ + ' + _createExpression(recurmax, noComma, stmtDepth, canThrow); - if (r < 2) return '(--b) + ' + _createExpression(recurmax, noComma, stmtDepth, canThrow); - if (r < 3) return '(c = c + 1) + ' + _createExpression(recurmax, noComma, stmtDepth, canThrow); // c only gets incremented - - return _createExpression(recurmax, noComma, stmtDepth, canThrow); -} -function _createExpression(recurmax, noComma, stmtDepth, canThrow) { - switch (rng(31)) { + switch (rng(6)) { case 0: + return '(a++ + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))'; case 1: - return createUnaryOp() + (rng(2) === 1 ? 'a' : 'b'); + return '((--b) + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))'; case 2: - case 3: - return 'a' + (rng(2) == 1 ? '++' : '--'); - case 4: - case 5: + return '((c = c + 1) + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))'; // c only gets incremented + default: + 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'); + case p++: + case p++: + 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)'; - case 6: - case 7: + return 'b ' + createAssignment() + ' a'; + case p++: + case p++: return rng(2) + ' === 1 ? a : b'; - case 8: - case 9: - return createNestedBinaryExpr(recurmax, noComma) + createBinaryOp(noComma) + createExpression(recurmax, noComma, stmtDepth, canThrow); - case 10: - case 11: + case p++: + case p++: return createValue(); - case 12: - return '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')'; - case 13: + 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); - case 14: + case p++: var nameLenBefore = VAR_NAMES.length; var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that. if (name === 'c') name = 'a'; @@ -520,19 +543,18 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) { } VAR_NAMES.length = nameLenBefore; return s; - case 15: - case 16: + case p++: + case p++: return createTypeofExpr(recurmax, stmtDepth, canThrow); - case 17: - // you could statically infer that this is just `Math`, regardless of the other expression - // I don't think Uglify does this at this time... - return ''+ - 'new function(){ \n' + - (rng(2) === 1 ? createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '\n' : '') + - 'return Math;\n' + - '}'; - case 18: - case 19: + case p++: + return [ + 'new function() {', + rng(2) ? '' : createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ';', + 'return ' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ';', + '}' + ].join('\n'); + case p++: + case p++: // 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) // for prefix ops we need parens to prevent accidental syntax errors. @@ -542,47 +564,56 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) { case 1: 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'; default: - return '(--/* ignore */b)'; + return '--/* ignore */b'; } - case 20: - case 21: - return createNestedBinaryExpr(recurmax, noComma); - case 22: + case p++: + case p++: + return createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow); + case p++: + case p++: + return createUnarySafePrefix() + '(' + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + case p++: return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || a || 3).toString() "; - case 23: + case p++: return " /[abc4]/.test(((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || b || 5).toString()) "; - case 24: + case p++: return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || " + rng(10) + ").toString()[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] "; - case 25: + case p++: return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow); - case 26: + case p++: return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow); - case 27: - return '(' + createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' + - createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]) "; - case 28: - return '(' + createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' + - createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]) "; - case 29: - return '(' + createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + - SAFE_KEYS[rng(SAFE_KEYS.length)] + ") "; - case 30: - return '(' + createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + - SAFE_KEYS[rng(SAFE_KEYS.length)] + ") "; + case p++: + return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' + + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; + case p++: + return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' + + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; + case p++: + return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey(); + case p++: + return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey(); + case p++: + var name = getVarName(); + return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; + case p++: + var name = getVarName(); + return name + ' && ' + name + '.' + getDotKey(); } + _createExpression.N = p; + return _createExpression(recurmax, noComma, stmtDepth, canThrow); } function createArrayLiteral(recurmax, noComma, stmtDepth, canThrow) { @@ -618,6 +649,10 @@ var KEYS = [ "3", ].concat(SAFE_KEYS); +function getDotKey() { + return SAFE_KEYS[rng(SAFE_KEYS.length)]; +} + function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) { recurmax--; var obj = "({"; @@ -628,36 +663,52 @@ function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) { return obj + "})"; } -function createNestedBinaryExpr(recurmax, noComma) { +function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { recurmax = 3; // note that this generates 2^recurmax expression parts... make sure to cap it - return _createSimpleBinaryExpr(recurmax, noComma); + 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) + ')'; } -function _createSimpleBinaryExpr(recurmax, noComma) { +function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { // intentionally generate more hardcore ops if (--recurmax < 0) return createValue(); - var r = rng(30); - if (r === 0) return '(c = c + 1, ' + _createSimpleBinaryExpr(recurmax, noComma) + ')'; - var s = _createSimpleBinaryExpr(recurmax, noComma) + createBinaryOp(noComma) + _createSimpleBinaryExpr(recurmax, noComma); - if (r === 1) { - // try to get a generated name reachable from current scope. default to just `a` - var assignee = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)] || 'a'; - return '( ' + assignee + createAssignment() + s + ')'; + switch (rng(30)) { + case 0: + return '(c = c + 1, ' + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + case 1: + return '(' + createUnarySafePrefix() + '(' + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + '))'; + case 2: + var assignee = getVarName(); + return '(' + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + case 3: + var assignee = getVarName(); + var expr = '(' + assignee + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')'; + case 4: + var assignee = getVarName(); + var expr = '(' + assignee + '.' + getDotKey() + createAssignment() + + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')'; + default: + return _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow); } - return s; } 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) + ')'; } @@ -676,16 +727,31 @@ function createAssignment() { return ASSIGNMENTS[rng(ASSIGNMENTS.length)]; } -function createUnaryOp() { - return UNARY_OPS[rng(UNARY_OPS.length)]; +function createUnarySafePrefix() { + return UNARY_SAFE[rng(UNARY_SAFE.length)]; +} + +function createUnaryPrefix() { + return UNARY_PREFIX[rng(UNARY_PREFIX.length)]; +} + +function createUnaryPostfix() { + return UNARY_POSTFIX[rng(UNARY_POSTFIX.length)]; +} + +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'; } function createVarName(maybe, dontStore) { - if (!maybe || rng(2) === 1) { - var r = rng(VAR_NAMES.length); - var suffixed = rng(5) > 0; - var name = VAR_NAMES[r] + (suffixed ? '_' + (++loops) : ''); - if (!dontStore && suffixed) VAR_NAMES.push(name); + if (!maybe || rng(2)) { + var name = VAR_NAMES[rng(VAR_NAMES.length)]; + var suffix = rng(3); + if (suffix) { + name += '_' + suffix; + if (!dontStore) VAR_NAMES.push(name); + } return name; } return ''; |