if (!(node[prop] instanceof AST_Node)) throw new Error(prop + " must be AST_Node");
if (node[prop] instanceof AST_Hole) throw new Error(prop + " cannot be AST_Hole");
if (node[prop] instanceof AST_Spread) throw new Error(prop + " cannot be AST_Spread");
- if (node[prop] instanceof AST_Statement && !(node[prop] instanceof AST_Function)) {
+ if (node[prop] instanceof AST_Statement && !is_function(node[prop])) {
throw new Error(prop + " cannot be AST_Statement");
}
}
_validate: function() {
this.body.forEach(function(node) {
if (!(node instanceof AST_Statement)) throw new Error("body must be AST_Statement[]");
- if (node instanceof AST_Function) throw new Error("body cannot contain AST_Function");
+ if (is_function(node)) throw new Error("body cannot contain AST_Function");
});
},
}, AST_BlockScope);
},
_validate: function() {
if (!(this.body instanceof AST_Statement)) throw new Error("body must be AST_Statement");
- if (this.body instanceof AST_Function) throw new Error("body cannot be AST_Function");
+ if (is_function(this.body)) throw new Error("body cannot be AST_Function");
},
}, AST_BlockScope);
if (this.init != null) {
if (!(this.init instanceof AST_Node)) throw new Error("init must be AST_Node");
if (this.init instanceof AST_Statement
- && !(this.init instanceof AST_Definitions || this.init instanceof AST_Function)) {
+ && !(this.init instanceof AST_Definitions || is_function(this.init))) {
throw new Error("init cannot be AST_Statement");
}
}
},
}, AST_Lambda);
+function is_function(node) {
+ return node instanceof AST_AsyncFunction || node instanceof AST_Function;
+}
+
+var AST_AsyncFunction = DEFNODE("AsyncFunction", null, {
+ $documentation: "An asynchronous function expression",
+ _validate: function() {
+ if (this.name != null) {
+ if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
+ }
+ },
+}, AST_Lambda);
+
var AST_Function = DEFNODE("Function", "inlined", {
$documentation: "A function expression",
_validate: function() {
},
}, AST_Lambda);
+var AST_AsyncDefun = DEFNODE("AsyncDefun", null, {
+ $documentation: "An asynchronous function definition",
+ _validate: function() {
+ if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
+ },
+}, AST_Lambda);
+
var AST_Defun = DEFNODE("Defun", "inlined", {
$documentation: "A function definition",
_validate: function() {
must_be_expression(this, "condition");
if (this.alternative != null) {
if (!(this.alternative instanceof AST_Statement)) throw new Error("alternative must be AST_Statement");
- if (this.alternative instanceof AST_Function) throw new error("alternative cannot be AST_Function");
+ if (is_function(this.alternative)) throw new error("alternative cannot be AST_Function");
}
},
}, AST_StatementWithBody);
if (!(node instanceof AST_Node)) throw new Error(prop + " must be AST_Node[]");
if (!allow_hole && node instanceof AST_Hole) throw new Error(prop + " cannot be AST_Hole");
if (!allow_spread && node instanceof AST_Spread) throw new Error(prop + " cannot be AST_Spread");
- if (node instanceof AST_Statement && !(node instanceof AST_Function)) {
+ if (node instanceof AST_Statement && !is_function(node)) {
throw new Error(prop + " cannot contain AST_Statement");
}
});
},
}, AST_Binary);
+var AST_Await = DEFNODE("Await", "expression", {
+ $documentation: "An await expression",
+ $propdoc: {
+ expression: "[AST_Node] expression with Promise to resolve on",
+ },
+ walk: function(visitor) {
+ var node = this;
+ visitor.visit(node, function() {
+ node.expression.walk(visitor);
+ });
+ },
+ _validate: function() {
+ must_be_expression(this, "expression");
+ },
+});
+
/* -----[ LITERALS ]----- */
var AST_Array = DEFNODE("Array", "elements", {
if (value instanceof AST_String) return native_fns.String[name];
if (name == "valueOf") return false;
if (value instanceof AST_Array) return native_fns.Array[name];
- if (value instanceof AST_Function) return native_fns.Function[name];
+ if (value instanceof AST_Lambda) return native_fns.Function[name];
if (value instanceof AST_Object) return native_fns.Object[name];
if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global;
}
lhs.walk(scanner);
}
- def(AST_Accessor, function(tw, descend, compressor) {
- push(tw);
- reset_variables(tw, compressor, this);
- descend();
- pop(tw);
- walk_defuns(tw, this);
- return true;
- });
def(AST_Assign, function(tw, descend, compressor) {
var node = this;
var left = node.left;
pop(tw);
return true;
});
+ def(AST_Lambda, function(tw, descend, compressor) {
+ push(tw);
+ reset_variables(tw, compressor, this);
+ descend();
+ pop(tw);
+ walk_defuns(tw, this);
+ return true;
+ });
def(AST_Switch, function(tw, descend, compressor) {
this.variables.each(function(def) {
reset_def(tw, compressor, def);
function is_iife_call(node) {
if (node.TYPE != "Call") return false;
- return node.expression instanceof AST_Function || is_iife_call(node.expression);
+ return is_function(node.expression) || is_iife_call(node.expression);
}
function is_undeclared_ref(node) {
}
function is_last_node(node, parent) {
+ if (node instanceof AST_Await) return true;
if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right);
if (node instanceof AST_Call) {
var def, fn = node.expression;
if (expr.left instanceof AST_SymbolRef) {
assignments[expr.left.name] = (assignments[expr.left.name] || 0) + 1;
}
+ } else if (expr instanceof AST_Await) {
+ extract_candidates(expr.expression);
} else if (expr instanceof AST_Binary) {
extract_candidates(expr.left);
extract_candidates(expr.right);
var parent = scanner.parent(level);
if (parent instanceof AST_Array) return node;
if (parent instanceof AST_Assign) return node;
+ if (parent instanceof AST_Await) return node;
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Assign) {
return may_throw(parent) ? node : find_stop_unused(parent, level + 1);
}
+ if (parent instanceof AST_Await) return node;
if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
var exp = this.expression;
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
- if (fn instanceof AST_Lambda) {
+ if (fn instanceof AST_Defun || fn instanceof AST_Function) {
if (fn.evaluating) return this;
if (fn.name && fn.name.definition().recursive_refs > 0) return this;
if (this.is_expr_pure(compressor)) return this;
def(AST_Statement, function() {
throw new Error("Cannot negate a statement");
});
+ def(AST_AsyncFunction, function() {
+ return basic_negation(this);
+ });
def(AST_Function, function() {
return basic_negation(this);
});
}
if (node === self) return;
if (scope === self) {
- if (node instanceof AST_Defun) {
+ if (node instanceof AST_AsyncDefun || node instanceof AST_Defun) {
var def = node.name.definition();
if (!drop_funcs && !(def.id in in_use_ids)) {
in_use_ids[def.id] = true;
if (node instanceof AST_Call) calls_to_drop_args.push(node);
if (scope !== self) return;
if (node instanceof AST_Lambda) {
- if (drop_funcs && node !== self && node instanceof AST_Defun) {
+ if (drop_funcs && node !== self && (node instanceof AST_AsyncDefun || node instanceof AST_Defun)) {
var def = node.name.definition();
if (!(def.id in in_use_ids)) {
log(node.name, "Dropping unused function {name}");
return in_list ? List.skip : make_node(AST_EmptyStatement, node);
}
}
- if (node instanceof AST_Function && node.name && drop_fn_name(node.name.definition())) {
+ if (is_function(node) && node.name && drop_fn_name(node.name.definition())) {
unused_fn_names.push(node);
}
if (!(node instanceof AST_Accessor)) {
}
}
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
- var is_func = fn instanceof AST_Lambda;
+ var is_func = fn instanceof AST_Defun || fn instanceof AST_Function;
var stat = is_func && fn.first_statement();
var can_inline = is_func
&& compressor.option("inline")
def.single_use = false;
fixed._squeezed = true;
fixed.single_use = true;
- if (fixed instanceof AST_Defun) {
+ if (fixed instanceof AST_AsyncDefun) {
+ fixed = make_node(AST_AsyncFunction, fixed, fixed);
+ fixed.name = make_node(AST_SymbolLambda, fixed.name, fixed.name);
+ } else if (fixed instanceof AST_Defun) {
fixed = make_node(AST_Function, fixed, fixed);
fixed.name = make_node(AST_SymbolLambda, fixed.name, fixed.name);
}
// a function expression needs parens around it when it's provably
// the first token to appear in a statement.
- PARENS(AST_Function, function(output) {
+ function needs_parens_function(output) {
if (!output.has_parens() && first_in_statement(output)) return true;
if (output.option("webkit")) {
var p = output.parent();
var p = output.parent();
if (p instanceof AST_Call && p.expression === this) return true;
}
- });
+ }
+ PARENS(AST_AsyncFunction, needs_parens_function);
+ PARENS(AST_Function, needs_parens_function);
// same goes for an object literal, because otherwise it would be
// interpreted as a block of code.
var p = output.parent();
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
return p instanceof AST_Array
+ // await (foo, bar)
+ || p instanceof AST_Await
// 1 + (2, 3) + 4 ==> 8
|| p instanceof AST_Binary
// new (foo, bar) or foo(1, (2, 3), 4)
PARENS(AST_Binary, function(output) {
var p = output.parent();
+ // await (foo && bar)
+ if (p instanceof AST_Await) return true;
// this deals with precedence: 3 * (2 + 1)
if (p instanceof AST_Binary) {
var po = p.operator, pp = PRECEDENCE[po];
function needs_parens_assign_cond(self, output) {
var p = output.parent();
+ // await (a = foo)
+ if (p instanceof AST_Await) return true;
// 1 + (a = 2) + 3 → 6, side effect setting a = 2
if (p instanceof AST_Binary) return !(p instanceof AST_Assign);
// (a = func)() —or— new (a = Object)()
});
/* -----[ functions ]----- */
- DEFPRINT(AST_Lambda, function(output, nokeyword) {
- var self = this;
- if (!nokeyword) {
- output.print("function");
- }
+ function print_lambda(self, output) {
if (self.name) {
output.space();
self.name.print(output);
});
output.space();
print_braced(self, output, true);
+ }
+ DEFPRINT(AST_Lambda, function(output) {
+ output.print("function");
+ print_lambda(this, output);
});
+ function print_async(output) {
+ output.print("async");
+ output.space();
+ output.print("function");
+ print_lambda(this, output);
+ }
+ DEFPRINT(AST_AsyncDefun, print_async);
+ DEFPRINT(AST_AsyncFunction, print_async);
/* -----[ jumps ]----- */
function print_jump(kind, prop) {
output.colon();
self.alternative.print(output);
});
+ DEFPRINT(AST_Await, function(output) {
+ output.print("await");
+ output.space();
+ this.expression.print(output);
+ });
/* -----[ literals ]----- */
DEFPRINT(AST_Array, function(output) {
output.print(type);
output.space();
print_property_key(self, output);
- self.value._codegen(output, true);
+ print_lambda(self.value, output);
};
}
DEFPRINT(AST_ObjectGetter, print_accessor("get"));
var KEYWORDS = "break case catch const continue debugger default delete do else finally for function if in instanceof let new return switch throw try typeof var void while with";
var KEYWORDS_ATOM = "false null true";
var RESERVED_WORDS = [
- "abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield",
+ "await abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield",
KEYWORDS_ATOM,
KEYWORDS,
].join(" ");
token : null,
prev : null,
peeked : null,
+ in_async : false,
in_function : 0,
in_directives : true,
in_loop : 0,
return simple_statement();
case "name":
- return is_token(peek(), "punc", ":")
- ? labeled_statement()
- : simple_statement();
+ switch (S.token.value) {
+ case "async":
+ if (is_token(peek(), "keyword", "function")) {
+ next();
+ next();
+ return function_(AST_AsyncDefun);
+ }
+ case "await":
+ if (S.in_async) return simple_statement();
+ default:
+ return is_token(peek(), "punc", ":")
+ ? labeled_statement()
+ : simple_statement();
+ }
case "punc":
switch (S.token.value) {
}
var function_ = function(ctor) {
- var in_statement = ctor === AST_Defun;
- var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
- if (in_statement && !name)
- expect_token("name");
+ var was_async = S.in_async;
+ var name;
+ if (ctor === AST_AsyncDefun) {
+ name = as_symbol(AST_SymbolDefun);
+ S.in_async = true;
+ } else if (ctor === AST_Defun) {
+ name = as_symbol(AST_SymbolDefun);
+ S.in_async = false;
+ } else {
+ S.in_async = ctor === AST_AsyncFunction;
+ name = as_symbol(AST_SymbolLambda, true);
+ }
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
unexpected(prev());
expect("(");
--S.in_function;
S.in_loop = loop;
S.labels = labels;
+ S.in_async = was_async;
return new ctor({
name: name,
argnames: argnames,
}
unexpected();
}
- if (is("keyword", "function")) {
+ var ctor;
+ if (is("name", "async") && is_token(peek(), "keyword", "function")) {
next();
- var func = function_(AST_Function);
+ ctor = AST_AsyncFunction;
+ } else if (is("keyword", "function")) {
+ ctor = AST_Function;
+ }
+ if (ctor) {
+ next();
+ var func = function_(ctor);
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
function _make_symbol(type, token) {
var name = token.value;
+ if (name === "await" && S.in_async) unexpected(token);
return new (name === "this" ? AST_This : type)({
name: "" + name,
start: token,
return expr;
};
- var maybe_unary = function(allow_calls) {
+ function maybe_unary() {
var start = S.token;
if (is("operator") && UNARY_PREFIX[start.value]) {
next();
handle_regexp();
- var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(allow_calls));
+ var ex = make_unary(AST_UnaryPrefix, start, maybe_await());
ex.start = start;
ex.end = prev();
return ex;
}
- var val = expr_atom(allow_calls);
+ var val = expr_atom(true);
while (is("operator") && UNARY_POSTFIX[S.token.value] && !has_newline_before(S.token)) {
val = make_unary(AST_UnaryPostfix, S.token, val);
val.start = start;
next();
}
return val;
- };
+ }
function make_unary(ctor, token, expr) {
var op = token.value;
return new ctor({ operator: op, expression: expr });
}
+ function maybe_await() {
+ var start = S.token;
+ if (!(S.in_async && is("name", "await"))) return maybe_unary();
+ next();
+ return new AST_Await({
+ start: start,
+ expression: maybe_await(),
+ end: prev(),
+ });
+ }
+
var expr_op = function(left, min_prec, no_in) {
var op = is("operator") ? S.token.value : null;
if (op == "in" && no_in) op = null;
var prec = op != null ? PRECEDENCE[op] : null;
if (prec != null && prec > min_prec) {
next();
- var right = expr_op(maybe_unary(true), prec, no_in);
+ var right = expr_op(maybe_await(), prec, no_in);
return expr_op(new AST_Binary({
start : left.start,
left : left,
};
function expr_ops(no_in) {
- return expr_op(maybe_unary(true), 0, no_in);
+ return expr_op(maybe_await(), 0, no_in);
}
var maybe_conditional = function(no_in) {
var next_def_id = 0;
var scope = self.parent_scope = null;
var tw = new TreeWalker(function(node, descend) {
- if (node instanceof AST_Defun) {
+ if (node instanceof AST_AsyncDefun || node instanceof AST_Defun) {
node.name.walk(tw);
walk_scope(function() {
node.argnames.forEach(function(argname) {
DEF(AST_Sequence, function(self, tw) {
self.expressions = do_list(self.expressions, tw);
});
+ DEF(AST_Await, function(self, tw) {
+ self.expression = self.expression.transform(tw);
+ });
DEF(AST_Dot, function(self, tw) {
self.expression = self.expression.transform(tw);
});
--- /dev/null
+await_await: {
+ input: {
+ (async function() {
+ console.log("PASS");
+ await await 42;
+ })();
+ }
+ expect: {
+ (async function() {
+ console.log("PASS");
+ await await 42;
+ })();
+ }
+ expect_stdout: "PASS"
+ node_version: ">=8"
+}
+
+defun_name: {
+ input: {
+ async function await() {
+ console.log("PASS");
+ }
+ await();
+ }
+ expect: {
+ async function await() {
+ console.log("PASS");
+ }
+ await();
+ }
+ expect_stdout: "PASS"
+ node_version: ">=8"
+}
+
+nested_await: {
+ input: {
+ (async function() {
+ console.log(function(await) {
+ return await;
+ }("PASS"));
+ })();
+ }
+ expect: {
+ (async function() {
+ console.log(function(await) {
+ return await;
+ }("PASS"));
+ })();
+ }
+ expect_stdout: "PASS"
+ node_version: ">=8"
+}
+
+reduce_single_use_defun: {
+ options = {
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ async function f(a) {
+ console.log(a);
+ }
+ f("PASS");
+ }
+ expect: {
+ (async function(a) {
+ console.log(a);
+ })("PASS");
+ }
+ expect_stdout: "PASS"
+ node_version: ">=8"
+}
+
+dont_inline: {
+ options = {
+ inline: true,
+ }
+ input: {
+ (async function() {
+ A;
+ })().catch(function() {});
+ console.log("PASS");
+ }
+ expect: {
+ (async function() {
+ A;
+ })().catch(function() {});
+ console.log("PASS");
+ }
+ expect_stdout: "PASS"
+ node_version: ">=8"
+}
+
+evaluate: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ var a = async function() {}();
+ console.log(typeof a);
+ }
+ expect: {
+ var a = async function() {}();
+ console.log(typeof a);
+ }
+ expect_stdout: "object"
+ node_version: ">=8"
+}
+
+negate: {
+ options = {
+ side_effects: true,
+ }
+ input: {
+ console && async function() {} && console.log("PASS");
+ }
+ expect: {
+ console && async function() {} && console.log("PASS");
+ }
+ expect_stdout: "PASS"
+ node_version: ">=8"
+}
+
+negate_iife: {
+ options = {
+ negate_iife: true,
+ }
+ input: {
+ (async function() {
+ console.log("PASS");
+ })();
+ }
+ expect: {
+ !async function() {
+ console.log("PASS");
+ }();
+ }
+ expect_stdout: "PASS"
+ node_version: ">=8"
+}
+
+collapse_vars_1: {
+ options = {
+ collapse_vars: true,
+ }
+ input: {
+ var a = "FAIL";
+ (async function() {
+ a = "PASS";
+ await 42;
+ return "PASS";
+ })();
+ console.log(a);
+ }
+ expect: {
+ var a = "FAIL";
+ (async function() {
+ a = "PASS";
+ await 42;
+ return "PASS";
+ })();
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=8"
+}
+
+collapse_vars_2: {
+ options = {
+ collapse_vars: true,
+ }
+ input: {
+ var a = "FAIL";
+ (async function() {
+ await (a = "PASS");
+ return "PASS";
+ })();
+ console.log(a);
+ }
+ expect: {
+ var a = "FAIL";
+ (async function() {
+ await (a = "PASS");
+ return "PASS";
+ })();
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=8"
+}
+
+collapse_vars_3: {
+ options = {
+ collapse_vars: true,
+ }
+ input: {
+ var a = "FAIL";
+ (async function() {
+ await (a = "PASS", 42);
+ return "PASS";
+ })();
+ console.log(a);
+ }
+ expect: {
+ var a = "FAIL";
+ (async function() {
+ await (a = "PASS", 42);
+ return "PASS";
+ })();
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=8"
+}
--- /dev/null
+var assert = require("assert");
+var UglifyJS = require("../node");
+
+describe("async", function() {
+ it("Should reject `await` as symbol name within async functions only", function() {
+ [
+ "function await() {}",
+ "function(await) {}",
+ "function() { await; }",
+ "function() { await:{} }",
+ "function() { var await; }",
+ "function() { function await() {} }",
+ "function() { try {} catch (await) {} }",
+ ].forEach(function(code) {
+ UglifyJS.parse("(" + code + ")();");
+ assert.throws(function() {
+ UglifyJS.parse("(async " + code + ")();");
+ }, function(e) {
+ return e instanceof UglifyJS.JS_Parse_Error;
+ }, code);
+ });
+ });
+});
}
function is_statement(node) {
- return node instanceof U.AST_Statement && !(node instanceof U.AST_Function);
+ return node instanceof U.AST_Statement && !(node instanceof U.AST_AsyncFunction || node instanceof U.AST_Function);
}
function merge_sequence(array, node) {
function safe_log(arg, level) {
if (arg) switch (typeof arg) {
- case "function":
+ case "function":
return arg.toString();
- case "object":
+ case "object":
if (arg === global) return "[object global]";
if (/Error$/.test(arg.name)) return arg.toString();
+ if (typeof arg.then == "function") return "[object Promise]";
arg.constructor.toString();
if (level--) for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key);
"arguments",
"Math",
"parseInt",
+ "async",
+ "await",
];
var INITIAL_NAMES_LEN = VAR_NAMES.length;
var avoid_vars = [];
var block_vars = [];
var unique_vars = [];
+var async = false;
var loops = 0;
var funcs = 0;
var called = Object.create(null);
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
block_vars.length = 0;
unique_vars.length = 0;
+ async = false;
loops = 0;
funcs = 0;
called = Object.create(null);
return args.join(", ");
}
-function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames, maybe, dontStore) {
+function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames, was_async) {
var avoid = [];
var len = unique_vars.length;
var pairs = createPairs(recurmax);
function createAssignmentValue(recurmax) {
var current = VAR_NAMES;
VAR_NAMES = (varNames || VAR_NAMES).slice();
+ var save_async = async;
+ if (was_async != null) async = was_async;
var value = varNames && rng(2) ? createValue() : createExpression(recurmax, noComma, stmtDepth, canThrow);
+ async = save_async;
VAR_NAMES = current;
return value;
}
}
if (i < m) {
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
- var name = createVarName(maybe, dontStore);
+ var name = createVarName(MANDATORY);
unique_vars.length -= 6;
avoid.push(name);
unique_vars.push(name);
break;
default:
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
- var name = createVarName(maybe, dontStore);
+ var name = createVarName(MANDATORY);
unique_vars.length -= 6;
avoid.push(name);
unique_vars.push(name);
}
}
+function makeFunction(name) {
+ return (async ? "async function " : "function ") + name;
+}
+
function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
if (--recurmax < 0) { return ";"; }
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
var s = [];
var name, args;
var varNames = VAR_NAMES.slice();
+ var save_async = async;
+ async = rng(50) == 0;
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
if (allowDefun || rng(5) > 0) {
name = "f" + funcs++;
var params;
if ((!allowDefun || !(name in called)) && rng(2)) {
called[name] = false;
- var pairs = createAssignmentPairs(recurmax, COMMA_OK, stmtDepth, canThrow, varNames, MANDATORY);
+ var pairs = createAssignmentPairs(recurmax, COMMA_OK, stmtDepth, canThrow, varNames, save_async);
params = pairs.names.join(", ");
args = pairs.values.join(", ");
} else {
params = createParams();
}
- s.push("function " + name + "(" + params + "){", strictMode());
+ s.push(makeFunction(name) + "(" + params + "){", strictMode());
s.push(defns());
if (rng(5) === 0) {
// functions with functions. lower the recursion to prevent a mess.
s.push("}", "");
s = filterDirective(s).join("\n");
});
+ async = save_async;
VAR_NAMES = varNames;
if (!allowDefun) {
return "switch (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") { " + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
case STMT_VAR:
if (!rng(20)) {
- var pairs = createAssignmentPairs(recurmax, NO_COMMA, stmtDepth, canThrow, null, MANDATORY);
+ var pairs = createAssignmentPairs(recurmax, NO_COMMA, stmtDepth, canThrow);
return "var " + pairs.names.map(function(name, index) {
return index in pairs.values ? name + " = " + pairs.values[index] : name;
}).join(", ") + ";";
case 2:
return "((c = c + 1) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; // c only gets incremented
default:
- return "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")";
+ var expr = "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")";
+ return async && rng(50) == 0 ? "(await" + expr + ")" : expr;
}
}
case p++:
case p++:
var nameLenBefore = VAR_NAMES.length;
+ var save_async = async;
+ async = rng(50) == 0;
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();
switch (rng(5)) {
case 0:
s.push(
- "(function " + name + "(){",
+ "(" + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
rng(2) == 0 ? "})" : "})()"
break;
case 1:
s.push(
- "+function " + name + "(){",
+ "+" + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"}()"
break;
case 2:
s.push(
- "!function " + name + "(){",
+ "!" + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"}()"
break;
case 3:
s.push(
- "void function " + name + "(){",
+ "void " + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"}()"
);
break;
default:
+ async = false;
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
var instantiate = rng(4) ? "new " : "";
s.push(
});
break;
}
+ async = save_async;
VAR_NAMES.length = nameLenBefore;
return filterDirective(s).join("\n");
case p++:
do {
name = VAR_NAMES[rng(VAR_NAMES.length)];
if (suffix) name += "_" + suffix;
- } while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0);
+ } while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0 || async && name == "await");
if (suffix && !dontStore) VAR_NAMES.push(name);
return name;
}