[parser] Allow try {} catch (e) { for (var e of x) {} }

This patch changes the parser to allow for-of initializer
var-redeclaration of non-destructured catch parameters.

Previously, the spec allowed var-redeclaration of a
non-destructured catch parameter…

    try {} catch (e) { var e; }

…except in the particular case where the var declaration is
a for-of initializer:

    try {} catch (e) { for (var e of whatever) {} }

https://github.com/tc39/ecma262/pull/1393 removes this strange
exceptional case. This patch implements that change.

BUG=v8:8759

Change-Id: Ia4e33ac1eab89085f8a5fdb547f479cfa38bbee5
Reviewed-on: https://chromium-review.googlesource.com/c/1444954
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Mathias Bynens <mathias@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59209}
This commit is contained in:
Mathias Bynens 2019-01-30 06:47:03 +01:00 committed by Commit Bot
parent 09d42cc632
commit b645a259bd
4 changed files with 124 additions and 150 deletions

View File

@ -1183,40 +1183,6 @@ class ParserBase {
return identifier == ast_value_factory()->let_string();
}
void DesugarBindingInForEachStatement(ForInfo* for_info, BlockT* body_block,
ExpressionT* each_variable) {
// Annex B.3.5 prohibits the form
// `try {} catch(e) { for (var e of {}); }`
// So if we are parsing a statement like `for (var ... of ...)`
// we need to walk up the scope chain and look for catch scopes
// which have a simple binding, then compare their binding against
// all of the names declared in the init of the for-of we're
// parsing.
bool is_for_var_of =
for_info->mode == ForEachStatement::ITERATE &&
for_info->parsing_result.descriptor.mode == VariableMode::kVar;
if (is_for_var_of) {
Scope* scope = this->scope();
while (!scope->is_declaration_scope() ||
(scope->is_eval_scope() && is_sloppy(scope->language_mode()))) {
if (scope->is_catch_scope()) {
auto name = scope->catch_variable()->raw_name();
// If it's a simple binding and the name is declared in the for loop.
if (name != ast_value_factory()->dot_catch_string() &&
for_info->bound_names.Contains(name)) {
impl()->ReportMessageAt(for_info->parsing_result.bindings_loc,
MessageTemplate::kVarRedeclaration, name);
}
}
scope = scope->outer_scope();
}
}
impl()->DesugarBindingInForEachStatement(for_info, body_block,
each_variable);
}
bool IsNextLetKeyword();
// Checks if the expression is a valid reference expression (e.g., on the
@ -5531,7 +5497,8 @@ ParserBase<Impl>::ParseForEachStatementWithDeclarations(
}
impl()->RecordIterationStatementSourceRange(loop, body_range);
DesugarBindingInForEachStatement(for_info, &body_block, &each_variable);
impl()->DesugarBindingInForEachStatement(for_info, &body_block,
&each_variable);
body_block->statements()->Add(body, zone());
if (IsLexicalVariableMode(for_info->parsing_result.descriptor.mode)) {
@ -5787,7 +5754,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
if (has_declarations) {
BlockT body_block = impl()->NullBlock();
DesugarBindingInForEachStatement(&for_info, &body_block, &each_variable);
impl()->DesugarBindingInForEachStatement(&for_info, &body_block,
&each_variable);
body_block->statements()->Add(body, zone());
body_block->set_scope(scope()->FinalizeBlockScope());
body = body_block;

View File

@ -13,7 +13,7 @@ ${code}
assertUnreachable();
} catch (e) {
assertInstanceof(e, SyntaxError);
assertTrue( e.toString().indexOf("has already been declared") >= 0 );
assertTrue(e.toString().includes("has already been declared"));
}
}
@ -52,10 +52,10 @@ let not_var_e = [
'const {f:e}'
];
// Check that `for (var ... of ...)` cannot redeclare a simple catch variable
// but `for (var ... in ...)` can.
// Check that both `for (var ... of ...)` and `for (var ... in ...)`
// can redeclare a simple catch variable.
for (let binding of var_e) {
checkIsRedeclarationError(`
checkIsNotRedeclarationError(`
try {
throw 0;
} catch (e) {
@ -72,9 +72,9 @@ try {
`);
}
// Check that the above error occurs even for nested catches.
// Check that the above applies even for nested catches.
for (let binding of var_e) {
checkIsRedeclarationError(`
checkIsNotRedeclarationError(`
try {
throw 0;
} catch (e) {
@ -107,8 +107,8 @@ try {
`);
}
// Check that the above error does not occur if a declaration scope is between
// the catch and the loop.
// Check that the above applies if a declaration scope is between the
// catch and the loop.
for (let binding of var_e) {
checkIsNotRedeclarationError(`
try {
@ -122,7 +122,9 @@ try {
try {
throw 0;
} catch (e) {
(function(){for (${binding} of []);})();
(function() {
for (${binding} of []);
})();
}
`);
}
@ -139,7 +141,7 @@ try {
}
// Check that there is an error for both for-in and for-of when redeclaring
// a non-simple catch parameter
// a non-simple catch parameter.
for (let binding of var_e) {
checkIsRedeclarationError(`
try {

View File

@ -2,4 +2,4 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
assertThrows("try { } catch (e) { var e; for (var e of []) {} }")
assertDoesNotThrow("try { } catch (e) { var e; for (var e of []) {} }")

View File

@ -690,6 +690,10 @@
# https://bugs.chromium.org/p/v8/issues/detail?id=7187
'built-ins/Function/prototype/toString/line-terminator-normalisation-CR': [SKIP],
# https://bugs.chromium.org/p/v8/issues/detail?id=8759
'language/eval-code/direct/var-env-lower-lex-catch-non-strict': [SKIP],
'language/statements/try/early-catch-var': [SKIP],
############################ SLOW TESTS #############################
'annexB/built-ins/RegExp/RegExp-leading-escape-BMP': [PASS, SLOW],