pop();
return true;
}
+ if (node instanceof AST_Accessor) {
+ var save_ids = safe_ids;
+ safe_ids = Object.create(null);
+ descend();
+ safe_ids = save_ids;
+ return true;
+ }
if (node instanceof AST_Binary
&& (node.operator == "&&" || node.operator == "||")) {
node.left.walk(tw);
&& !node.expression.has_side_effects(compressor);
}
- // may_eq_null()
- // returns true if this node may evaluate to null or undefined
+ // may_throw_on_access()
+ // returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
- AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
+ AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
var pure_getters = compressor.option("pure_getters");
- return !pure_getters || this._eq_null(pure_getters);
+ return !pure_getters || this._throw_on_access(pure_getters);
});
function is_strict(pure_getters) {
def(AST_Undefined, return_true);
def(AST_Constant, return_false);
def(AST_Array, return_false);
- def(AST_Object, return_false);
+ def(AST_Object, function(pure_getters) {
+ if (!is_strict(pure_getters)) return false;
+ for (var i = this.properties.length; --i >=0;)
+ if (this.properties[i].value instanceof AST_Accessor) return true;
+ return false;
+ });
def(AST_Function, return_false);
def(AST_UnaryPostfix, return_false);
def(AST_UnaryPrefix, function() {
def(AST_Binary, function(pure_getters) {
switch (this.operator) {
case "&&":
- return this.left._eq_null(pure_getters);
+ return this.left._throw_on_access(pure_getters);
case "||":
- return this.left._eq_null(pure_getters)
- && this.right._eq_null(pure_getters);
+ return this.left._throw_on_access(pure_getters)
+ && this.right._throw_on_access(pure_getters);
default:
return false;
}
})
def(AST_Assign, function(pure_getters) {
return this.operator == "="
- && this.right._eq_null(pure_getters);
+ && this.right._throw_on_access(pure_getters);
})
def(AST_Conditional, function(pure_getters) {
- return this.consequent._eq_null(pure_getters)
- || this.alternative._eq_null(pure_getters);
+ return this.consequent._throw_on_access(pure_getters)
+ || this.alternative._throw_on_access(pure_getters);
})
def(AST_Sequence, function(pure_getters) {
- return this.expressions[this.expressions.length - 1]._eq_null(pure_getters);
+ return this.expressions[this.expressions.length - 1]._throw_on_access(pure_getters);
});
def(AST_SymbolRef, function(pure_getters) {
if (this.is_undefined) return true;
if (!is_strict(pure_getters)) return false;
var fixed = this.fixed_value();
- return !fixed || fixed._eq_null(pure_getters);
+ return !fixed || fixed._throw_on_access(pure_getters);
});
})(function(node, func) {
- node.DEFMETHOD("_eq_null", func);
+ node.DEFMETHOD("_throw_on_access", func);
});
/* -----[ boolean/negation helpers ]----- */
return any(this.elements, compressor);
});
def(AST_Dot, function(compressor){
- return this.expression.may_eq_null(compressor)
+ return this.expression.may_throw_on_access(compressor)
|| this.expression.has_side_effects(compressor);
});
def(AST_Sub, function(compressor){
- return this.expression.may_eq_null(compressor)
+ return this.expression.may_throw_on_access(compressor)
|| this.expression.has_side_effects(compressor)
|| this.property.has_side_effects(compressor);
});
var args = trim(this.args, compressor, first_in_statement);
return args && make_sequence(this, args);
});
+ def(AST_Accessor, return_null);
def(AST_Function, return_null);
def(AST_Binary, function(compressor, first_in_statement){
var right = this.right.drop_side_effect_free(compressor);
return values && make_sequence(this, values);
});
def(AST_Dot, function(compressor, first_in_statement){
- if (this.expression.may_eq_null(compressor)) return this;
+ if (this.expression.may_throw_on_access(compressor)) return this;
return this.expression.drop_side_effect_free(compressor, first_in_statement);
});
def(AST_Sub, function(compressor, first_in_statement){
- if (this.expression.may_eq_null(compressor)) return this;
+ if (this.expression.may_throw_on_access(compressor)) return this;
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
var property = this.property.drop_side_effect_free(compressor);
var num_iterations = +process.argv[2] || 1/0;
var verbose = false; // log every generated test
var verbose_interval = false; // log every 100 generated tests
-var verbose_error = false;
var use_strict = false;
for (var i = 2; i < process.argv.length; ++i) {
switch (process.argv[i]) {
case '-V':
verbose_interval = true;
break;
- case '-E':
- verbose_error = true;
- break;
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');
console.log('<number>: generate this many cases (if used must be first arg)');
console.log('-v: print every generated test case');
console.log('-V: print every 100th generated test case');
- console.log('-E: print generated test case with runtime error');
console.log('-t <int>: generate this many toplevels per run (more take longer)');
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
'=',
'=',
'=',
+ '=',
+ '=',
+ '=',
+ '=',
- '==',
- '!=',
- '===',
- '!==',
+ '=',
+ '=',
+ '=',
+ '=',
+ '=',
+ '=',
+ '=',
+ '=',
+ '=',
+ '=',
+
+ '+=',
+ '+=',
+ '+=',
+ '+=',
'+=',
+ '+=',
+ '+=',
+ '+=',
+ '+=',
+ '+=',
+
'-=',
'*=',
'/=',
'<<=',
'>>=',
'>>>=',
- '%=' ];
+ '%=',
+];
var UNARY_SAFE = [
'+',
// avoid "function statements" (decl inside statements)
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
-
return s;
}
return label && " L" + label;
}
-function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
+function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, target) {
++stmtDepth;
var loop = ++loops;
if (--recurmax < 0) {
}
// allow to forcefully generate certain structures at first or second recursion level
- var target = 0;
- if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE;
- else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE;
- else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
+ if (target === undefined) {
+ if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE;
+ else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE;
+ else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
+ }
switch (target) {
case STMT_BLOCK:
strictMode()
);
if (instantiate) for (var i = rng(4); --i >= 0;) {
- if (rng(2)) s.push('this.' + getDotKey() + 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(
") || " + rng(10) + ").toString()[" +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
case p++:
- return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
+ return createArrayLiteral(recurmax, stmtDepth, canThrow);
case p++:
- return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
+ return createObjectLiteral(recurmax, stmtDepth, canThrow);
case p++:
- return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
+ return createArrayLiteral(recurmax, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case p++:
- return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
+ return createObjectLiteral(recurmax, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case p++:
- return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
+ return createArrayLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey();
case p++:
- return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
+ return createObjectLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey();
case p++:
var name = getVarName();
return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
return _createExpression(recurmax, noComma, stmtDepth, canThrow);
}
-function createArrayLiteral(recurmax, noComma, stmtDepth, canThrow) {
+function createArrayLiteral(recurmax, stmtDepth, canThrow) {
recurmax--;
var arr = "[";
for (var i = rng(6); --i >= 0;) {
"3",
].concat(SAFE_KEYS);
-function getDotKey() {
- return SAFE_KEYS[rng(SAFE_KEYS.length)];
+function getDotKey(assign) {
+ var key;
+ do {
+ key = SAFE_KEYS[rng(SAFE_KEYS.length)];
+ } while (assign && key == "length");
+ return key;
+}
+
+function createAccessor(recurmax, stmtDepth, canThrow) {
+ var namesLenBefore = VAR_NAMES.length;
+ var s;
+ var prop1 = getDotKey();
+ if (rng(2) == 0) {
+ s = [
+ '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),
+ '},'
+ ].join('\n');
+ } else {
+ var prop2;
+ do {
+ prop2 = getDotKey();
+ } while (prop1 == prop2);
+ s = [
+ '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) + ';',
+ '},'
+ ].join('\n');
+ }
+ VAR_NAMES.length = namesLenBefore;
+ return s;
}
-function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) {
+function createObjectLiteral(recurmax, stmtDepth, canThrow) {
recurmax--;
- var obj = "({";
+ var obj = ['({'];
for (var i = rng(6); --i >= 0;) {
- var key = KEYS[rng(KEYS.length)];
- obj += key + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "), ";
+ 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) + '),');
+ }
}
- return obj + "})";
+ obj.push('})');
+ return obj.join('\n');
}
function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
case 4:
assignee = getVarName();
- expr = '(' + assignee + '.' + getDotKey() + createAssignment()
+ expr = '(' + assignee + '.' + getDotKey(true) + createAssignment()
+ _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
default:
}
}
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
- else if (verbose_error && typeof original_result != "string") {
+ else if (typeof original_result != "string") {
console.log("//=============================================================");
console.log("// original code");
try_beautify(original_code, original_result);