[Pkg-javascript-commits] [uglifyjs] 213/228: extend ufuzz generator (#1783)

Jonas Smedegaard dr at jones.dk
Sat Apr 15 14:25:31 UTC 2017


This is an automated email from the git hooks/post-receive script.

js pushed a commit to branch master
in repository uglifyjs.

commit a1532eb076d506fbf87a04c0ec4f26e1929aa902
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date:   Fri Apr 7 18:47:30 2017 +0800

    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
---
 test/sandbox.js |   2 +-
 test/ufuzz.js   | 292 ++++++++++++++++++++++++++++++++++----------------------
 test/ufuzz.json |  31 +++---
 3 files changed, 195 insertions(+), 130 deletions(-)

diff --git a/test/sandbox.js b/test/sandbox.js
index ea3a60a..894349f 100644
--- a/test/sandbox.js
+++ b/test/sandbox.js
@@ -29,7 +29,7 @@ exports.run_code = function(code) {
             console: {
                 log: function() {
                     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;
                     }));
                 }
             }
diff --git a/test/ufuzz.js b/test/ufuzz.js
index 1bb3f69..78943ca 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 '';
diff --git a/test/ufuzz.json b/test/ufuzz.json
index 4523795..c481347 100644
--- a/test/ufuzz.json
+++ b/test/ufuzz.json
@@ -1,17 +1,14 @@
 [
     {
-        "compress": {
-            "warnings": false
+        "compress": false,
+        "mangle": false,
+        "output": {
+            "beautify": true,
+            "bracketize": true
         }
     },
     {
-        "compress": {
-            "toplevel": true,
-            "warnings": false
-        },
-        "mangle": {
-            "toplevel": true
-        }
+        "compress": false
     },
     {
         "compress": {
@@ -20,15 +17,17 @@
         "mangle": false
     },
     {
-        "compress": false,
-        "mangle": true
+        "compress": {
+            "warnings": false
+        }
     },
     {
-        "compress": false,
-        "mangle": false,
-        "output": {
-            "beautify": true,
-            "bracketize": true
+        "compress": {
+            "toplevel": true,
+            "warnings": false
+        },
+        "mangle": {
+            "toplevel": true
         }
     },
     {

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/uglifyjs.git



More information about the Pkg-javascript-commits mailing list