support for [await]...of statements (#4627)
This commit is contained in:
@@ -1272,3 +1272,12 @@ To allow for better optimizations, the compiler makes various assumptions:
|
|||||||
// SyntaxError: Invalid Unicode escape sequence
|
// SyntaxError: Invalid Unicode escape sequence
|
||||||
```
|
```
|
||||||
UglifyJS may modify the input which in turn may suppress those errors.
|
UglifyJS may modify the input which in turn may suppress those errors.
|
||||||
|
- Some versions of JavaScript will throw `SyntaxError` with the
|
||||||
|
following:
|
||||||
|
```javascript
|
||||||
|
try {} catch (e) {
|
||||||
|
for (var e of []);
|
||||||
|
}
|
||||||
|
// SyntaxError: Identifier 'e' has already been declared
|
||||||
|
```
|
||||||
|
UglifyJS may modify the input which in turn may suppress those errors.
|
||||||
|
|||||||
21
lib/ast.js
21
lib/ast.js
@@ -422,11 +422,11 @@ var AST_For = DEFNODE("For", "init condition step", {
|
|||||||
},
|
},
|
||||||
}, AST_IterationStatement);
|
}, AST_IterationStatement);
|
||||||
|
|
||||||
var AST_ForIn = DEFNODE("ForIn", "init object", {
|
var AST_ForEnumeration = DEFNODE("ForEnumeration", "init object", {
|
||||||
$documentation: "A `for ... in` statement",
|
$documentation: "Base class for enumeration loops, i.e. `for ... in`, `for ... of` & `for await ... of`",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
init: "[AST_Node] the `for/in` initialization code",
|
init: "[AST_Node] the assignment target during iteration",
|
||||||
object: "[AST_Node] the object that we're looping through"
|
object: "[AST_Node] the object to iterate over"
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
@@ -437,6 +437,7 @@ var AST_ForIn = DEFNODE("ForIn", "init object", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
|
if (this.TYPE == "ForEnumeration") throw new Error("should not instantiate AST_ForEnumeration");
|
||||||
if (this.init instanceof AST_Definitions) {
|
if (this.init instanceof AST_Definitions) {
|
||||||
if (this.init.definitions.length != 1) throw new Error("init must have single declaration");
|
if (this.init.definitions.length != 1) throw new Error("init must have single declaration");
|
||||||
} else {
|
} else {
|
||||||
@@ -450,6 +451,18 @@ var AST_ForIn = DEFNODE("ForIn", "init object", {
|
|||||||
},
|
},
|
||||||
}, AST_IterationStatement);
|
}, 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", {
|
var AST_With = DEFNODE("With", "expression", {
|
||||||
$documentation: "A `with` statement",
|
$documentation: "A `with` statement",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ merge(Compressor.prototype, {
|
|||||||
&& !parent.is_expr_pure(compressor)
|
&& !parent.is_expr_pure(compressor)
|
||||||
&& (!(value instanceof AST_LambdaExpression) || !(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_ForEnumeration) return parent.init === node;
|
||||||
if (parent instanceof AST_ObjectKeyVal) {
|
if (parent instanceof AST_ObjectKeyVal) {
|
||||||
if (parent.value !== node) return;
|
if (parent.value !== node) return;
|
||||||
var obj = tw.parent(level + 1);
|
var obj = tw.parent(level + 1);
|
||||||
@@ -930,7 +930,7 @@ merge(Compressor.prototype, {
|
|||||||
tw.in_loop = saved_loop;
|
tw.in_loop = saved_loop;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
def(AST_ForIn, function(tw, descend, compressor) {
|
def(AST_ForEnumeration, function(tw, descend, compressor) {
|
||||||
this.variables.each(function(def) {
|
this.variables.each(function(def) {
|
||||||
reset_def(tw, compressor, def);
|
reset_def(tw, compressor, def);
|
||||||
});
|
});
|
||||||
@@ -1802,8 +1802,8 @@ merge(Compressor.prototype, {
|
|||||||
abort = true;
|
abort = true;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
// Scan object only in a for-in statement
|
// Scan object only in a for-in/of statement
|
||||||
if (node instanceof AST_ForIn) {
|
if (node instanceof AST_ForEnumeration) {
|
||||||
node.object = node.object.transform(tt);
|
node.object = node.object.transform(tt);
|
||||||
abort = true;
|
abort = true;
|
||||||
return node;
|
return node;
|
||||||
@@ -2117,7 +2117,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!(expr.body instanceof AST_Block)) {
|
if (!(expr.body instanceof AST_Block)) {
|
||||||
extract_candidates(expr.body);
|
extract_candidates(expr.body);
|
||||||
}
|
}
|
||||||
} else if (expr instanceof AST_ForIn) {
|
} else if (expr instanceof AST_ForEnumeration) {
|
||||||
extract_candidates(expr.object);
|
extract_candidates(expr.object);
|
||||||
if (!(expr.body instanceof AST_Block)) {
|
if (!(expr.body instanceof AST_Block)) {
|
||||||
extract_candidates(expr.body);
|
extract_candidates(expr.body);
|
||||||
@@ -2232,7 +2232,7 @@ merge(Compressor.prototype, {
|
|||||||
if (parent.init !== node && parent.condition !== node) return node;
|
if (parent.init !== node && parent.condition !== node) return node;
|
||||||
return find_stop_value(parent, level + 1);
|
return find_stop_value(parent, level + 1);
|
||||||
}
|
}
|
||||||
if (parent instanceof AST_ForIn) {
|
if (parent instanceof AST_ForEnumeration) {
|
||||||
if (parent.init !== node) return node;
|
if (parent.init !== node) return node;
|
||||||
return find_stop_value(parent, level + 1);
|
return find_stop_value(parent, level + 1);
|
||||||
}
|
}
|
||||||
@@ -3190,7 +3190,7 @@ merge(Compressor.prototype, {
|
|||||||
} else if (stat.init instanceof AST_Var) {
|
} else if (stat.init instanceof AST_Var) {
|
||||||
defs = stat.init;
|
defs = stat.init;
|
||||||
}
|
}
|
||||||
} else if (stat instanceof AST_ForIn) {
|
} else if (stat instanceof AST_ForEnumeration) {
|
||||||
if (defs && defs.TYPE == stat.init.TYPE) {
|
if (defs && defs.TYPE == stat.init.TYPE) {
|
||||||
var defns = defs.definitions.slice();
|
var defns = defs.definitions.slice();
|
||||||
stat.init = stat.init.definitions[0].name.convert_symbol(AST_SymbolRef, function(ref, name) {
|
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 === node) return node;
|
||||||
if (defs.TYPE != node.TYPE) return node;
|
if (defs.TYPE != node.TYPE) return node;
|
||||||
var parent = this.parent();
|
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;
|
if (!declarations_only(node)) return node;
|
||||||
defs.definitions = defs.definitions.concat(node.definitions);
|
defs.definitions = defs.definitions.concat(node.definitions);
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
@@ -5155,7 +5155,7 @@ merge(Compressor.prototype, {
|
|||||||
pop();
|
pop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_ForIn) {
|
if (node instanceof AST_ForEnumeration) {
|
||||||
node.object.walk(tw);
|
node.object.walk(tw);
|
||||||
push();
|
push();
|
||||||
segment.block = node;
|
segment.block = node;
|
||||||
@@ -5788,7 +5788,7 @@ merge(Compressor.prototype, {
|
|||||||
if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
|
if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
|
||||||
node.argname.transform(trimmer);
|
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
|
// place uninitialized names at the start
|
||||||
var body = [], head = [], tail = [];
|
var body = [], head = [], tail = [];
|
||||||
// for unused names whose initialization has
|
// for unused names whose initialization has
|
||||||
@@ -6456,7 +6456,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
var seq = node.to_assignments();
|
var seq = node.to_assignments();
|
||||||
var p = tt.parent();
|
var p = tt.parent();
|
||||||
if (p instanceof AST_ForIn && p.init === node) {
|
if (p instanceof AST_ForEnumeration && p.init === node) {
|
||||||
if (seq) return seq;
|
if (seq) return seq;
|
||||||
var def = node.definitions[0].name;
|
var def = node.definitions[0].name;
|
||||||
return make_node(AST_SymbolRef, def, def);
|
return make_node(AST_SymbolRef, def, def);
|
||||||
@@ -7330,7 +7330,7 @@ merge(Compressor.prototype, {
|
|||||||
return if_break_in_loop(self, compressor);
|
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)) {
|
if (compressor.option("varify") && (self.init instanceof AST_Const || self.init instanceof AST_Let)) {
|
||||||
var name = self.init.definitions[0].name;
|
var name = self.init.definitions[0].name;
|
||||||
if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
|
if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
|
||||||
@@ -8661,7 +8661,7 @@ merge(Compressor.prototype, {
|
|||||||
} else if (scope instanceof AST_For) {
|
} else if (scope instanceof AST_For) {
|
||||||
if (scope.init === child) continue;
|
if (scope.init === child) continue;
|
||||||
in_loop = [];
|
in_loop = [];
|
||||||
} else if (scope instanceof AST_ForIn) {
|
} else if (scope instanceof AST_ForEnumeration) {
|
||||||
if (scope.init === child) continue;
|
if (scope.init === child) continue;
|
||||||
if (scope.object === child) continue;
|
if (scope.object === child) continue;
|
||||||
in_loop = [];
|
in_loop = [];
|
||||||
@@ -10734,7 +10734,7 @@ merge(Compressor.prototype, {
|
|||||||
if (assigned) return self;
|
if (assigned) return self;
|
||||||
if (compressor.option("sequences")
|
if (compressor.option("sequences")
|
||||||
&& parent.TYPE != "Call"
|
&& 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);
|
var seq = lift_sequence_in_expression(self, compressor);
|
||||||
if (seq !== self) return seq.optimize(compressor);
|
if (seq !== self) return seq.optimize(compressor);
|
||||||
}
|
}
|
||||||
@@ -10857,7 +10857,7 @@ merge(Compressor.prototype, {
|
|||||||
if (is_lhs(compressor.self(), parent)) return self;
|
if (is_lhs(compressor.self(), parent)) return self;
|
||||||
if (compressor.option("sequences")
|
if (compressor.option("sequences")
|
||||||
&& parent.TYPE != "Call"
|
&& 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);
|
var seq = lift_sequence_in_expression(self, compressor);
|
||||||
if (seq !== self) return seq.optimize(compressor);
|
if (seq !== self) return seq.optimize(compressor);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -715,9 +715,13 @@ function OutputStream(options) {
|
|||||||
|| p instanceof AST_Conditional
|
|| p instanceof AST_Conditional
|
||||||
// [ a = (1, 2) ] = [] ---> a == 2
|
// [ a = (1, 2) ] = [] ---> a == 2
|
||||||
|| p instanceof AST_DefaultValue
|
|| 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
|
// { [(1, 2)]: 3 }[2] ---> 3
|
||||||
// { foo: (1, 2) }.foo ---> 2
|
// { foo: (1, 2) }.foo ---> 2
|
||||||
|| p instanceof AST_DestructuredKeyVal
|
|
||||||
|| p instanceof AST_ObjectProperty
|
|| p instanceof AST_ObjectProperty
|
||||||
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ---> 2
|
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ---> 2
|
||||||
|| p instanceof AST_PropAccess && p.expression === this
|
|| p instanceof AST_PropAccess && p.expression === this
|
||||||
@@ -978,20 +982,25 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
force_statement(self.body, output);
|
force_statement(self.body, output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_ForIn, function(output) {
|
function print_for_enum(prefix, infix) {
|
||||||
|
return function(output) {
|
||||||
var self = this;
|
var self = this;
|
||||||
output.print("for");
|
output.print(prefix);
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function() {
|
output.with_parens(function() {
|
||||||
self.init.print(output);
|
self.init.print(output);
|
||||||
output.space();
|
output.space();
|
||||||
output.print("in");
|
output.print(infix);
|
||||||
output.space();
|
output.space();
|
||||||
self.object.print(output);
|
self.object.print(output);
|
||||||
});
|
});
|
||||||
output.space();
|
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) {
|
DEFPRINT(AST_With, function(output) {
|
||||||
var self = this;
|
var self = this;
|
||||||
output.print("with");
|
output.print("with");
|
||||||
@@ -1226,7 +1235,7 @@ function OutputStream(options) {
|
|||||||
def.print(output);
|
def.print(output);
|
||||||
});
|
});
|
||||||
var p = output.parent();
|
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"));
|
DEFPRINT(AST_Const, print_definitinos("const"));
|
||||||
@@ -1253,7 +1262,7 @@ function OutputStream(options) {
|
|||||||
output.print("=");
|
output.print("=");
|
||||||
output.space();
|
output.space();
|
||||||
var p = output.parent(1);
|
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);
|
parenthesize_for_noin(self.value, output, noin);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
27
lib/parse.js
27
lib/parse.js
@@ -1039,6 +1039,7 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function for_() {
|
function for_() {
|
||||||
|
var await = is("name", "await") && next();
|
||||||
expect("(");
|
expect("(");
|
||||||
var init = null;
|
var init = null;
|
||||||
if (!is("punc", ";")) {
|
if (!is("punc", ";")) {
|
||||||
@@ -1049,16 +1050,26 @@ function parse($TEXT, options) {
|
|||||||
: is("keyword", "var")
|
: is("keyword", "var")
|
||||||
? (next(), var_(true))
|
? (next(), var_(true))
|
||||||
: expression(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 instanceof AST_Definitions) {
|
||||||
if (init.definitions.length > 1) {
|
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)) {
|
} 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_enum(ctor, init);
|
||||||
return for_in(init);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return regular_for(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();
|
var obj = expression();
|
||||||
expect(")");
|
expect(")");
|
||||||
return new AST_ForIn({
|
return new ctor({
|
||||||
init : init,
|
init : init,
|
||||||
object : obj,
|
object : obj,
|
||||||
body : in_loop(statement)
|
body : in_loop(statement)
|
||||||
@@ -1523,7 +1534,7 @@ function parse($TEXT, options) {
|
|||||||
func.end = prev();
|
func.end = prev();
|
||||||
return subscripts(func, allow_calls);
|
return subscripts(func, allow_calls);
|
||||||
}
|
}
|
||||||
if (is("name")) {
|
if (is("name") && is_token(peek(), "punc", "=>")) {
|
||||||
start = S.token;
|
start = S.token;
|
||||||
sym = _make_symbol(AST_SymbolRef, start);
|
sym = _make_symbol(AST_SymbolRef, start);
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
if (self.step) self.step = self.step.transform(tw);
|
if (self.step) self.step = self.step.transform(tw);
|
||||||
self.body = self.body.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.init = self.init.transform(tw);
|
||||||
self.object = self.object.transform(tw);
|
self.object = self.object.transform(tw);
|
||||||
self.body = self.body.transform(tw);
|
self.body = self.body.transform(tw);
|
||||||
|
|||||||
@@ -828,6 +828,21 @@ empty_for_in_prop_init: {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for_of: {
|
||||||
|
input: {
|
||||||
|
var async = [ "PASS", 42 ];
|
||||||
|
async.p = "FAIL";
|
||||||
|
for (async of (null, async))
|
||||||
|
console.log(async);
|
||||||
|
}
|
||||||
|
expect_exact: 'var async=["PASS",42];async.p="FAIL";for(async of(null,async))console.log(async);'
|
||||||
|
expect_stdout: [
|
||||||
|
"PASS",
|
||||||
|
"42",
|
||||||
|
]
|
||||||
|
node_version: ">=0.12"
|
||||||
|
}
|
||||||
|
|
||||||
issue_3631_1: {
|
issue_3631_1: {
|
||||||
options = {
|
options = {
|
||||||
dead_code: true,
|
dead_code: true,
|
||||||
|
|||||||
@@ -96,6 +96,48 @@ pause_resume: {
|
|||||||
node_version: ">=4"
|
node_version: ">=4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for_of: {
|
||||||
|
input: {
|
||||||
|
function* f() {
|
||||||
|
if (yield "PASS") yield "FAIL 1";
|
||||||
|
yield 42;
|
||||||
|
return "FAIL 2";
|
||||||
|
}
|
||||||
|
for (var a of f())
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_exact: 'function*f(){if(yield"PASS")yield"FAIL 1";yield 42;return"FAIL 2"}for(var a of f())console.log(a);'
|
||||||
|
expect_stdout: [
|
||||||
|
"PASS",
|
||||||
|
"42",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
for_await_of: {
|
||||||
|
input: {
|
||||||
|
async function* f() {
|
||||||
|
if (yield "PASS") yield "FAIL 1";
|
||||||
|
yield {
|
||||||
|
then: function(r) {
|
||||||
|
r(42);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return "FAIL 2";
|
||||||
|
}
|
||||||
|
(async function(a) {
|
||||||
|
for await (a of f())
|
||||||
|
console.log(a);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_exact: 'async function*f(){if(yield"PASS")yield"FAIL 1";yield{then:function(r){r(42)}};return"FAIL 2"}(async function(a){for await(a of f())console.log(a)})();'
|
||||||
|
expect_stdout: [
|
||||||
|
"PASS",
|
||||||
|
"42",
|
||||||
|
]
|
||||||
|
node_version: ">=10"
|
||||||
|
}
|
||||||
|
|
||||||
collapse_vars_1: {
|
collapse_vars_1: {
|
||||||
options = {
|
options = {
|
||||||
collapse_vars: true,
|
collapse_vars: true,
|
||||||
|
|||||||
@@ -624,7 +624,7 @@ describe("bin/uglifyjs", function() {
|
|||||||
"Parse error at test/input/invalid/for-in_1.js:2,5",
|
"Parse error at test/input/invalid/for-in_1.js:2,5",
|
||||||
"for (1, 2, a in b) {",
|
"for (1, 2, a in b) {",
|
||||||
" ^",
|
" ^",
|
||||||
"ERROR: Invalid left-hand side in for..in loop"
|
"ERROR: Invalid left-hand side in for..in/of loop"
|
||||||
].join("\n"));
|
].join("\n"));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -638,7 +638,7 @@ describe("bin/uglifyjs", function() {
|
|||||||
"Parse error at test/input/invalid/for-in_2.js:2,5",
|
"Parse error at test/input/invalid/for-in_2.js:2,5",
|
||||||
"for (var a, b in c) {",
|
"for (var a, b in c) {",
|
||||||
" ^",
|
" ^",
|
||||||
"ERROR: Only one variable declaration allowed in for..in loop"
|
"ERROR: Only one variable declaration allowed in for..in/of loop"
|
||||||
].join("\n"));
|
].join("\n"));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
if (parent instanceof U.AST_VarDef && parent.name === node) return;
|
if (parent instanceof U.AST_VarDef && parent.name === node) return;
|
||||||
// preserve for (var xxx; ...)
|
// preserve for (var xxx; ...)
|
||||||
if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Definitions) return node;
|
if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Definitions) return node;
|
||||||
// preserve for (xxx in ...)
|
// preserve for (xxx in/of ...)
|
||||||
if (parent instanceof U.AST_ForIn && parent.init === node) return node;
|
if (parent instanceof U.AST_ForEnumeration && parent.init === node) return node;
|
||||||
|
|
||||||
// node specific permutations with no parent logic
|
// node specific permutations with no parent logic
|
||||||
|
|
||||||
@@ -303,7 +303,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
return to_statement(expr);
|
return to_statement(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (node instanceof U.AST_ForIn) {
|
else if (node instanceof U.AST_ForEnumeration) {
|
||||||
var expr;
|
var expr;
|
||||||
switch ((node.start._permute * steps | 0) % 3) {
|
switch ((node.start._permute * steps | 0) % 3) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ var STMT_IF_ELSE = STMT_("ifelse");
|
|||||||
var STMT_DO_WHILE = STMT_("dowhile");
|
var STMT_DO_WHILE = STMT_("dowhile");
|
||||||
var STMT_WHILE = STMT_("while");
|
var STMT_WHILE = STMT_("while");
|
||||||
var STMT_FOR_LOOP = STMT_("forloop");
|
var STMT_FOR_LOOP = STMT_("forloop");
|
||||||
var STMT_FOR_IN = STMT_("forin");
|
var STMT_FOR_ENUM = STMT_("forenum");
|
||||||
var STMT_SEMI = STMT_("semi");
|
var STMT_SEMI = STMT_("semi");
|
||||||
var STMT_EXPR = STMT_("expr");
|
var STMT_EXPR = STMT_("expr");
|
||||||
var STMT_SWITCH = STMT_("switch");
|
var STMT_SWITCH = STMT_("switch");
|
||||||
@@ -142,6 +142,8 @@ var SUPPORT = function(matrix) {
|
|||||||
default_value: "[ a = 0 ] = [];",
|
default_value: "[ a = 0 ] = [];",
|
||||||
destructuring: "[] = [];",
|
destructuring: "[] = [];",
|
||||||
exponentiation: "0 ** 0",
|
exponentiation: "0 ** 0",
|
||||||
|
for_await_of: "async function f(a) { for await (a of []); }",
|
||||||
|
for_of: "for (var a of []);",
|
||||||
generator: "function* f(){}",
|
generator: "function* f(){}",
|
||||||
let: "let a;",
|
let: "let a;",
|
||||||
rest: "var [...a] = [];",
|
rest: "var [...a] = [];",
|
||||||
@@ -901,21 +903,58 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
||||||
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
|
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
|
||||||
return label.target + "for (var brake" + loop + " = 5; " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " && brake" + loop + " > 0; --brake" + loop + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
|
return label.target + "for (var brake" + loop + " = 5; " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " && brake" + loop + " > 0; --brake" + loop + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
|
||||||
case STMT_FOR_IN:
|
case STMT_FOR_ENUM:
|
||||||
var label = createLabel(canBreak, canContinue);
|
var label = createLabel(canBreak, canContinue);
|
||||||
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
||||||
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
|
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
|
||||||
var key = rng(10) ? "key" + loop : getVarName(NO_CONST);
|
var key = rng(10) ? "key" + loop : getVarName(NO_CONST);
|
||||||
return [
|
var of = SUPPORT.for_of && rng(20) == 0;
|
||||||
"{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ",
|
var init = "";
|
||||||
label.target + " for (",
|
if (!/^key/.test(key)) {
|
||||||
!/^key/.test(key) ? rng(10) ? "" : "var " : !SUPPORT.let || rng(10) ? "var " : rng(2) ? "let " : "const ",
|
if (!(of && bug_for_of_var) && rng(10) == 0) init = "var ";
|
||||||
!SUPPORT.destructuring || rng(10) ? key : rng(5) ? "[ " + key + " ]" : "{ length: " + key + " }",
|
} else if (!SUPPORT.let || !(of && bug_for_of_var) && rng(10)) {
|
||||||
" in expr" + loop + ") {",
|
init = "var ";
|
||||||
rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "",
|
} else if (rng(2)) {
|
||||||
createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
|
init = "let ";
|
||||||
"}}",
|
} else {
|
||||||
].join("");
|
init = "const ";
|
||||||
|
}
|
||||||
|
if (!SUPPORT.destructuring || of && !(canThrow && rng(10) == 0) || rng(10)) {
|
||||||
|
init += key;
|
||||||
|
} else if (rng(5)) {
|
||||||
|
init += "[ " + key + " ]";
|
||||||
|
} else {
|
||||||
|
init += "{ length: " + key + " }";
|
||||||
|
}
|
||||||
|
var s = "var expr" + loop + " = ";
|
||||||
|
if (of) {
|
||||||
|
var await = SUPPORT.for_await_of && async && rng(20) == 0;
|
||||||
|
if (SUPPORT.generator && rng(20) == 0) {
|
||||||
|
var gen = getVarName();
|
||||||
|
if (canThrow && rng(10) == 0) {
|
||||||
|
s += gen + "; ";
|
||||||
|
} else {
|
||||||
|
s += gen + " && typeof " + gen + "[Symbol.";
|
||||||
|
s += await ? "asyncIterator" : "iterator";
|
||||||
|
s += '] == "function" ? ' + gen + " : " + createArrayLiteral(recurmax, stmtDepth, canThrow) + "; ";
|
||||||
|
}
|
||||||
|
} else if (rng(5)) {
|
||||||
|
s += createArrayLiteral(recurmax, stmtDepth, canThrow) + "; ";
|
||||||
|
} else if (canThrow && rng(10) == 0) {
|
||||||
|
s += createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ";
|
||||||
|
} else {
|
||||||
|
s += '"" + (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "); ";
|
||||||
|
}
|
||||||
|
s += label.target + " for ";
|
||||||
|
if (await) s += "await ";
|
||||||
|
s += "(" + init + " of expr" + loop + ") {";
|
||||||
|
} else {
|
||||||
|
s += createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ";
|
||||||
|
s += label.target + " for (" + init + " in expr" + loop + ") {";
|
||||||
|
}
|
||||||
|
if (rng(3)) s += "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; ";
|
||||||
|
s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
|
||||||
|
return "{" + s + "}";
|
||||||
case STMT_SEMI:
|
case STMT_SEMI:
|
||||||
return use_strict && rng(20) === 0 ? '"use strict";' : ";";
|
return use_strict && rng(20) === 0 ? '"use strict";' : ";";
|
||||||
case STMT_EXPR:
|
case STMT_EXPR:
|
||||||
@@ -2048,12 +2087,13 @@ if (typeof sandbox.run_code("A:if (0) B:; else B:;") != "string") {
|
|||||||
if (o.mangle) o.mangle.v8 = true;
|
if (o.mangle) o.mangle.v8 = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var is_bug_async_arrow_rest = function() {};
|
var bug_async_arrow_rest = function() {};
|
||||||
if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && typeof sandbox.run_code("async (a = f(...[], b)) => 0;") != "string") {
|
if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && typeof sandbox.run_code("async (a = f(...[], b)) => 0;") != "string") {
|
||||||
is_bug_async_arrow_rest = function(ex) {
|
bug_async_arrow_rest = function(ex) {
|
||||||
return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter";
|
return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && typeof sandbox.run_code("try {} catch (e) { for (var e of []); }") != "string";
|
||||||
if (SUPPORT.destructuring && typeof sandbox.run_code("console.log([ 1 ], {} = 2);") != "string") {
|
if (SUPPORT.destructuring && typeof sandbox.run_code("console.log([ 1 ], {} = 2);") != "string") {
|
||||||
beautify_options.output.v8 = true;
|
beautify_options.output.v8 = true;
|
||||||
minify_options.forEach(function(o) {
|
minify_options.forEach(function(o) {
|
||||||
@@ -2083,7 +2123,7 @@ for (var round = 1; round <= num_iterations; round++) {
|
|||||||
println(result);
|
println(result);
|
||||||
println();
|
println();
|
||||||
// ignore v8 parser bug
|
// ignore v8 parser bug
|
||||||
return is_bug_async_arrow_rest(result);
|
return bug_async_arrow_rest(result);
|
||||||
})) continue;
|
})) continue;
|
||||||
minify_options.forEach(function(options) {
|
minify_options.forEach(function(options) {
|
||||||
var o = JSON.parse(options);
|
var o = JSON.parse(options);
|
||||||
@@ -2097,7 +2137,7 @@ for (var round = 1; round <= num_iterations; round++) {
|
|||||||
uglify_result = sandbox.run_code(uglify_code, toplevel);
|
uglify_result = sandbox.run_code(uglify_code, toplevel);
|
||||||
ok = sandbox.same_stdout(original_result, uglify_result);
|
ok = sandbox.same_stdout(original_result, uglify_result);
|
||||||
// ignore v8 parser bug
|
// ignore v8 parser bug
|
||||||
if (!ok && is_bug_async_arrow_rest(uglify_result)) ok = true;
|
if (!ok && bug_async_arrow_rest(uglify_result)) ok = true;
|
||||||
// ignore declaration order of global variables
|
// ignore declaration order of global variables
|
||||||
if (!ok && !toplevel) {
|
if (!ok && !toplevel) {
|
||||||
ok = sandbox.same_stdout(sandbox.run_code(sort_globals(original_code)), sandbox.run_code(sort_globals(uglify_code)));
|
ok = sandbox.same_stdout(sandbox.run_code(sort_globals(original_code)), sandbox.run_code(sort_globals(uglify_code)));
|
||||||
|
|||||||
Reference in New Issue
Block a user