- `varify` (default: `true`) -- convert block-scoped declaractions into `var`
whenever safe to do so
+- `yields` (default: `true`) -- apply optimizations to `yield` expressions
+
## Mangle options
- `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
walk: function(visitor) {
visitor.visit(this);
},
- _validate: noop,
+ _validate: function() {
+ if (this.TYPE == "Node") throw new Error("should not instantiate AST_Node");
+ },
validate: function() {
var ctor = this.CTOR;
do {
var AST_Statement = DEFNODE("Statement", null, {
$documentation: "Base class of all statements",
+ _validate: function() {
+ if (this.TYPE == "Statement") throw new Error("should not instantiate AST_Statement");
+ },
});
var AST_Debugger = DEFNODE("Debugger", null, {
if (value instanceof AST_Destructured) throw new Error(prop + " cannot " + multiple + " AST_Destructured");
if (value instanceof AST_Hole && !allow_hole) throw new Error(prop + " cannot " + multiple + " AST_Hole");
if (value instanceof AST_Spread && !allow_spread) throw new Error(prop + " cannot " + multiple + " AST_Spread");
- if (value instanceof AST_Statement && !is_function(value)) {
+ if (value instanceof AST_Statement && !(value instanceof AST_LambdaExpression)) {
throw new Error(prop + " cannot " + multiple + " AST_Statement");
}
if (value instanceof AST_SymbolDeclaration) {
return this.parent_scope.resolve();
},
_validate: function() {
+ if (this.TYPE == "BlockScope") throw new Error("should not instantiate AST_BlockScope");
if (this.parent_scope == null) return;
if (!(this.parent_scope instanceof AST_BlockScope)) throw new Error("parent_scope must be AST_BlockScope");
if (!(this.resolve() instanceof AST_Scope)) throw new Error("must be contained within AST_Scope");
});
},
_validate: function() {
+ if (this.TYPE == "Block") throw new Error("should not instantiate AST_Block");
this.body.forEach(function(node) {
if (!(node instanceof AST_Statement)) throw new Error("body must be AST_Statement[]");
- if (is_function(node)) throw new Error("body cannot contain AST_Function");
+ if (node instanceof AST_LambdaExpression) throw new Error("body cannot contain AST_LambdaExpression");
});
},
}, AST_BlockScope);
body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
},
_validate: function() {
+ if (this.TYPE == "StatementWithBody") throw new Error("should not instantiate AST_StatementWithBody");
if (!(this.body instanceof AST_Statement)) throw new Error("body must be AST_Statement");
- if (is_function(this.body)) throw new Error("body cannot be AST_Function");
+ if (this.body instanceof AST_LambdaExpression) throw new Error("body cannot be AST_LambdaExpression");
},
}, AST_BlockScope);
}, AST_StatementWithBody);
var AST_IterationStatement = DEFNODE("IterationStatement", null, {
- $documentation: "Internal class. All loops inherit from it."
+ $documentation: "Internal class. All loops inherit from it.",
+ _validate: function() {
+ if (this.TYPE == "IterationStatement") throw new Error("should not instantiate AST_IterationStatement");
+ },
}, AST_StatementWithBody);
var AST_DWLoop = DEFNODE("DWLoop", "condition", {
condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
},
_validate: function() {
+ if (this.TYPE == "DWLoop") throw new Error("should not instantiate AST_DWLoop");
must_be_expression(this, "condition");
},
}, AST_IterationStatement);
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 || is_function(this.init))) {
+ && !(this.init instanceof AST_Definitions || this.init instanceof AST_LambdaExpression)) {
throw new Error("init cannot be AST_Statement");
}
}
return this.uses_eval || this.uses_with;
},
resolve: return_this,
+ _validate: function() {
+ if (this.TYPE == "Scope") throw new Error("should not instantiate AST_Scope");
+ },
}, AST_Block);
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
$documentation: "Base class for functions",
$propdoc: {
argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
+ length_read: "[boolean/S] whether length property of this function is accessed",
rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent",
- uses_arguments: "[boolean/S] tells whether this function accesses the arguments array",
+ uses_arguments: "[boolean/S] whether this function accesses the arguments array",
},
each_argname: function(visit) {
var tw = new TreeWalker(function(node) {
});
},
_validate: function() {
+ if (this.TYPE == "Lambda") throw new Error("should not instantiate AST_Lambda");
this.argnames.forEach(function(node) {
validate_destructured(node, function(node) {
if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]");
},
}, AST_Lambda);
+var AST_LambdaExpression = DEFNODE("LambdaExpression", "inlined", {
+ $documentation: "Base class for function expressions",
+ $propdoc: {
+ inlined: "[boolean/S] whether this function has been inlined",
+ },
+ _validate: function() {
+ if (this.TYPE == "LambdaExpression") throw new Error("should not instantiate AST_LambdaExpression");
+ },
+}, AST_Lambda);
+
function is_arrow(node) {
- return node instanceof AST_AsyncArrow || node instanceof AST_Arrow;
+ return node instanceof AST_Arrow || node instanceof AST_AsyncArrow;
+}
+
+function is_async(node) {
+ return node instanceof AST_AsyncArrow
+ || node instanceof AST_AsyncDefun
+ || node instanceof AST_AsyncFunction
+ || node instanceof AST_AsyncGeneratorDefun
+ || node instanceof AST_AsyncGeneratorFunction;
}
-function is_function(node) {
- return is_arrow(node) || node instanceof AST_AsyncFunction || node instanceof AST_Function;
+function is_generator(node) {
+ return node instanceof AST_AsyncGeneratorDefun
+ || node instanceof AST_AsyncGeneratorFunction
+ || node instanceof AST_GeneratorDefun
+ || node instanceof AST_GeneratorFunction;
}
function walk_lambda(node, tw) {
}
}
-var AST_Arrow = DEFNODE("Arrow", "inlined value", {
+var AST_Arrow = DEFNODE("Arrow", "value", {
$documentation: "An arrow function expression",
$propdoc: {
value: "[AST_Node?] simple return expression, or null if using function body.",
if (this.body.length) throw new Error("body must be empty if value exists");
}
},
-}, AST_Lambda);
+}, AST_LambdaExpression);
-function is_async(node) {
- return node instanceof AST_AsyncArrow || node instanceof AST_AsyncDefun || node instanceof AST_AsyncFunction;
-}
-
-var AST_AsyncArrow = DEFNODE("AsyncArrow", "inlined value", {
+var AST_AsyncArrow = DEFNODE("AsyncArrow", "value", {
$documentation: "An asynchronous arrow function expression",
$propdoc: {
value: "[AST_Node?] simple return expression, or null if using function body.",
if (this.body.length) throw new Error("body must be empty if value exists");
}
},
-}, AST_Lambda);
+}, AST_LambdaExpression);
-var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined name", {
+var AST_AsyncFunction = DEFNODE("AsyncFunction", "name", {
$documentation: "An asynchronous function expression",
$propdoc: {
name: "[AST_SymbolLambda?] the name of this function",
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
}
},
-}, AST_Lambda);
+}, AST_LambdaExpression);
-var AST_Function = DEFNODE("Function", "inlined name", {
- $documentation: "A function expression",
+var AST_AsyncGeneratorFunction = DEFNODE("AsyncGeneratorFunction", "name", {
+ $documentation: "An asynchronous generator function expression",
$propdoc: {
name: "[AST_SymbolLambda?] the name of this function",
},
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
}
},
-}, AST_Lambda);
+}, AST_LambdaExpression);
-function is_defun(node) {
- return node instanceof AST_AsyncDefun || node instanceof AST_Defun;
-}
+var AST_Function = DEFNODE("Function", "name", {
+ $documentation: "A function expression",
+ $propdoc: {
+ name: "[AST_SymbolLambda?] the name of this function",
+ },
+ _validate: function() {
+ if (this.name != null) {
+ if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
+ }
+ },
+}, AST_LambdaExpression);
-var AST_AsyncDefun = DEFNODE("AsyncDefun", "inlined name", {
- $documentation: "An asynchronous function definition",
+var AST_GeneratorFunction = DEFNODE("GeneratorFunction", "name", {
+ $documentation: "A generator function expression",
$propdoc: {
- name: "[AST_SymbolDefun] the name of this function",
+ name: "[AST_SymbolLambda?] the name of this function",
},
_validate: function() {
- if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
+ if (this.name != null) {
+ if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
+ }
},
-}, AST_Lambda);
+}, AST_LambdaExpression);
-var AST_Defun = DEFNODE("Defun", "inlined name", {
- $documentation: "A function definition",
+var AST_LambdaDefinition = DEFNODE("LambdaDefinition", "inlined name", {
+ $documentation: "Base class for function definitions",
$propdoc: {
+ inlined: "[boolean/S] whether this function has been inlined",
name: "[AST_SymbolDefun] the name of this function",
},
_validate: function() {
+ if (this.TYPE == "LambdaDefinition") throw new Error("should not instantiate AST_LambdaDefinition");
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
},
}, AST_Lambda);
+var AST_AsyncDefun = DEFNODE("AsyncDefun", null, {
+ $documentation: "An asynchronous function definition",
+}, AST_LambdaDefinition);
+
+var AST_AsyncGeneratorDefun = DEFNODE("AsyncGeneratorDefun", null, {
+ $documentation: "An asynchronous generator function definition",
+}, AST_LambdaDefinition);
+
+var AST_Defun = DEFNODE("Defun", null, {
+ $documentation: "A function definition",
+}, AST_LambdaDefinition);
+
+var AST_GeneratorDefun = DEFNODE("GeneratorDefun", null, {
+ $documentation: "A generator function definition",
+}, AST_LambdaDefinition);
+
/* -----[ JUMPS ]----- */
var AST_Jump = DEFNODE("Jump", null, {
- $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
+ $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)",
+ _validate: function() {
+ if (this.TYPE == "Jump") throw new Error("should not instantiate AST_Jump");
+ },
}, AST_Statement);
var AST_Exit = DEFNODE("Exit", "value", {
visitor.visit(node, function() {
if (node.value) node.value.walk(visitor);
});
- }
+ },
+ _validate: function() {
+ if (this.TYPE == "Exit") throw new Error("should not instantiate AST_Exit");
+ },
}, AST_Jump);
var AST_Return = DEFNODE("Return", null, {
});
},
_validate: function() {
+ if (this.TYPE == "LoopControl") throw new Error("should not instantiate AST_LoopControl");
if (this.label != null) {
if (!(this.label instanceof AST_LabelRef)) throw new Error("label must be AST_LabelRef");
}
must_be_expression(this, "condition");
if (this.alternative != null) {
if (!(this.alternative instanceof AST_Statement)) throw new Error("alternative must be AST_Statement");
- if (is_function(this.alternative)) throw new error("alternative cannot be AST_Function");
+ if (this.alternative instanceof AST_LambdaExpression) throw new error("alternative cannot be AST_LambdaExpression");
}
},
}, AST_StatementWithBody);
var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
$documentation: "Base class for `switch` branches",
+ _validate: function() {
+ if (this.TYPE == "SwitchBranch") throw new Error("should not instantiate AST_SwitchBranch");
+ },
}, AST_Block);
var AST_Default = DEFNODE("Default", null, {
});
},
_validate: function() {
+ if (this.TYPE == "Definitions") throw new Error("should not instantiate AST_Definitions");
if (this.definitions.length < 1) throw new Error("must have at least one definition");
},
}, AST_Statement);
return p;
},
_validate: function() {
+ if (this.TYPE == "PropAccess") throw new Error("should not instantiate AST_PropAccess");
must_be_expression(this, "expression");
},
});
});
},
_validate: function() {
+ if (this.TYPE == "Unary") throw new Error("should not instantiate AST_Unary");
if (typeof this.operator != "string") throw new Error("operator must be string");
must_be_expression(this, "expression");
},
},
});
+var AST_Yield = DEFNODE("Yield", "expression nested", {
+ $documentation: "A yield expression",
+ $propdoc: {
+ expression: "[AST_Node?] return value for iterator, or null if undefined",
+ nested: "[boolean] whether to iterate over expression as generator",
+ },
+ walk: function(visitor) {
+ var node = this;
+ visitor.visit(node, function() {
+ if (node.expression) node.expression.walk(visitor);
+ });
+ },
+ _validate: function() {
+ if (this.expression != null) {
+ must_be_expression(this, "expression");
+ } else if (this.nested) {
+ throw new Error("yield* must contain expression");
+ }
+ },
+});
+
/* -----[ LITERALS ]----- */
var AST_Array = DEFNODE("Array", "elements", {
$propdoc: {
rest: "[(AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)?] rest parameter, or null if absent",
},
+ _validate: function() {
+ if (this.TYPE == "Destructured") throw new Error("should not instantiate AST_Destructured");
+ },
});
function validate_destructured(node, check, allow_default) {
});
},
_validate: function() {
+ if (this.TYPE == "ObjectProperty") throw new Error("should not instantiate AST_ObjectProperty");
if (typeof this.key != "string") {
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
must_be_expression(this, "key");
thedef: "[SymbolDef/S] the definition of this symbol"
},
_validate: function() {
+ if (this.TYPE == "Symbol") throw new Error("should not instantiate AST_Symbol");
if (typeof this.name != "string") throw new Error("name must be string");
},
});
var AST_Constant = DEFNODE("Constant", null, {
$documentation: "Base class for all constants",
+ _validate: function() {
+ if (this.TYPE == "Constant") throw new Error("should not instantiate AST_Constant");
+ },
});
var AST_String = DEFNODE("String", "value quote", {
var AST_Atom = DEFNODE("Atom", null, {
$documentation: "Base class for atoms",
+ _validate: function() {
+ if (this.TYPE == "Atom") throw new Error("should not instantiate AST_Atom");
+ },
}, AST_Constant);
var AST_Null = DEFNODE("Null", null, {
var AST_Boolean = DEFNODE("Boolean", null, {
$documentation: "Base class for booleans",
+ _validate: function() {
+ if (this.TYPE == "Boolean") throw new Error("should not instantiate AST_Boolean");
+ },
}, AST_Atom);
var AST_False = DEFNODE("False", null, {
unsafe_undefined: false,
unused : !false_by_default,
varify : !false_by_default,
+ yields : !false_by_default,
}, true);
var evaluate = this.options["evaluate"];
this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
return !immutable
&& parent.expression === node
&& !parent.is_expr_pure(compressor)
- && (!is_function(value) || !(parent instanceof AST_New) && value.contains_this());
+ && (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this());
}
if (parent instanceof AST_ForIn) return parent.init === node;
if (parent instanceof AST_ObjectKeyVal) {
def.fixed = !def.const_redefs
&& !def.scope.pinned()
&& !compressor.exposed(def)
- && !(is_function(def.init) && def.init !== def.scope)
+ && !(def.init instanceof AST_LambdaExpression && def.init !== def.scope)
&& def.init;
- if (is_defun(def.fixed) && !all(def.references, function(ref) {
+ if (def.fixed instanceof AST_LambdaDefinition && !all(def.references, function(ref) {
var scope = ref.scope.resolve();
do {
if (def.scope === scope) return true;
- } while (is_function(scope) && (scope = scope.parent_scope.resolve()));
+ } while (scope instanceof AST_LambdaExpression && (scope = scope.parent_scope.resolve()));
})) {
tw.defun_ids[def.id] = false;
}
scope.may_call_this = noop;
if (!scope.contains_this()) return;
scope.functions.each(function(def) {
- if (is_defun(def.init) && !(def.id in tw.defun_ids)) {
+ if (def.init instanceof AST_LambdaDefinition && !(def.id in tw.defun_ids)) {
tw.defun_ids[def.id] = false;
}
});
function walk_defuns(tw, scope) {
scope.functions.each(function(def) {
- if (is_defun(def.init) && !tw.defun_visited[def.id]) {
+ if (def.init instanceof AST_LambdaDefinition && !tw.defun_visited[def.id]) {
tw.defun_ids[def.id] = tw.safe_ids;
def.init.walk(tw);
}
}
return !safe.assign || safe.assign === tw.safe_ids;
}
- return is_defun(def.fixed);
+ return def.fixed instanceof AST_LambdaDefinition;
}
function safe_to_assign(tw, def, declare) {
lhs.walk(scanner);
}
- function reduce_defun(tw, descend, compressor) {
- var id = this.name.definition().id;
- if (tw.defun_visited[id]) return true;
- if (tw.defun_ids[id] !== tw.safe_ids) return true;
- tw.defun_visited[id] = true;
- this.inlined = false;
- push(tw);
- reset_variables(tw, compressor, this);
- descend();
- pop(tw);
- walk_defuns(tw, this);
- return true;
- }
-
function reduce_iife(tw, descend, compressor) {
var fn = this;
fn.inlined = false;
var iife = tw.parent();
- var hit = is_async(fn);
+ var hit = is_async(fn) || is_generator(fn);
var aborts = false;
fn.walk(new TreeWalker(function(node) {
if (hit) return aborts = true;
}
}
});
- def(AST_AsyncDefun, reduce_defun);
def(AST_Binary, function(tw) {
if (!lazy_op[this.operator]) return;
this.left.walk(tw);
def(AST_Call, function(tw, descend) {
tw.find_parent(AST_Scope).may_call_this();
var exp = this.expression;
- if (is_function(exp)) {
+ if (exp instanceof AST_LambdaExpression) {
var iife = !exp.name;
this.args.forEach(function(arg) {
arg.walk(tw);
} else if (exp instanceof AST_SymbolRef) {
var def = exp.definition();
if (this.TYPE == "Call" && tw.in_boolean_context()) def.bool_fn++;
- if (!is_defun(def.fixed)) return;
+ if (!(def.fixed instanceof AST_LambdaDefinition)) return;
var defun = mark_defun(tw, def);
if (!defun) return;
descend();
pop(tw);
return true;
});
- def(AST_Defun, reduce_defun);
def(AST_Do, function(tw) {
var saved_loop = tw.in_loop;
tw.in_loop = this;
walk_defuns(tw, fn);
return true;
});
+ def(AST_LambdaDefinition, function(tw, descend, compressor) {
+ var id = this.name.definition().id;
+ if (tw.defun_visited[id]) return true;
+ if (tw.defun_ids[id] !== tw.safe_ids) return true;
+ tw.defun_visited[id] = true;
+ this.inlined = false;
+ 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);
}
if (!this.fixed) this.fixed = d.fixed;
var parent;
- if (is_defun(d.fixed) && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
+ if (d.fixed instanceof AST_LambdaDefinition
+ && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
var defun = mark_defun(tw, d);
if (defun) defun.walk(tw);
}
function is_iife_call(node) {
if (node.TYPE != "Call") return false;
- var exp = node.expression;
- return exp instanceof AST_AsyncFunction || exp instanceof AST_Function || is_iife_call(exp);
+ do {
+ node = node.expression;
+ } while (node instanceof AST_PropAccess);
+ return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
}
function is_undeclared_ref(node) {
if (node instanceof AST_Call) {
if (!(lhs instanceof AST_PropAccess)) return false;
if (!lhs.equivalent_to(node.expression)) return false;
- return !(is_function(rvalue) && !rvalue.contains_this());
+ return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this());
}
if (node instanceof AST_Debugger) return true;
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
&& (lvalues.has(node.name) || side_effects && may_modify(node));
}, true);
}
+ if (node instanceof AST_Yield) return true;
var sym = is_lhs(node.left, node);
if (!sym) return false;
if (sym instanceof AST_PropAccess) return true;
function extract_args() {
var iife, fn = compressor.self();
- if (is_function(fn)
+ if (fn instanceof AST_LambdaExpression
+ && !is_generator(fn)
&& !fn.name
&& !fn.uses_arguments
&& !fn.pinned()
if (modify_toplevel) return;
var exp = node.expression;
if (exp instanceof AST_PropAccess) return;
- if (is_function(exp) && !exp.contains_this()) return;
+ if (exp instanceof AST_LambdaExpression && !exp.contains_this()) return;
modify_toplevel = true;
} else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
if (node === lhs && !(expr instanceof AST_Unary)) {
}
function extract_declarations_from_unreachable_code(compressor, stat, target) {
- if (!(stat instanceof AST_Definitions || is_defun(stat))) {
+ if (!(stat instanceof AST_Definitions || stat instanceof AST_LambdaDefinition)) {
AST_Node.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
}
var block;
}
return true;
}
- if (is_defun(node)) {
+ if (node instanceof AST_LambdaDefinition) {
push(node);
return true;
}
if (node instanceof AST_Call) {
var exp = node.expression;
var tail = exp.tail_node();
- if (!is_function(tail)) return;
+ if (!(tail instanceof AST_LambdaExpression)) return;
if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) {
node.walk(tw);
});
}
if (node === self) return;
if (scope === self) {
- if (is_defun(node)) {
+ if (node instanceof AST_LambdaDefinition) {
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 && is_defun(node)) {
+ if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) {
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 (is_function(node) && node.name && drop_fn_name(node.name.definition())) {
+ if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) {
unused_fn_names.push(node);
}
if (!(node instanceof AST_Accessor)) {
&& node instanceof AST_Var
&& var_defs[sym.id] == 1
&& sym.assignments == 0
- && (value instanceof AST_AsyncFunction || value instanceof AST_Function)
+ && value instanceof AST_LambdaExpression
+ && !is_arrow(value)
&& assigned_once(value, sym.references)
&& can_declare_defun()
&& can_rename(value, def.name.name)) {
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
- var defun = make_node(value instanceof AST_Function ? AST_Defun : AST_AsyncDefun, def, value);
+ var ctor;
+ switch (value.CTOR) {
+ case AST_AsyncFunction:
+ ctor = AST_AsyncDefun;
+ break;
+ case AST_AsyncGeneratorFunction:
+ ctor = AST_AsyncGeneratorDefun;
+ break;
+ case AST_Function:
+ ctor = AST_Defun;
+ break;
+ case AST_GeneratorFunction:
+ ctor = AST_GeneratorDefun;
+ break;
+ }
+ var defun = make_node(ctor, def, value);
defun.name = make_node(AST_SymbolDefun, def.name, def.name);
var name_def = def.name.scope.resolve().def_function(defun.name);
if (old_def) old_def.forEach(function(node) {
if (old_def.assignments > 0) return false;
if (old_def.name == name) return true;
if (name == "await" && is_async(fn)) return false;
+ if (name == "yield" && is_generator(fn)) return false;
return all(old_def.references, function(ref) {
return ref.scope.find_variable(name) === sym;
});
})) return this;
return make_sequence(this, values.map(convert_spread));
});
- def(AST_Arrow, return_null);
def(AST_Assign, function(compressor) {
var left = this.left;
if (left instanceof AST_PropAccess) {
}
return this;
});
- def(AST_AsyncArrow, return_null);
- def(AST_AsyncFunction, return_null);
def(AST_Await, function(compressor) {
if (!compressor.option("awaits")) return this;
var exp = this.expression.drop_side_effect_free(compressor);
def(AST_Function, function(compressor) {
return fn_name_unused(this, compressor) ? null : this;
});
+ def(AST_LambdaExpression, return_null);
def(AST_Object, function(compressor, first_in_statement) {
var exprs = [];
this.properties.forEach(function(prop) {
var exprs = [];
for (var i = 0; i < stat.body.length; i++) {
var line = stat.body[i];
- if (is_defun(line)) {
+ if (line instanceof AST_LambdaDefinition) {
defuns.push(line);
} else if (line instanceof AST_EmptyStatement) {
continue;
}
return exprs;
}
- if (is_defun(stat)) {
+ if (stat instanceof AST_LambdaDefinition) {
defuns.push(stat);
return [];
}
}
}
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
- var is_func = fn instanceof AST_Lambda && (!is_async(fn)
- || compressor.option("awaits") && compressor.parent() instanceof AST_Await);
+ var parent = compressor.parent(), current = compressor.self();
+ var is_func = fn instanceof AST_Lambda
+ && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await)
+ && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested);
var stat = is_func && fn.first_statement();
var has_default = 0, has_destructured = false;
var has_spread = !all(self.args, function(arg) {
if (can_inline
&& !fn.uses_arguments
&& !fn.pinned()
- && !(fn.name && is_function(fn))
+ && !(fn.name && fn instanceof AST_LambdaExpression)
&& (exp === fn || !recursive_ref(compressor, def = exp.definition())
&& fn.is_constant_expression(find_scope(compressor)))
&& !has_spread
return arg;
})).optimize(compressor);
fn.inlined = save_inlined;
- node = maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
+ node = maintain_this_binding(compressor, parent, current, node);
if (replacing || best_of_expression(node, self) === node) {
refs.forEach(function(ref) {
var def = ref.definition();
fn._squeezed = true;
if (exp !== fn) fn.parent_scope = exp.scope;
var node = make_sequence(self, flatten_fn()).optimize(compressor);
- return maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
+ return maintain_this_binding(compressor, parent, current, node);
}
}
if (compressor.option("side_effects")
}
}
}
- if (compressor.option("negate_iife")
- && compressor.parent() instanceof AST_SimpleStatement
- && is_iife_call(self)) {
+ if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) {
return self.negate(compressor, true);
}
return try_evaluate(compressor, self);
if (node === fn) return;
if (is_arrow(node)) {
for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
- } else if (is_defun(node) && node.name.name == "await") {
+ } else if (node instanceof AST_LambdaDefinition && node.name.name == "await") {
safe = false;
}
return true;
in_order = null;
return;
}
- if (is_defun(def.init)) return abort = true;
+ if (def.init instanceof AST_LambdaDefinition) return abort = true;
if (is_lhs(node, this.parent())) return abort = true;
var index = resolve_index(def);
if (!(begin < index)) begin = index;
function can_inject_vars(defined, used, safe_to_inject) {
for (var i = 0; i < fn.body.length; i++) {
var stat = fn.body[i];
- if (is_defun(stat)) {
+ if (stat instanceof AST_LambdaDefinition) {
if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
if (!all(stat.enclosed, function(def) {
return def.scope === stat || !defined[def.name];
function can_inject_symbols() {
var defined = Object.create(null);
var level = 0, child;
- scope = compressor.self();
+ scope = current;
do {
if (scope.variables) scope.variables.each(function(def) {
defined[def.name] = true;
flatten_vars(decls, expressions);
expressions.push(value);
var args = fn.body.filter(function(stat) {
- if (is_defun(stat)) {
+ if (stat instanceof AST_LambdaDefinition) {
var def = stat.name.definition();
scope.functions.set(def.name, def);
scope.variables.set(def.name, def);
return self;
});
+ OPT(AST_Yield, function(self, compressor) {
+ if (!compressor.option("yields")) return self;
+ if (compressor.option("sequences")) {
+ var seq = lift_sequence_in_expression(self, compressor);
+ if (seq !== self) return seq.optimize(compressor);
+ }
+ var exp = self.expression;
+ if (self.nested && exp.TYPE == "Call") {
+ var inlined = exp.clone().optimize(compressor);
+ if (inlined.TYPE != "Call") return inlined;
+ }
+ return self;
+ });
+
AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
if (this.left instanceof AST_PropAccess) {
if (!(this.left.expression instanceof AST_Sequence)) return this;
if (single_use == "f") {
var scope = self.scope;
do {
- if (is_defun(scope) || is_function(scope)) scope.inlined = true;
+ if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) {
+ scope.inlined = true;
+ }
} while (scope = scope.parent_scope);
}
} else if (fixed.name && fixed.name.name == "await" && is_async(fixed)) {
def.single_use = false;
fixed._squeezed = true;
fixed.single_use = true;
- 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);
+ if (fixed instanceof AST_LambdaDefinition) {
+ var ctor;
+ switch (fixed.CTOR) {
+ case AST_AsyncDefun:
+ ctor = AST_AsyncFunction;
+ break;
+ case AST_AsyncGeneratorDefun:
+ ctor = AST_AsyncGeneratorFunction;
+ break;
+ case AST_Defun:
+ ctor = AST_Function;
+ break;
+ case AST_GeneratorDefun:
+ ctor = AST_GeneratorFunction;
+ break;
+ }
+ fixed = make_node(ctor, fixed, fixed);
fixed.name = make_node(AST_SymbolLambda, fixed.name, fixed.name);
}
if (fixed instanceof AST_Lambda) {
}
}
PARENS(AST_AsyncFunction, needs_parens_function);
+ PARENS(AST_AsyncGeneratorFunction, needs_parens_function);
PARENS(AST_Function, needs_parens_function);
+ PARENS(AST_GeneratorFunction, needs_parens_function);
// same goes for an object literal, because otherwise it would be
// interpreted as a block of code.
}
PARENS(AST_Object, needs_parens_obj);
- PARENS(AST_Unary, function(output) {
+ function needs_parens_unary(output) {
var p = output.parent();
// (-x) ** y
if (p instanceof AST_Binary) return p.operator == "**" && p.left === this;
- // (x++).toString(3)
- // (typeof x).length
- return (p instanceof AST_Call || p instanceof AST_PropAccess) && p.expression === this;
- });
+ // (await x)(y)
+ // new (await x)
+ if (p instanceof AST_Call) return p.expression === this;
+ // (x++)[y]
+ // (typeof x).y
+ if (p instanceof AST_PropAccess) return p.expression === this;
+ }
+ PARENS(AST_Await, needs_parens_unary);
+ PARENS(AST_Unary, needs_parens_unary);
PARENS(AST_Sequence, function(output) {
var p = output.parent();
// !(foo, bar, baz)
|| p instanceof AST_Unary
// var a = (1, 2), b = a + a; ---> b == 4
- || p instanceof AST_VarDef;
+ || p instanceof AST_VarDef
+ // yield (foo, bar)
+ || p instanceof AST_Yield;
});
PARENS(AST_Binary, function(output) {
PARENS(AST_Conditional, function(output) {
return needs_parens_assign_cond(this, output);
});
-
- PARENS(AST_Await, function(output) {
- var p = output.parent();
- // (await x) ** y
- if (p instanceof AST_Binary) return p.operator == "**" && p.left === this;
- // new (await foo)
- // (await foo)(bar)
- if (p instanceof AST_Call) return p.expression === this;
- // (await foo).prop
- // (await foo)["prop"]
- if (p instanceof AST_PropAccess) return p.expression === this;
+ PARENS(AST_Yield, function(output) {
+ return needs_parens_assign_cond(this, output);
});
/* -----[ PRINTERS ]----- */
}
DEFPRINT(AST_AsyncDefun, print_async);
DEFPRINT(AST_AsyncFunction, print_async);
+ function print_async_generator(output) {
+ output.print("async");
+ output.space();
+ output.print("function*");
+ print_lambda(this, output);
+ }
+ DEFPRINT(AST_AsyncGeneratorDefun, print_async_generator);
+ DEFPRINT(AST_AsyncGeneratorFunction, print_async_generator);
+ function print_generator(output) {
+ output.print("function*");
+ print_lambda(this, output);
+ }
+ DEFPRINT(AST_GeneratorDefun, print_generator);
+ DEFPRINT(AST_GeneratorFunction, print_generator);
/* -----[ jumps ]----- */
function print_jump(kind, prop) {
output.space();
this.expression.print(output);
});
+ DEFPRINT(AST_Yield, function(output) {
+ output.print(this.nested ? "yield*" : "yield");
+ if (this.expression) {
+ output.space();
+ this.expression.print(output);
+ }
+ });
/* -----[ literals ]----- */
DEFPRINT(AST_Array, function(output) {
var NEWLINE_CHARS = "\n\r\u2028\u2029";
var OPERATOR_CHARS = "+-*&%=<>!?|~^";
-var PUNC_BEFORE_EXPRESSION = "[{(,;:";
-var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + "`)}]";
+var PUNC_OPENERS = "[{(";
+var PUNC_SEPARATORS = ",;:";
+var PUNC_CLOSERS = ")}]";
+var PUNC_AFTER_EXPRESSION = PUNC_SEPARATORS + PUNC_CLOSERS;
+var PUNC_BEFORE_EXPRESSION = PUNC_OPENERS + PUNC_SEPARATORS;
+var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + "`" + PUNC_CLOSERS;
var WHITESPACE_CHARS = NEWLINE_CHARS + " \u00a0\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF";
var NON_IDENTIFIER_CHARS = makePredicate(characters("./'\"" + OPERATOR_CHARS + PUNC_CHARS + WHITESPACE_CHARS));
NEWLINE_CHARS = makePredicate(characters(NEWLINE_CHARS));
OPERATOR_CHARS = makePredicate(characters(OPERATOR_CHARS));
+PUNC_AFTER_EXPRESSION = makePredicate(characters(PUNC_AFTER_EXPRESSION));
PUNC_BEFORE_EXPRESSION = makePredicate(characters(PUNC_BEFORE_EXPRESSION));
PUNC_CHARS = makePredicate(characters(PUNC_CHARS));
WHITESPACE_CHARS = makePredicate(characters(WHITESPACE_CHARS));
in_directives : true,
in_funarg : -1,
in_function : 0,
+ in_generator : false,
in_loop : 0,
labels : [],
peeked : null,
if (is_token(peek(), "keyword", "function")) {
next();
next();
- return function_(AST_AsyncDefun);
+ if (!is("operator", "*")) return function_(AST_AsyncDefun);
+ next();
+ return function_(AST_AsyncGeneratorDefun);
}
break;
case "await":
if (S.in_async) return simple_statement();
break;
+ case "yield":
+ if (S.in_generator) return simple_statement();
+ break;
}
return is_token(peek(), "punc", ":")
? labeled_statement()
case "function":
next();
- return function_(AST_Defun);
+ if (!is("operator", "*")) return function_(AST_Defun);
+ next();
+ return function_(AST_GeneratorDefun);
case "if":
next();
var function_ = function(ctor) {
var was_async = S.in_async;
+ var was_gen = S.in_generator;
var name;
- if (ctor === AST_AsyncDefun) {
+ if (/Defun$/.test(ctor.TYPE)) {
name = as_symbol(AST_SymbolDefun);
- S.in_async = true;
- } else if (ctor === AST_Defun) {
- name = as_symbol(AST_SymbolDefun);
- S.in_async = false;
+ S.in_async = /^Async/.test(ctor.TYPE);
+ S.in_generator = /Generator/.test(ctor.TYPE);
} else {
- S.in_async = ctor === AST_AsyncFunction;
+ S.in_async = /^Async/.test(ctor.TYPE);
+ S.in_generator = /Generator/.test(ctor.TYPE);
name = as_symbol(AST_SymbolLambda, true);
}
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
--S.in_function;
S.in_loop = loop;
S.labels = labels;
+ S.in_generator = was_gen;
S.in_async = was_async;
return new ctor({
name: name,
}
if (is("keyword", "function")) {
next();
- var func = function_(AST_Function);
+ var func;
+ if (is("operator", "*")) {
+ next();
+ func = function_(AST_GeneratorFunction);
+ } else {
+ func = function_(AST_Function);
+ }
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
if (sym.name == "async") {
if (is("keyword", "function")) {
next();
- var func = function_(AST_AsyncFunction);
+ var func;
+ if (is("operator", "*")) {
+ next();
+ func = function_(AST_AsyncGeneratorFunction);
+ } else {
+ func = function_(AST_AsyncFunction);
+ }
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
// allow trailing comma
if (!options.strict && is("punc", "}")) break;
var start = S.token;
+ if (is("operator", "*")) {
+ next();
+ var key = as_property_key();
+ var gen_start = S.token;
+ var gen = function_(AST_GeneratorFunction);
+ gen.start = gen_start;
+ gen.end = prev();
+ a.push(new AST_ObjectKeyVal({
+ start: start,
+ key: key,
+ value: gen,
+ end: prev(),
+ }));
+ continue;
+ }
if (is("operator", "...")) {
next();
a.push(new AST_Spread({
}
if (start.type == "name") switch (key) {
case "async":
+ var is_gen = is("operator", "*") && next();
key = as_property_key();
var func_start = S.token;
- var func = function_(AST_AsyncFunction);
+ var func = function_(is_gen ? AST_AsyncGeneratorFunction : AST_AsyncFunction);
func.start = func_start;
func.end = prev();
a.push(new AST_ObjectKeyVal({
function _make_symbol(type, token) {
var name = token.value;
if (name === "await" && S.in_async) unexpected(token);
+ if (name === "yield" && S.in_generator) unexpected(token);
return new (name === "this" ? AST_This : type)({
name: "" + name,
start: token,
return expr;
};
- function maybe_unary() {
+ function maybe_unary(no_in) {
var start = S.token;
+ if (S.in_async && is("name", "await")) {
+ if (S.in_funarg === S.in_function) croak("Invalid use of await in function argument");
+ S.input.context().regex_allowed = true;
+ next();
+ return new AST_Await({
+ start: start,
+ expression: maybe_unary(no_in),
+ end: prev(),
+ });
+ }
+ if (S.in_generator && is("name", "yield")) {
+ if (S.in_funarg === S.in_function) croak("Invalid use of yield in function argument");
+ S.input.context().regex_allowed = true;
+ next();
+ var exp = null;
+ var nested = false;
+ if (is("operator", "*")) {
+ next();
+ exp = maybe_assign(no_in);
+ nested = true;
+ } else if (is("punc") ? !PUNC_AFTER_EXPRESSION[S.token.value] : !can_insert_semicolon()) {
+ exp = maybe_assign(no_in);
+ }
+ return new AST_Yield({
+ start: start,
+ expression: exp,
+ nested: nested,
+ end: prev(),
+ });
+ }
if (is("operator") && UNARY_PREFIX[start.value]) {
next();
handle_regexp();
- var ex = make_unary(AST_UnaryPrefix, start, maybe_await());
+ var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(no_in));
ex.start = start;
ex.end = prev();
return ex;
return new ctor({ operator: op, expression: expr });
}
- function maybe_await() {
- var start = S.token;
- if (!(S.in_async && is("name", "await"))) return maybe_unary();
- if (S.in_funarg === S.in_function) croak("Invalid use of await in function argument");
- S.input.context().regex_allowed = true;
- 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_await(), op == "**" ? prec - 1 : prec, no_in);
+ var right = expr_op(maybe_unary(no_in), op == "**" ? prec - 1 : prec, no_in);
return expr_op(new AST_Binary({
start : left.start,
left : left,
};
function expr_ops(no_in) {
- return expr_op(maybe_await(), 0, no_in);
+ return expr_op(maybe_unary(no_in), 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 (is_defun(node)) {
+ if (node instanceof AST_LambdaDefinition) {
node.name.walk(tw);
walk_scope(function() {
node.argnames.forEach(function(argname) {
AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) {
var def = this.def_variable(symbol, init);
- if (!def.init || is_defun(def.init)) def.init = init;
+ if (!def.init || def.init instanceof AST_LambdaDefinition) def.init = init;
this.functions.set(symbol.name, def);
return def;
});
var def = this.variables.get(symbol.name);
if (def) {
def.orig.push(symbol);
- if (is_function(def.init)) def.init = init;
+ if (def.init instanceof AST_LambdaExpression) def.init = init;
} else {
def = this.make_def(symbol, init);
this.variables.set(symbol.name, def);
DEF(AST_Await, function(self, tw) {
self.expression = self.expression.transform(tw);
});
+ DEF(AST_Yield, function(self, tw) {
+ if (self.expression) self.expression = self.expression.transform(tw);
+ });
DEF(AST_Dot, function(self, tw) {
self.expression = self.expression.transform(tw);
});
--- /dev/null
+binary: {
+ input: {
+ var a = function*() {
+ console.log(6 * (yield "PA" + "SS"));
+ }();
+ console.log(a.next("FAIL").value);
+ console.log(a.next(7).done);
+ }
+ expect_exact: 'var a=function*(){console.log(6*(yield"PA"+"SS"))}();console.log(a.next("FAIL").value);console.log(a.next(7).done);'
+ expect_stdout: [
+ "PASS",
+ "42",
+ "true",
+ ]
+ node_version: ">=4"
+}
+
+empty_yield: {
+ input: {
+ var a = function*() {
+ yield;
+ console.log(yield);
+ yield
+ "FAIL 1";
+ }();
+ console.log(a.next("FAIL 2").value);
+ console.log(a.next("FAIL 3").value);
+ console.log(a.next("PASS").value);
+ console.log(a.next("FAIL 4").done);
+ }
+ expect_exact: 'var a=function*(){yield;console.log(yield);yield;"FAIL 1"}();console.log(a.next("FAIL 2").value);console.log(a.next("FAIL 3").value);console.log(a.next("PASS").value);console.log(a.next("FAIL 4").done);'
+ expect_stdout: [
+ "undefined",
+ "undefined",
+ "PASS",
+ "undefined",
+ "true",
+ ]
+ node_version: ">=4"
+}
+
+empty_yield_conditional: {
+ input: {
+ var a = function*() {
+ console.log((yield) ? yield : yield);
+ }();
+ console.log(a.next("FAIL 1").value);
+ console.log(a.next("FAIL 2").value);
+ console.log(a.next("PASS").value);
+ console.log(a.next("FAIL 3").done);
+ }
+ expect_exact: 'var a=function*(){console.log((yield)?yield:yield)}();console.log(a.next("FAIL 1").value);console.log(a.next("FAIL 2").value);console.log(a.next("PASS").value);console.log(a.next("FAIL 3").done);'
+ expect_stdout: [
+ "undefined",
+ "undefined",
+ "PASS",
+ "undefined",
+ "true",
+ ]
+ node_version: ">=4"
+}
+
+nested_yield: {
+ input: {
+ console.log(function*() {
+ (yield*
+ f())
+ function* f() {
+ return "FAIL";
+ }
+ yield*
+ f();
+ yield *f();
+ }().next().value || "PASS");
+ }
+ expect_exact: 'console.log(function*(){yield*f();function*f(){return"FAIL"}yield*f();yield*f()}().next().value||"PASS");'
+ expect_stdout: "PASS"
+ node_version: ">=4"
+}
+
+pause_resume: {
+ input: {
+ function* f() {
+ console.log(yield "PASS");
+ }
+ var a = f();
+ console.log(a.next("FAIL").value);
+ console.log(a.next(42).done);
+ }
+ expect_exact: 'function*f(){console.log(yield"PASS")}var a=f();console.log(a.next("FAIL").value);console.log(a.next(42).done);'
+ expect_stdout: [
+ "PASS",
+ "42",
+ "true",
+ ]
+ node_version: ">=4"
+}
+
+collapse_vars_1: {
+ options = {
+ collapse_vars: true,
+ }
+ input: {
+ var a = "FAIL";
+ (function*() {
+ a = "PASS";
+ yield 42;
+ return "PASS";
+ })().next();
+ console.log(a);
+ }
+ expect: {
+ var a = "FAIL";
+ (function*() {
+ a = "PASS";
+ yield 42;
+ return "PASS";
+ })().next();
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=4"
+}
+
+collapse_vars_2: {
+ options = {
+ collapse_vars: true,
+ }
+ input: {
+ var a = "FAIL";
+ (function*() {
+ yield (a = "PASS");
+ return "PASS";
+ })().next();
+ console.log(a);
+ }
+ expect: {
+ var a = "FAIL";
+ (function*() {
+ yield (a = "PASS");
+ return "PASS";
+ })().next();
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=4"
+}
+
+collapse_vars_3: {
+ options = {
+ collapse_vars: true,
+ }
+ input: {
+ var a = "FAIL";
+ (function*() {
+ yield (a = "PASS", 42);
+ return "PASS";
+ })().next();
+ console.log(a);
+ }
+ expect: {
+ var a = "FAIL";
+ (function*() {
+ yield (a = "PASS", 42);
+ return "PASS";
+ })().next();
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=4"
+}
+
+collapse_vars_4: {
+ options = {
+ collapse_vars: true,
+ unused: true,
+ }
+ input: {
+ var a = "FAIL";
+ var b = function*(c) {
+ return c;
+ }(a = "PASS");
+ console.log(a, b.next().done);
+ }
+ expect: {
+ var a = "FAIL";
+ var b = function*(c) {
+ return c;
+ }(a = "PASS");
+ console.log(a, b.next().done);
+ }
+ expect_stdout: "PASS true"
+ node_version: ">=4"
+}
+
+collapse_property_lambda: {
+ options = {
+ collapse_vars: true,
+ pure_getters: "strict",
+ }
+ input: {
+ console.log(function* f() {
+ f.g = () => 42;
+ return f.g();
+ }().next().value);
+ }
+ expect: {
+ console.log(function* f() {
+ return (f.g = () => 42)();
+ }().next().value);
+ }
+ expect_stdout: "42"
+ node_version: ">=4"
+}
+
+evaluate: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ var a = function*() {}();
+ console.log(typeof a);
+ }
+ expect: {
+ var a = function*() {}();
+ console.log(typeof a);
+ }
+ expect_stdout: "object"
+ node_version: ">=4"
+}
+
+functions: {
+ options = {
+ functions: true,
+ reduce_vars: true,
+ unused: true,
+ }
+ input: {
+ !function*() {
+ var a = function* a() {
+ return a && "a";
+ };
+ var b = function* x() {
+ return !!x;
+ };
+ var c = function*(c) {
+ return c;
+ };
+ if (yield* c(yield* b(yield* a()))) {
+ var d = function*() {};
+ var e = function* y() {
+ return typeof y;
+ };
+ var f = function*(f) {
+ return f;
+ };
+ console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f);
+ }
+ }().next();
+ }
+ expect: {
+ !function*() {
+ function* a() {
+ return a && "a";
+ }
+ function* b() {
+ return !!b;
+ }
+ var c = function*(c) {
+ return c;
+ };
+ if (yield* c(yield* b(yield* a()))) {
+ function* d() {}
+ function* e() {
+ return typeof e;
+ }
+ var f = function*(f) {
+ return f;
+ };
+ console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f);
+ }
+ }().next();
+ }
+ expect_stdout: "a true 42 function function function"
+ node_version: ">=4"
+}
+
+functions_use_strict: {
+ options = {
+ functions: true,
+ reduce_vars: true,
+ unused: true,
+ }
+ input: {
+ "use strict";
+ !function*() {
+ var a = function* a() {
+ return a && "a";
+ };
+ var b = function* x() {
+ return !!x;
+ };
+ var c = function*(c) {
+ return c;
+ };
+ if (yield* c(yield* b(yield* a()))) {
+ var d = function*() {};
+ var e = function* y() {
+ return typeof y;
+ };
+ var f = function*(f) {
+ return f;
+ };
+ console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f);
+ }
+ }().next();
+ }
+ expect: {
+ "use strict";
+ !function*() {
+ function* a() {
+ return a && "a";
+ }
+ function* b() {
+ return !!b;
+ }
+ var c = function*(c) {
+ return c;
+ };
+ if (yield* c(yield* b(yield* a()))) {
+ var d = function*() {};
+ var e = function* y() {
+ return typeof y;
+ };
+ var f = function*(f) {
+ return f;
+ };
+ console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f);
+ }
+ }().next();
+ }
+ expect_stdout: "a true 42 function function function"
+ node_version: ">=4"
+}
+
+negate_iife: {
+ options = {
+ negate_iife: true,
+ side_effects: true,
+ }
+ input: {
+ (function*(a) {
+ console.log(a);
+ })("PASS").next();
+ }
+ expect: {
+ !function*(a) {
+ console.log(a);
+ }("PASS").next();
+ }
+ expect_stdout: "PASS"
+ node_version: ">=4"
+}
+
+reduce_iife_1: {
+ options = {
+ reduce_vars: true,
+ unused: true,
+ }
+ input: {
+ console.log(function*(a) {
+ yield a;
+ }(42).next().value);
+ }
+ expect: {
+ console.log(function*(a) {
+ yield 42;
+ }().next().value);
+ }
+ expect_stdout: "42"
+ node_version: ">=4"
+}
+
+reduce_iife_2: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ var a = "PASS";
+ (function*() {
+ a = "FAIL";
+ })();
+ console.log(a);
+ }
+ expect: {
+ var a = "PASS";
+ (function*() {
+ a = "FAIL";
+ })();
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=4"
+}
+
+reduce_single_use_defun: {
+ options = {
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ function* f(a) {
+ console.log(a);
+ }
+ f("PASS").next();
+ }
+ expect: {
+ (function*(a) {
+ console.log(a);
+ })("PASS").next();
+ }
+ expect_stdout: "PASS"
+ node_version: ">=4"
+}
+
+reduce_tagged: {
+ options = {
+ reduce_funcs: true,
+ reduce_vars: true,
+ unused: true,
+ }
+ input: {
+ function* f() {
+ function g() {
+ h`foo`;
+ }
+ g();
+ function h(s) {
+ console.log(s[0]);
+ }
+ h([ "bar" ]);
+ }
+ f().next();
+ }
+ expect: {
+ function* f() {
+ (function() {
+ h`foo`;
+ })();
+ function h(s) {
+ console.log(s[0]);
+ }
+ h([ "bar" ]);
+ }
+ f().next();
+ }
+ expect_stdout: [
+ "foo",
+ "bar",
+ ]
+ node_version: ">=4"
+}
+
+reduce_tagged_async: {
+ options = {
+ reduce_funcs: true,
+ reduce_vars: true,
+ unused: true,
+ }
+ input: {
+ async function* f() {
+ function g() {
+ h`foo`;
+ }
+ g();
+ function h(s) {
+ console.log(s[0]);
+ }
+ h([ "bar" ]);
+ }
+ f().next();
+ }
+ expect: {
+ async function* f() {
+ (function() {
+ h`foo`;
+ })();
+ function h(s) {
+ console.log(s[0]);
+ }
+ h([ "bar" ]);
+ }
+ f().next();
+ }
+ expect_stdout: [
+ "foo",
+ "bar",
+ ]
+ node_version: ">=10"
+}
+
+lift_sequence: {
+ options = {
+ sequences: true,
+ yields: true,
+ }
+ input: {
+ console.log(function*() {
+ yield (console, "PASS");
+ }().next().value);
+ }
+ expect: {
+ console.log(function*() {
+ console, yield "PASS";
+ }().next().value);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=4"
+}
+
+inline_nested_yield: {
+ options = {
+ inline: true,
+ sequences: true,
+ yields: true,
+ }
+ input: {
+ var a = function*() {
+ yield* function*() {
+ yield "foo";
+ return "FAIL";
+ }();
+ }(), b;
+ do {
+ b = a.next();
+ console.log(b.value);
+ } while (!b.done);
+ }
+ expect: {
+ var a = function*() {
+ yield "foo",
+ "FAIL";
+ }(), b;
+ do {
+ b = a.next(),
+ console.log(b.value);
+ } while (!b.done);
+ }
+ expect_stdout: [
+ "foo",
+ "undefined",
+ ]
+ node_version: ">=4"
+}
+
+issue_4618: {
+ options = {
+ functions: true,
+ reduce_vars: true,
+ unused: true,
+ }
+ input: {
+ console.log(typeof function() {
+ var yield = function* f() {
+ console || f();
+ };
+ console.log;
+ return yield;
+ }());
+ }
+ expect: {
+ console.log(typeof function() {
+ var yield = function* f() {
+ console || f();
+ };
+ console.log;
+ return yield;
+ }());
+ }
+ expect_stdout: "function"
+ node_version: ">=4"
+}
--- /dev/null
+var assert = require("assert");
+var UglifyJS = require("../node");
+
+describe("generator", function() {
+ it("Should reject `yield` as symbol name within generator functions only", function() {
+ [
+ "function yield() {}",
+ "function(yield) {}",
+ "function() { yield:{} }",
+ "function() { var yield; }",
+ "function() { function yield() {} }",
+ "function() { try {} catch (yield) {} }",
+ ].forEach(function(code) {
+ var ast = UglifyJS.parse("(" + code + ")();");
+ assert.strictEqual(ast.TYPE, "Toplevel");
+ assert.strictEqual(ast.body.length, 1);
+ assert.strictEqual(ast.body[0].TYPE, "SimpleStatement");
+ assert.strictEqual(ast.body[0].body.TYPE, "Call");
+ assert.strictEqual(ast.body[0].body.expression.TYPE, "Function");
+ assert.throws(function() {
+ UglifyJS.parse("(" + code.replace(/^function/, "function*") + ")();");
+ }, function(e) {
+ return e instanceof UglifyJS.JS_Parse_Error;
+ }, code);
+ });
+ });
+ it("Should reject `yield` expression outside of generator functions", function() {
+ [
+ "yield 42;",
+ "function f() { yield 42; }",
+ "function* f() { function g() { yield 42; } }",
+ ].forEach(function(code) {
+ assert.throws(function() {
+ UglifyJS.parse(code);
+ }, function(e) {
+ return e instanceof UglifyJS.JS_Parse_Error;
+ }, code);
+ });
+ });
+ it("Should reject `yield` expression directly on computed key of function argument", function() {
+ [
+ "function f({ [yield 42]: a }) {}",
+ "function* f({ [yield 42]: a }) {}",
+ ].forEach(function(code) {
+ assert.throws(function() {
+ UglifyJS.parse(code);
+ }, function(e) {
+ return e instanceof UglifyJS.JS_Parse_Error;
+ }, code);
+ });
+ });
+ it("Should accept `yield` expression nested within computed key of function argument", function() {
+ [
+ "function f({ [function*() { yield 42; }()]: a }) {}",
+ "function* f({ [function*() { yield 42; }()]: a }) {}",
+ ].forEach(function(code) {
+ var ast = UglifyJS.parse(code);
+ assert.strictEqual(ast.TYPE, "Toplevel");
+ assert.strictEqual(ast.body.length, 1);
+ assert.strictEqual(ast.body[0].argnames.length, 1);
+ assert.strictEqual(ast.body[0].argnames[0].TYPE, "DestructuredObject");
+ });
+ });
+ it("Should reject `yield*` without an expression", function() {
+ [
+ "yield*",
+ "yield*;",
+ "yield*,",
+ "(yield*)",
+ "[ yield* ]",
+ "42[yield*]",
+ "yield* && 42",
+ ].forEach(function(code) {
+ code = "function* f() { " + code + " }";
+ assert.throws(function() {
+ UglifyJS.parse(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_Arrow
- || node instanceof U.AST_AsyncArrow
- || node instanceof U.AST_AsyncFunction
- || node instanceof U.AST_Function);
+ return node instanceof U.AST_Statement && !(node instanceof U.AST_LambdaExpression);
}
function merge_sequence(array, node) {
}({
arrow: "a => 0;",
async: "async function f(){}",
+ async_generator: "async function* f(){}",
bigint: "42n",
catch_omit_var: "try {} catch {}",
computed_key: "({[0]: 0});",
default_value: "[ a = 0 ] = [];",
destructuring: "[] = [];",
exponentiation: "0 ** 0",
+ generator: "function* f(){}",
let: "let a;",
rest: "var [...a] = [];",
rest_object: "var {...a} = {};",
var block_vars = [];
var unique_vars = [];
var async = false;
+var generator = false;
var loops = 0;
var funcs = 0;
var called = Object.create(null);
block_vars.length = 0;
unique_vars.length = 0;
async = false;
+ generator = false;
loops = 0;
funcs = 0;
called = Object.create(null);
return SUPPORT.trailing_comma && list && rng(20) == 0 ? list + "," : list;
}
-function createParams(was_async, noDuplicate) {
+function createParams(was_async, was_generator, noDuplicate) {
var save_async = async;
if (was_async) async = true;
+ var save_generator = generator;
+ if (was_generator) generator = true;
var len = unique_vars.length;
var params = [];
for (var n = rng(4); --n >= 0;) {
params.push(name);
}
unique_vars.length = len;
+ generator = save_generator;
async = save_async;
return addTrailingComma(params.join(", "));
}
return "(" + addTrailingComma(args.join(", ")) + ")";
}
-function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async) {
+function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async, was_generator) {
var avoid = [];
var len = unique_vars.length;
var pairs = createPairs(recurmax, !nameLenBefore);
async = false;
if (save_async || was_async) addAvoidVar("await");
}
+ var save_generator = generator;
+ if (was_generator != null) {
+ generator = false;
+ if (save_generator || was_generator) addAvoidVar("yield");
+ }
avoid.forEach(addAvoidVar);
var save_vars = nameLenBefore && VAR_NAMES.splice(nameLenBefore);
if (nameFn) nameFn();
+ if (was_generator != null) {
+ generator = was_generator;
+ if (save_generator || was_generator) removeAvoidVar("yield");
+ }
if (was_async != null) {
async = was_async;
if (save_async || was_async) removeAvoidVar("await");
if (valueFn) valueFn();
if (save_vars) [].push.apply(VAR_NAMES, save_vars);
avoid.forEach(removeAvoidVar);
+ generator = save_generator;
async = save_async;
}
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
var save_async = async;
if (was_async) async = true;
+ var save_generator = generator;
+ if (was_generator) generator = true;
var name = createVarName(MANDATORY);
+ generator = save_generator;
async = save_async;
unique_vars.length -= 6;
avoid.push(name);
}
function makeFunction(name) {
- return (async ? "async function " : "function ") + name;
+ if (generator) {
+ name = "function* " + name;
+ } else {
+ name = "function " + name;
+ }
+ if (async) name = "async " + name;
+ return name;
+}
+
+function invokeGenerator(was_generator) {
+ if (generator && !was_generator) switch (rng(4)) {
+ case 0:
+ return ".next()";
+ case 1:
+ return ".next().done";
+ case 2:
+ return ".next().value";
+ }
+ return "";
}
function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
var nameLenBefore = VAR_NAMES.length;
var save_async = async;
async = SUPPORT.async && rng(50) == 0;
+ var save_generator = generator;
+ generator = SUPPORT.generator && rng(50) == 0;
+ if (async && generator && !SUPPORT.async_generator) {
+ if (rng(2)) {
+ async = false;
+ } else {
+ generator = false;
+ }
+ }
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
if (allowDefun || rng(5) > 0) {
name = "f" + funcs++;
var params;
if (SUPPORT.destructuring && (!allowDefun || !(name in called)) && rng(2)) {
called[name] = false;
- var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
+ var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async, save_generator);
params = pairs.names.join(", ");
if (!pairs.has_rest) params = addTrailingComma(params);
args = "(" + addTrailingComma(pairs.values.join(", ")) + ")";
} else {
- params = createParams(save_async);
+ params = createParams(save_async, save_generator);
}
s.push(makeFunction(name) + "(" + params + "){", strictMode());
s.push(defns());
s.push("}", "");
s = filterDirective(s).join("\n");
});
+ var call_next = invokeGenerator(save_generator);
+ generator = save_generator;
async = save_async;
VAR_NAMES.length = nameLenBefore;
// avoid "function statements" (decl inside statements)
s = "var " + createVarName(MANDATORY) + " = " + s;
s += args || createArgs(recurmax, stmtDepth, canThrow);
+ s += call_next;
} else if (!(name in called) || args || rng(3)) {
s += "var " + createVarName(MANDATORY) + " = " + name;
s += args || createArgs(recurmax, stmtDepth, canThrow);
+ s += call_next;
}
return s + ";";
return "((c = c + 1) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; // c only gets incremented
default:
var expr = "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")";
- return async && rng(50) == 0 ? "(await" + expr + ")" : expr;
+ if (async && rng(50) == 0) return "(await" + expr + ")";
+ if (generator && rng(50) == 0) return "(yield" + (canThrow && rng(20) == 0 ? "*" : "") + expr + ")";
+ return expr;
}
}
var p = 0;
switch (rng(_createExpression.N)) {
case p++:
+ if (generator && rng(50) == 0) return "yield";
case p++:
return createUnaryPrefix() + (rng(2) ? "a" : "b");
case p++:
var nameLenBefore = VAR_NAMES.length;
var save_async = async;
async = SUPPORT.async && rng(50) == 0;
+ var save_generator = generator;
+ generator = SUPPORT.generator && rng(50) == 0;
+ if (async && generator && !SUPPORT.async_generator) {
+ if (rng(2)) {
+ async = false;
+ } else {
+ generator = false;
+ }
+ }
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();
var s = [];
switch (rng(5)) {
case 0:
- if (SUPPORT.arrow && !name && rng(2)) {
+ if (SUPPORT.arrow && !name && !generator && rng(2)) {
var args, suffix;
(rng(2) ? createBlockVariables : function() {
arguments[3]();
})(recurmax, stmtDepth, canThrow, function(defns) {
var params;
if (SUPPORT.destructuring && rng(2)) {
- var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
+ var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async, save_generator);
params = pairs.names.join(", ");
if (!pairs.has_rest) params = addTrailingComma(params);
args = "(" + addTrailingComma(pairs.values.join(", ")) + ")";
} else {
- params = createParams(save_async, NO_DUPLICATE);
+ params = createParams(save_async, save_generator, NO_DUPLICATE);
}
params = (async ? "async (" : "(") + params + ") => ";
if (defns) {
suffix = ")";
}
});
+ generator = save_generator;
async = save_async;
VAR_NAMES.length = nameLenBefore;
if (!args && rng(2)) args = createArgs(recurmax, stmtDepth, canThrow);
"(" + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
- rng(2) ? "})" : "})()"
+ rng(2) ? "})" : "})()" + invokeGenerator(save_generator)
);
}
break;
"+" + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
- "}()"
+ "}()" + invokeGenerator(save_generator)
);
break;
case 2:
"!" + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
- "}()"
+ "}()" + invokeGenerator(save_generator)
);
break;
case 3:
"void " + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
- "}()"
+ "}()" + invokeGenerator(save_generator)
);
break;
default:
async = false;
+ generator = false;
var instantiate = rng(4) ? "new " : "";
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
s.push(
- instantiate + "function " + name + "(" + createParams(save_async) + "){",
+ instantiate + "function " + name + "(" + createParams(save_async, save_generator) + "){",
strictMode(),
defns()
);
}
s.push(_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
});
+ generator = save_generator;
async = save_async;
VAR_NAMES.length = nameLenBefore;
s.push(rng(2) ? "}" : "}" + createArgs(recurmax, stmtDepth, canThrow, instantiate));
break;
}
+ generator = save_generator;
async = save_async;
VAR_NAMES.length = nameLenBefore;
return filterDirective(s).join("\n");
}
var SAFE_KEYS = [
- "length",
- "foo",
"a",
"b",
"c",
"null",
"NaN",
"Infinity",
+ "done",
+ "foo",
"in",
+ "length",
+ "next",
+ "then",
+ "value",
"var",
];
var KEYS = [
function createObjectFunction(recurmax, stmtDepth, canThrow) {
var nameLenBefore = VAR_NAMES.length;
var save_async = async;
+ var save_generator = generator;
var s;
var name = createObjectKey(recurmax, stmtDepth, canThrow);
var fn;
switch (rng(SUPPORT.computed_key ? 3 : 2)) {
case 0:
async = false;
+ generator = false;
fn = function(defns) {
s = [
"get " + name + "(){",
prop = getDotKey();
} while (name == prop);
async = false;
+ generator = false;
fn = function(defns) {
s = [
"set " + name + "(" + createVarName(MANDATORY) + "){",
break;
default:
async = SUPPORT.async && rng(50) == 0;
+ generator = SUPPORT.generator && rng(50) == 0;
+ if (async && generator && !SUPPORT.async_generator) {
+ if (rng(2)) {
+ async = false;
+ } else {
+ generator = false;
+ }
+ }
fn = function(defns) {
+ if (generator) name = "*" + name;
+ if (async) name = "async "+ name;
s = [
- (async ? "async " : "") + name + "(" + createParams(save_async, NO_DUPLICATE) + "){",
+ name + "(" + createParams(save_async, save_generator, NO_DUPLICATE) + "){",
strictMode(),
defns(),
_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
break;
}
createBlockVariables(recurmax, stmtDepth, canThrow, fn);
+ generator = save_generator;
async = save_async;
VAR_NAMES.length = nameLenBefore;
return filterDirective(s).join("\n");
function createVar() {
var save_async = async;
+ var save_generator = generator;
if (!async && avoid_vars.indexOf("await") >= 0) async = true;
+ if (!generator && avoid_vars.indexOf("yield") >= 0) generator = true;
var name = createVarName(MANDATORY, DONT_STORE);
+ generator = save_generator;
async = save_async;
return name;
}
do {
if (--tries < 0) return "a";
name = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)];
- } while (!name || avoid_vars.indexOf(name) >= 0 || noConst && block_vars.indexOf(name) >= 0 || async && name == "await");
+ } while (!name
+ || avoid_vars.indexOf(name) >= 0
+ || noConst && block_vars.indexOf(name) >= 0
+ || async && name == "await"
+ || generator && name == "yield");
return name;
}
name = VAR_NAMES[rng(VAR_NAMES.length)];
if (--tries < 0) suffix++;
if (suffix) name += "_" + suffix;
- } while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0 || async && name == "await");
+ } while (unique_vars.indexOf(name) >= 0
+ || block_vars.indexOf(name) >= 0
+ || async && name == "await"
+ || generator && name == "yield");
if (!dontStore) VAR_NAMES.push(name);
return name;
}