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
This commit is contained in:
Alex Lam S.L
2017-04-07 18:47:30 +08:00
committed by GitHub
parent c2a1bceb77
commit a1532eb076
3 changed files with 198 additions and 133 deletions

View File

@@ -29,7 +29,7 @@ exports.run_code = function(code) {
console: { console: {
log: function() { log: function() {
return console.log.apply(console, [].map.call(arguments, function(arg) { return console.log.apply(console, [].map.call(arguments, function(arg) {
return typeof arg == "function" ? arg.toString() : arg; return typeof arg == "function" || arg && /Error$/.test(arg.name) ? arg.toString() : arg;
})); }));
} }
} }

View File

@@ -222,15 +222,19 @@ var ASSIGNMENTS = [
'>>>=', '>>>=',
'%=' ]; '%=' ];
var UNARY_OPS = [ var UNARY_SAFE = [
'--', '+',
'++', '-',
'~', '~',
'!', '!',
'void ', '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 NO_COMMA = true;
var COMMA_OK = false; var COMMA_OK = false;
@@ -251,26 +255,26 @@ var NO_DECL = true;
var DONT_STORE = true; var DONT_STORE = true;
var VAR_NAMES = [ var VAR_NAMES = [
'foo', 'a',
'bar', 'a',
'a',
'a', 'a',
'b', 'b',
'b',
'b',
'b',
'c', // prevent redeclaring this, avoid assigning to this 'c', // prevent redeclaring this, avoid assigning to this
'undefined', // fun! 'foo',
'eval', // mmmm, ok, also fun! 'foo',
'NaN', // mmmm, ok, also fun! 'bar',
'Infinity', // the fun never ends! 'bar',
'arguments', // this one is just creepy 'undefined',
'Math', // since Math is assumed to be a non-constructor/function it may trip certain cases 'NaN',
'Infinity',
'arguments',
'Math',
'parseInt', 'parseInt',
'parseFloat', ];
'isNaN',
'isFinite',
'decodeURI',
'decodeURIComponent',
'encodeURI',
'encodeURIComponent',
'Object'];
var INITIAL_NAMES_LEN = VAR_NAMES.length; var INITIAL_NAMES_LEN = VAR_NAMES.length;
var TYPEOF_OUTCOMES = [ var TYPEOF_OUTCOMES = [
@@ -307,6 +311,22 @@ function createFunctions(n, recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
return s; 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) { function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
if (--recurmax < 0) { return ';'; } if (--recurmax < 0) { return ';'; }
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0; if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
@@ -317,17 +337,17 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
var s = ''; var s = '';
if (rng(5) === 0) { if (rng(5) === 0) {
// 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) + '){' + 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 { } else {
// functions with statements // 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; 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) // 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; return s;
@@ -399,7 +419,8 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
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;'; if (rng(3) == 0) return '/*3*/return;';
return '/*4*/return ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
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) + '}';
@@ -464,42 +485,44 @@ function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotR
function createExpression(recurmax, noComma, stmtDepth, canThrow) { function createExpression(recurmax, noComma, stmtDepth, canThrow) {
if (--recurmax < 0) { 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) // 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); switch (rng(6)) {
if (r < 1) return 'a++ + ' + _createExpression(recurmax, noComma, stmtDepth, canThrow); case 0:
if (r < 2) return '(--b) + ' + _createExpression(recurmax, noComma, stmtDepth, canThrow); return '(a++ + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))';
if (r < 3) return '(c = c + 1) + ' + _createExpression(recurmax, noComma, stmtDepth, canThrow); // c only gets incremented case 1:
return '((--b) + (' + _createExpression(recurmax, noComma, stmtDepth, canThrow) + '))';
return _createExpression(recurmax, noComma, stmtDepth, canThrow); case 2:
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) { function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
switch (rng(31)) { var p = 0;
case 0: switch (rng(_createExpression.N)) {
case 1: case p++:
return createUnaryOp() + (rng(2) === 1 ? 'a' : 'b'); case p++:
case 2: return createUnaryPrefix() + (rng(2) === 1 ? 'a' : 'b');
case 3: case p++:
return 'a' + (rng(2) == 1 ? '++' : '--'); case p++:
case 4: return (rng(2) === 1 ? 'a' : 'b') + createUnaryPostfix();
case 5: case p++:
case p++:
// 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 6: case p++:
case 7: case p++:
return rng(2) + ' === 1 ? a : b'; return rng(2) + ' === 1 ? a : b';
case 8: case p++:
case 9: case p++:
return createNestedBinaryExpr(recurmax, noComma) + createBinaryOp(noComma) + createExpression(recurmax, noComma, stmtDepth, canThrow);
case 10:
case 11:
return createValue(); return createValue();
case 12: case p++:
return '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')'; return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case 13: 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 14: case p++:
var nameLenBefore = VAR_NAMES.length; var nameLenBefore = VAR_NAMES.length;
var name = createVarName(MAYBE); // 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';
@@ -520,19 +543,18 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
} }
VAR_NAMES.length = nameLenBefore; VAR_NAMES.length = nameLenBefore;
return s; return s;
case 15: case p++:
case 16: case p++:
return createTypeofExpr(recurmax, stmtDepth, canThrow); return createTypeofExpr(recurmax, stmtDepth, canThrow);
case 17: case p++:
// you could statically infer that this is just `Math`, regardless of the other expression return [
// I don't think Uglify does this at this time... 'new function() {',
return ''+ rng(2) ? '' : createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ';',
'new function(){ \n' + 'return ' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ';',
(rng(2) === 1 ? createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '\n' : '') + '}'
'return Math;\n' + ].join('\n');
'}'; case p++:
case 18: case p++:
case 19:
// 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.
@@ -542,47 +564,56 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
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 20: case p++:
case 21: case p++:
return createNestedBinaryExpr(recurmax, noComma); return createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
case 22: case p++:
case p++:
return createUnarySafePrefix() + '(' + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
case p++:
return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || a || 3).toString() "; 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()) "; return " /[abc4]/.test(((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") || b || 5).toString()) ";
case 24: case p++:
return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + return " ((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) +
") || " + rng(10) + ").toString()[" + ") || " + rng(10) + ").toString()[" +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] "; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
case 25: case p++:
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow); return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
case 26: case p++:
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow); return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
case 27: case p++:
return '(' + createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' + return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]) "; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case 28: case p++:
return '(' + createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' + return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]) "; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case 29: case p++:
return '(' + createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
SAFE_KEYS[rng(SAFE_KEYS.length)] + ") "; case p++:
case 30: return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
return '(' + createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + case p++:
SAFE_KEYS[rng(SAFE_KEYS.length)] + ") "; 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) { function createArrayLiteral(recurmax, noComma, stmtDepth, canThrow) {
@@ -618,6 +649,10 @@ var KEYS = [
"3", "3",
].concat(SAFE_KEYS); ].concat(SAFE_KEYS);
function getDotKey() {
return SAFE_KEYS[rng(SAFE_KEYS.length)];
}
function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) { function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) {
recurmax--; recurmax--;
var obj = "({"; var obj = "({";
@@ -628,36 +663,52 @@ function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) {
return obj + "})"; 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 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 _createSimpleBinaryExpr(recurmax, noComma) { function _createBinaryExpr(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 // intentionally generate more hardcore ops
if (--recurmax < 0) return createValue(); if (--recurmax < 0) return createValue();
var r = rng(30); switch (rng(30)) {
if (r === 0) return '(c = c + 1, ' + _createSimpleBinaryExpr(recurmax, noComma) + ')'; case 0:
var s = _createSimpleBinaryExpr(recurmax, noComma) + createBinaryOp(noComma) + _createSimpleBinaryExpr(recurmax, noComma); return '(c = c + 1, ' + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
if (r === 1) { case 1:
// try to get a generated name reachable from current scope. default to just `a` return '(' + createUnarySafePrefix() + '(' + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + '))';
var assignee = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)] || 'a'; case 2:
return '( ' + assignee + createAssignment() + s + ')'; 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) { function createTypeofExpr(recurmax, stmtDepth, canThrow) {
switch (rng(8)) { switch (rng(8)) {
case 0: 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: 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: 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: 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: case 4:
return 'typeof ' + createVarName(MANDATORY, DONT_STORE); return '(typeof ' + createVarName(MANDATORY, DONT_STORE) + ')';
default: default:
return '(typeof ' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')'; return '(typeof ' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ')';
} }
@@ -676,16 +727,31 @@ function createAssignment() {
return ASSIGNMENTS[rng(ASSIGNMENTS.length)]; return ASSIGNMENTS[rng(ASSIGNMENTS.length)];
} }
function createUnaryOp() { function createUnarySafePrefix() {
return UNARY_OPS[rng(UNARY_OPS.length)]; 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) { function createVarName(maybe, dontStore) {
if (!maybe || rng(2) === 1) { if (!maybe || rng(2)) {
var r = rng(VAR_NAMES.length); var name = VAR_NAMES[rng(VAR_NAMES.length)];
var suffixed = rng(5) > 0; var suffix = rng(3);
var name = VAR_NAMES[r] + (suffixed ? '_' + (++loops) : ''); if (suffix) {
if (!dontStore && suffixed) VAR_NAMES.push(name); name += '_' + suffix;
if (!dontStore) VAR_NAMES.push(name);
}
return name; return name;
} }
return ''; return '';

View File

@@ -1,4 +1,21 @@
[ [
{
"compress": false,
"mangle": false,
"output": {
"beautify": true,
"bracketize": true
}
},
{
"compress": false
},
{
"compress": {
"warnings": false
},
"mangle": false
},
{ {
"compress": { "compress": {
"warnings": false "warnings": false
@@ -13,24 +30,6 @@
"toplevel": true "toplevel": true
} }
}, },
{
"compress": {
"warnings": false
},
"mangle": false
},
{
"compress": false,
"mangle": true
},
{
"compress": false,
"mangle": false,
"output": {
"beautify": true,
"bracketize": true
}
},
{ {
"compress": { "compress": {
"keep_fargs": false, "keep_fargs": false,