Commit a316db99 authored by adamk's avatar adamk Committed by Commit bot

[es6] Implement destructuring binding in try/catch

The approach is to desugar

  try { ... }
  catch ({x, y}) { ... }

into

  try { ... }
  catch (.catch) {
    let x = .catch.x;
    let y = .catch.y;
    ...
  }

using the PatternRewriter's normal facilities. This has the side benefit
of throwing the appropriate variable conflict errors for declarations
made inside the catch block.

No change is made to non-destructured cases, which will hopefully save
us some work if https://github.com/tc39/ecma262/issues/150 is adopted
in the spec.

There's one big problem with this patch, which is a lack of PreParser
support for the redeclaration errors. But it seems we're already lacking
good PreParser support for such errors, so I'm not sure that should
block this moving forward.

BUG=v8:811
LOG=y

Review URL: https://codereview.chromium.org/1417483014

Cr-Commit-Position: refs/heads/master@{#31797}
parent 19bad2a6
......@@ -255,6 +255,7 @@ class AstValue : public ZoneObject {
F(dot_module, ".module") \
F(dot_result, ".result") \
F(dot_switch_tag, ".switch_tag") \
F(dot_catch, ".catch") \
F(empty, "") \
F(eval, "eval") \
F(let, "let") \
......
......@@ -1073,7 +1073,8 @@ FunctionLiteral* Parser::DoParseProgram(ParseInfo* info) {
// unchanged if the property already exists.
InsertSloppyBlockFunctionVarBindings(scope, &ok);
}
if (ok && (is_strict(language_mode()) || allow_harmony_sloppy())) {
if (ok && (is_strict(language_mode()) || allow_harmony_sloppy() ||
allow_harmony_destructuring())) {
CheckConflictingVarDeclarations(scope_, &ok);
}
......@@ -3148,21 +3149,78 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
Scope* catch_scope = NULL;
Variable* catch_variable = NULL;
Block* catch_block = NULL;
const AstRawString* name = NULL;
if (tok == Token::CATCH) {
Consume(Token::CATCH);
Expect(Token::LPAREN, CHECK_OK);
catch_scope = NewScope(scope_, CATCH_SCOPE);
catch_scope->set_start_position(scanner()->location().beg_pos);
name = ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
ExpressionClassifier pattern_classifier;
Expression* pattern = ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
ValidateBindingPattern(&pattern_classifier, CHECK_OK);
const AstRawString* name = ast_value_factory()->dot_catch_string();
bool is_simple = pattern->IsVariableProxy();
if (is_simple) {
auto proxy = pattern->AsVariableProxy();
scope_->RemoveUnresolved(proxy);
name = proxy->raw_name();
}
catch_variable = catch_scope->DeclareLocal(name, VAR, kCreatedInitialized,
Variable::NORMAL);
BlockState block_state(&scope_, catch_scope);
catch_block = ParseBlock(NULL, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
{
BlockState block_state(&scope_, catch_scope);
// TODO(adamk): Make a version of ParseScopedBlock that takes a scope and
// a block.
catch_block =
factory()->NewBlock(nullptr, 16, false, RelocInfo::kNoPosition);
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
block_scope->set_start_position(scanner()->location().beg_pos);
{
BlockState block_state(&scope_, block_scope);
Target target(&this->target_stack_, catch_block);
if (!is_simple) {
DeclarationDescriptor descriptor;
descriptor.declaration_kind = DeclarationDescriptor::NORMAL;
descriptor.parser = this;
descriptor.declaration_scope = scope_;
descriptor.scope = scope_;
descriptor.hoist_scope = nullptr;
descriptor.mode = LET;
descriptor.is_const = false;
descriptor.needs_init = true;
descriptor.declaration_pos = pattern->position();
descriptor.init_op = Token::INIT_LET;
DeclarationParsingResult::Declaration decl(
pattern, pattern->position(),
factory()->NewVariableProxy(catch_variable));
PatternRewriter::DeclareAndInitializeVariables(
catch_block, &descriptor, &decl, nullptr, CHECK_OK);
}
Expect(Token::LBRACE, CHECK_OK);
while (peek() != Token::RBRACE) {
Statement* stat = ParseStatementListItem(CHECK_OK);
if (stat && !stat->IsEmpty()) {
catch_block->statements()->Add(stat, zone());
}
}
Consume(Token::RBRACE);
}
block_scope->set_end_position(scanner()->location().end_pos);
block_scope = block_scope->FinalizeBlockScope();
catch_block->set_scope(block_scope);
}
catch_scope->set_end_position(scanner()->location().end_pos);
tok = peek();
......@@ -4364,7 +4422,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
if (is_sloppy(language_mode) && allow_harmony_sloppy_function()) {
InsertSloppyBlockFunctionVarBindings(scope, CHECK_OK);
}
if (is_strict(language_mode) || allow_harmony_sloppy()) {
if (is_strict(language_mode) || allow_harmony_sloppy() ||
allow_harmony_destructuring()) {
CheckConflictingVarDeclarations(scope, CHECK_OK);
}
}
......
......@@ -1029,9 +1029,12 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
if (tok == Token::CATCH) {
Consume(Token::CATCH);
Expect(Token::LPAREN, CHECK_OK);
ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
ExpressionClassifier pattern_classifier;
ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
ValidateBindingPattern(&pattern_classifier, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
{
// TODO(adamk): Make this CATCH_SCOPE
Scope* with_scope = NewScope(scope_, WITH_SCOPE);
BlockState block_state(&scope_, with_scope);
ParseBlock(CHECK_OK);
......
......@@ -6514,6 +6514,7 @@ TEST(DestructuringPositiveTests) {
{"function f(argument1, ", ") {}"},
{"var f = (", ") => {};"},
{"var f = (argument1,", ") => {};"},
{"try {} catch(", ") {}"},
{NULL, NULL}};
// clang-format off
......@@ -6575,6 +6576,7 @@ TEST(DestructuringNegativeTests) {
{"var f = (", ") => {};"},
{"var f = ", " => {};"},
{"var f = (argument1,", ") => {};"},
{"try {} catch(", ") {}"},
{NULL, NULL}};
// clang-format off
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-destructuring
"use strict";
try {
} catch ({x}) {
let x;
}
*%(basename)s:10: SyntaxError: Identifier 'x' has already been declared
let x;
^
SyntaxError: Identifier 'x' has already been declared
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-destructuring
try {
} catch ({x}) {
var x;
}
*%(basename)s:9: SyntaxError: Identifier 'x' has already been declared
var x;
^
SyntaxError: Identifier 'x' has already been declared
......@@ -1107,3 +1107,30 @@
(function TestDestructuringArrayWithoutInitializer() {
assertThrows('var [foo]', TypeError);
})();
(function TestCatch() {
"use strict";
// For testing proper scoping.
var foo = "hello", bar = "world", baz = 42;
try {
throw {foo: 1, bar: 2};
} catch ({foo, bar, baz = 3}) {
assertEquals(1, foo);
assertEquals(2, bar);
assertEquals(3, baz);
}
try {
throw [1, 2, 3];
} catch ([foo, ...bar]) {
assertEquals(1, foo);
assertEquals([2, 3], bar);
}
assertEquals("hello", foo);
assertEquals("world", bar);
assertEquals(42, baz);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment