diff --git a/src/ast-value-factory.h b/src/ast-value-factory.h index c2bfb83ac0..645b8b6631 100644 --- a/src/ast-value-factory.h +++ b/src/ast-value-factory.h @@ -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") \ diff --git a/src/parser.cc b/src/parser.cc index 90d6b6b378..9227272e09 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1074,7 +1074,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); } @@ -3162,21 +3163,79 @@ 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.initialization_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(); @@ -4376,7 +4435,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); } } diff --git a/src/preparser.cc b/src/preparser.cc index a12bf14e58..4b86d78597 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -1039,9 +1039,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); diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index bb171ad8e9..dc4ddb0af6 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -6555,6 +6555,7 @@ TEST(DestructuringPositiveTests) { {"function f(argument1, ", ") {}"}, {"var f = (", ") => {};"}, {"var f = (argument1,", ") => {};"}, + {"try {} catch(", ") {}"}, {NULL, NULL}}; // clang-format off @@ -6616,6 +6617,7 @@ TEST(DestructuringNegativeTests) { {"var f = (", ") => {};"}, {"var f = ", " => {};"}, {"var f = (argument1,", ") => {};"}, + {"try {} catch(", ") {}"}, {NULL, NULL}}; // clang-format off diff --git a/test/message/try-catch-lexical-conflict.js b/test/message/try-catch-lexical-conflict.js new file mode 100644 index 0000000000..efb1ec7e01 --- /dev/null +++ b/test/message/try-catch-lexical-conflict.js @@ -0,0 +1,11 @@ +// 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; +} diff --git a/test/message/try-catch-lexical-conflict.out b/test/message/try-catch-lexical-conflict.out new file mode 100644 index 0000000000..9dc1b54fd5 --- /dev/null +++ b/test/message/try-catch-lexical-conflict.out @@ -0,0 +1,4 @@ +*%(basename)s:10: SyntaxError: Identifier 'x' has already been declared + let x; + ^ +SyntaxError: Identifier 'x' has already been declared diff --git a/test/message/try-catch-variable-conflict.js b/test/message/try-catch-variable-conflict.js new file mode 100644 index 0000000000..9b0749b28c --- /dev/null +++ b/test/message/try-catch-variable-conflict.js @@ -0,0 +1,10 @@ +// 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; +} diff --git a/test/message/try-catch-variable-conflict.out b/test/message/try-catch-variable-conflict.out new file mode 100644 index 0000000000..c7fb8de510 --- /dev/null +++ b/test/message/try-catch-variable-conflict.out @@ -0,0 +1,4 @@ +*%(basename)s:9: SyntaxError: Identifier 'x' has already been declared + var x; + ^ +SyntaxError: Identifier 'x' has already been declared diff --git a/test/mjsunit/harmony/destructuring.js b/test/mjsunit/harmony/destructuring.js index 904058618a..7192d7aa5b 100644 --- a/test/mjsunit/harmony/destructuring.js +++ b/test/mjsunit/harmony/destructuring.js @@ -1098,8 +1098,36 @@ function(){ eval("(class{foo(a, {}) {'use strict';}});") }, SyntaxError); })(); + (function TestLegacyConstDestructuringInForLoop() { var result; for (const {foo} of [{foo: 1}]) { result = foo; } assertEquals(1, result); })(); + + +(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); +})();