support for [await]...of statements (#4627)

This commit is contained in:
Alex Lam S.L
2021-02-08 20:28:23 +00:00
committed by GitHub
parent aedc1e7fc9
commit e13d1e9969
11 changed files with 203 additions and 64 deletions

View File

@@ -422,11 +422,11 @@ var AST_For = DEFNODE("For", "init condition step", {
},
}, AST_IterationStatement);
var AST_ForIn = DEFNODE("ForIn", "init object", {
$documentation: "A `for ... in` statement",
var AST_ForEnumeration = DEFNODE("ForEnumeration", "init object", {
$documentation: "Base class for enumeration loops, i.e. `for ... in`, `for ... of` & `for await ... of`",
$propdoc: {
init: "[AST_Node] the `for/in` initialization code",
object: "[AST_Node] the object that we're looping through"
init: "[AST_Node] the assignment target during iteration",
object: "[AST_Node] the object to iterate over"
},
walk: function(visitor) {
var node = this;
@@ -437,6 +437,7 @@ var AST_ForIn = DEFNODE("ForIn", "init object", {
});
},
_validate: function() {
if (this.TYPE == "ForEnumeration") throw new Error("should not instantiate AST_ForEnumeration");
if (this.init instanceof AST_Definitions) {
if (this.init.definitions.length != 1) throw new Error("init must have single declaration");
} else {
@@ -450,6 +451,18 @@ var AST_ForIn = DEFNODE("ForIn", "init object", {
},
}, AST_IterationStatement);
var AST_ForIn = DEFNODE("ForIn", null, {
$documentation: "A `for ... in` statement",
}, AST_ForEnumeration);
var AST_ForOf = DEFNODE("ForOf", null, {
$documentation: "A `for ... of` statement",
}, AST_ForEnumeration);
var AST_ForAwaitOf = DEFNODE("ForAwaitOf", null, {
$documentation: "A `for await ... of` statement",
}, AST_ForOf);
var AST_With = DEFNODE("With", "expression", {
$documentation: "A `with` statement",
$propdoc: {

View File

@@ -347,7 +347,7 @@ merge(Compressor.prototype, {
&& !parent.is_expr_pure(compressor)
&& (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this());
}
if (parent instanceof AST_ForIn) return parent.init === node;
if (parent instanceof AST_ForEnumeration) return parent.init === node;
if (parent instanceof AST_ObjectKeyVal) {
if (parent.value !== node) return;
var obj = tw.parent(level + 1);
@@ -930,7 +930,7 @@ merge(Compressor.prototype, {
tw.in_loop = saved_loop;
return true;
});
def(AST_ForIn, function(tw, descend, compressor) {
def(AST_ForEnumeration, function(tw, descend, compressor) {
this.variables.each(function(def) {
reset_def(tw, compressor, def);
});
@@ -1802,8 +1802,8 @@ merge(Compressor.prototype, {
abort = true;
return node;
}
// Scan object only in a for-in statement
if (node instanceof AST_ForIn) {
// Scan object only in a for-in/of statement
if (node instanceof AST_ForEnumeration) {
node.object = node.object.transform(tt);
abort = true;
return node;
@@ -2117,7 +2117,7 @@ merge(Compressor.prototype, {
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
} else if (expr instanceof AST_ForEnumeration) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
@@ -2232,7 +2232,7 @@ merge(Compressor.prototype, {
if (parent.init !== node && parent.condition !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_ForIn) {
if (parent instanceof AST_ForEnumeration) {
if (parent.init !== node) return node;
return find_stop_value(parent, level + 1);
}
@@ -3190,7 +3190,7 @@ merge(Compressor.prototype, {
} else if (stat.init instanceof AST_Var) {
defs = stat.init;
}
} else if (stat instanceof AST_ForIn) {
} else if (stat instanceof AST_ForEnumeration) {
if (defs && defs.TYPE == stat.init.TYPE) {
var defns = defs.definitions.slice();
stat.init = stat.init.definitions[0].name.convert_symbol(AST_SymbolRef, function(ref, name) {
@@ -3237,7 +3237,7 @@ merge(Compressor.prototype, {
if (defs === node) return node;
if (defs.TYPE != node.TYPE) return node;
var parent = this.parent();
if (parent instanceof AST_ForIn && parent.init === node) return node;
if (parent instanceof AST_ForEnumeration && parent.init === node) return node;
if (!declarations_only(node)) return node;
defs.definitions = defs.definitions.concat(node.definitions);
CHANGED = true;
@@ -5155,7 +5155,7 @@ merge(Compressor.prototype, {
pop();
return true;
}
if (node instanceof AST_ForIn) {
if (node instanceof AST_ForEnumeration) {
node.object.walk(tw);
push();
segment.block = node;
@@ -5788,7 +5788,7 @@ merge(Compressor.prototype, {
if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
node.argname.transform(trimmer);
}
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
if (node instanceof AST_Definitions && !(parent instanceof AST_ForEnumeration && parent.init === node)) {
// place uninitialized names at the start
var body = [], head = [], tail = [];
// for unused names whose initialization has
@@ -6456,7 +6456,7 @@ merge(Compressor.prototype, {
});
var seq = node.to_assignments();
var p = tt.parent();
if (p instanceof AST_ForIn && p.init === node) {
if (p instanceof AST_ForEnumeration && p.init === node) {
if (seq) return seq;
var def = node.definitions[0].name;
return make_node(AST_SymbolRef, def, def);
@@ -7330,7 +7330,7 @@ merge(Compressor.prototype, {
return if_break_in_loop(self, compressor);
});
OPT(AST_ForIn, function(self, compressor) {
OPT(AST_ForEnumeration, function(self, compressor) {
if (compressor.option("varify") && (self.init instanceof AST_Const || self.init instanceof AST_Let)) {
var name = self.init.definitions[0].name;
if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
@@ -8661,7 +8661,7 @@ merge(Compressor.prototype, {
} else if (scope instanceof AST_For) {
if (scope.init === child) continue;
in_loop = [];
} else if (scope instanceof AST_ForIn) {
} else if (scope instanceof AST_ForEnumeration) {
if (scope.init === child) continue;
if (scope.object === child) continue;
in_loop = [];
@@ -10734,7 +10734,7 @@ merge(Compressor.prototype, {
if (assigned) return self;
if (compressor.option("sequences")
&& parent.TYPE != "Call"
&& !(parent instanceof AST_ForIn && parent.init === self)) {
&& !(parent instanceof AST_ForEnumeration && parent.init === self)) {
var seq = lift_sequence_in_expression(self, compressor);
if (seq !== self) return seq.optimize(compressor);
}
@@ -10857,7 +10857,7 @@ merge(Compressor.prototype, {
if (is_lhs(compressor.self(), parent)) return self;
if (compressor.option("sequences")
&& parent.TYPE != "Call"
&& !(parent instanceof AST_ForIn && parent.init === self)) {
&& !(parent instanceof AST_ForEnumeration && parent.init === self)) {
var seq = lift_sequence_in_expression(self, compressor);
if (seq !== self) return seq.optimize(compressor);
}

View File

@@ -715,9 +715,13 @@ function OutputStream(options) {
|| p instanceof AST_Conditional
// [ a = (1, 2) ] = [] ---> a == 2
|| p instanceof AST_DefaultValue
// { [(1, 2)]: foo } = bar
// { 1: (2, foo) } = bar
|| p instanceof AST_DestructuredKeyVal
// for (foo of (bar, baz));
|| p instanceof AST_ForOf
// { [(1, 2)]: 3 }[2] ---> 3
// { foo: (1, 2) }.foo ---> 2
|| p instanceof AST_DestructuredKeyVal
|| p instanceof AST_ObjectProperty
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ---> 2
|| p instanceof AST_PropAccess && p.expression === this
@@ -978,20 +982,25 @@ function OutputStream(options) {
output.space();
force_statement(self.body, output);
});
DEFPRINT(AST_ForIn, function(output) {
var self = this;
output.print("for");
output.space();
output.with_parens(function() {
self.init.print(output);
function print_for_enum(prefix, infix) {
return function(output) {
var self = this;
output.print(prefix);
output.space();
output.print("in");
output.with_parens(function() {
self.init.print(output);
output.space();
output.print(infix);
output.space();
self.object.print(output);
});
output.space();
self.object.print(output);
});
output.space();
force_statement(self.body, output);
});
force_statement(self.body, output);
};
}
DEFPRINT(AST_ForAwaitOf, print_for_enum("for await", "of"));
DEFPRINT(AST_ForIn, print_for_enum("for", "in"));
DEFPRINT(AST_ForOf, print_for_enum("for", "of"));
DEFPRINT(AST_With, function(output) {
var self = this;
output.print("with");
@@ -1226,7 +1235,7 @@ function OutputStream(options) {
def.print(output);
});
var p = output.parent();
if (p && p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon();
if (!(p instanceof AST_IterationStatement && p.init === self)) output.semicolon();
};
}
DEFPRINT(AST_Const, print_definitinos("const"));
@@ -1253,7 +1262,7 @@ function OutputStream(options) {
output.print("=");
output.space();
var p = output.parent(1);
var noin = p instanceof AST_For || p instanceof AST_ForIn;
var noin = p instanceof AST_For || p instanceof AST_ForEnumeration;
parenthesize_for_noin(self.value, output, noin);
}
});

View File

@@ -1039,6 +1039,7 @@ function parse($TEXT, options) {
}
function for_() {
var await = is("name", "await") && next();
expect("(");
var init = null;
if (!is("punc", ";")) {
@@ -1049,16 +1050,26 @@ function parse($TEXT, options) {
: is("keyword", "var")
? (next(), var_(true))
: expression(true);
if (is("operator", "in")) {
var ctor;
if (await) {
expect_token("name", "of");
ctor = AST_ForAwaitOf;
} else if (is("operator", "in")) {
next();
ctor = AST_ForIn;
} else if (is("name", "of")) {
next();
ctor = AST_ForOf;
}
if (ctor) {
if (init instanceof AST_Definitions) {
if (init.definitions.length > 1) {
token_error(init.start, "Only one variable declaration allowed in for..in loop");
token_error(init.start, "Only one variable declaration allowed in for..in/of loop");
}
} else if (!(is_assignable(init) || (init = to_destructured(init)) instanceof AST_Destructured)) {
token_error(init.start, "Invalid left-hand side in for..in loop");
token_error(init.start, "Invalid left-hand side in for..in/of loop");
}
next();
return for_in(init);
return for_enum(ctor, init);
}
}
return regular_for(init);
@@ -1078,10 +1089,10 @@ function parse($TEXT, options) {
});
}
function for_in(init) {
function for_enum(ctor, init) {
var obj = expression();
expect(")");
return new AST_ForIn({
return new ctor({
init : init,
object : obj,
body : in_loop(statement)
@@ -1523,7 +1534,7 @@ function parse($TEXT, options) {
func.end = prev();
return subscripts(func, allow_calls);
}
if (is("name")) {
if (is("name") && is_token(peek(), "punc", "=>")) {
start = S.token;
sym = _make_symbol(AST_SymbolRef, start);
next();

View File

@@ -82,7 +82,7 @@ TreeTransformer.prototype = new TreeWalker;
if (self.step) self.step = self.step.transform(tw);
self.body = self.body.transform(tw);
});
DEF(AST_ForIn, function(self, tw) {
DEF(AST_ForEnumeration, function(self, tw) {
self.init = self.init.transform(tw);
self.object = self.object.transform(tw);
self.body = self.body.transform(tw);