Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: src/parsing/parser.cc

Issue 2301183003: Move sloppy block function hoisting logic from Parser to Scope. (Closed)
Patch Set: code review (adamk@) Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/parsing/parser.h ('k') | src/parsing/pattern-rewriter.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/parsing/parser.h" 5 #include "src/parsing/parser.h"
6 6
7 #include <memory> 7 #include <memory>
8 8
9 #include "src/api.h" 9 #include "src/api.h"
10 #include "src/ast/ast-expression-rewriter.h" 10 #include "src/ast/ast-expression-rewriter.h"
(...skipping 701 matching lines...) Expand 10 before | Expand all | Expand 10 after
712 if (ok && is_strict(language_mode())) { 712 if (ok && is_strict(language_mode())) {
713 CheckStrictOctalLiteral(beg_pos, scanner()->location().end_pos, &ok); 713 CheckStrictOctalLiteral(beg_pos, scanner()->location().end_pos, &ok);
714 CheckDecimalLiteralWithLeadingZero(use_counts_, beg_pos, 714 CheckDecimalLiteralWithLeadingZero(use_counts_, beg_pos,
715 scanner()->location().end_pos); 715 scanner()->location().end_pos);
716 } 716 }
717 if (ok && is_sloppy(language_mode())) { 717 if (ok && is_sloppy(language_mode())) {
718 // TODO(littledan): Function bindings on the global object that modify 718 // TODO(littledan): Function bindings on the global object that modify
719 // pre-existing bindings should be made writable, enumerable and 719 // pre-existing bindings should be made writable, enumerable and
720 // nonconfigurable if possible, whereas this code will leave attributes 720 // nonconfigurable if possible, whereas this code will leave attributes
721 // unchanged if the property already exists. 721 // unchanged if the property already exists.
722 InsertSloppyBlockFunctionVarBindings(scope, nullptr, &ok); 722 InsertSloppyBlockFunctionVarBindings(scope, &ok);
723 } 723 }
724 if (ok) { 724 if (ok) {
725 CheckConflictingVarDeclarations(scope, &ok); 725 CheckConflictingVarDeclarations(scope, &ok);
726 } 726 }
727 727
728 if (ok && info->parse_restriction() == ONLY_SINGLE_FUNCTION_LITERAL) { 728 if (ok && info->parse_restriction() == ONLY_SINGLE_FUNCTION_LITERAL) {
729 if (body->length() != 1 || 729 if (body->length() != 1 ||
730 !body->at(0)->IsExpressionStatement() || 730 !body->at(0)->IsExpressionStatement() ||
731 !body->at(0)->AsExpressionStatement()-> 731 !body->at(0)->AsExpressionStatement()->
732 expression()->IsFunctionLiteral()) { 732 expression()->IsFunctionLiteral()) {
(...skipping 889 matching lines...) Expand 10 before | Expand all | Expand 10 after
1622 VariableProxy* Parser::NewUnresolved(const AstRawString* name, int begin_pos, 1622 VariableProxy* Parser::NewUnresolved(const AstRawString* name, int begin_pos,
1623 int end_pos, Variable::Kind kind) { 1623 int end_pos, Variable::Kind kind) {
1624 return scope()->NewUnresolved(factory(), name, begin_pos, end_pos, kind); 1624 return scope()->NewUnresolved(factory(), name, begin_pos, end_pos, kind);
1625 } 1625 }
1626 1626
1627 VariableProxy* Parser::NewUnresolved(const AstRawString* name) { 1627 VariableProxy* Parser::NewUnresolved(const AstRawString* name) {
1628 return scope()->NewUnresolved(factory(), name, scanner()->location().beg_pos, 1628 return scope()->NewUnresolved(factory(), name, scanner()->location().beg_pos,
1629 scanner()->location().end_pos); 1629 scanner()->location().end_pos);
1630 } 1630 }
1631 1631
1632 InitializationFlag Parser::DefaultInitializationFlag(VariableMode mode) {
1633 DCHECK(IsDeclaredVariableMode(mode));
1634 return mode == VAR ? kCreatedInitialized : kNeedsInitialization;
1635 }
1636
1637 Declaration* Parser::DeclareVariable(const AstRawString* name, 1632 Declaration* Parser::DeclareVariable(const AstRawString* name,
1638 VariableMode mode, int pos, bool* ok) { 1633 VariableMode mode, int pos, bool* ok) {
1639 return DeclareVariable(name, mode, DefaultInitializationFlag(mode), pos, ok); 1634 return DeclareVariable(name, mode, Variable::DefaultInitializationFlag(mode),
1635 pos, ok);
1640 } 1636 }
1641 1637
1642 Declaration* Parser::DeclareVariable(const AstRawString* name, 1638 Declaration* Parser::DeclareVariable(const AstRawString* name,
1643 VariableMode mode, InitializationFlag init, 1639 VariableMode mode, InitializationFlag init,
1644 int pos, bool* ok) { 1640 int pos, bool* ok) {
1645 DCHECK_NOT_NULL(name); 1641 DCHECK_NOT_NULL(name);
1646 Scope* scope = 1642 Scope* scope =
1647 IsLexicalVariableMode(mode) ? this->scope() : GetDeclarationScope(); 1643 IsLexicalVariableMode(mode) ? this->scope() : GetDeclarationScope();
1648 VariableProxy* proxy = 1644 VariableProxy* proxy =
1649 scope->NewUnresolved(factory(), name, scanner()->location().beg_pos, 1645 scope->NewUnresolved(factory(), name, scanner()->location().beg_pos,
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
1792 // a script scope, or the initial scope of eval or another function. 1788 // a script scope, or the initial scope of eval or another function.
1793 VariableMode mode = 1789 VariableMode mode =
1794 (!scope()->is_declaration_scope() || scope()->is_module_scope()) ? LET 1790 (!scope()->is_declaration_scope() || scope()->is_module_scope()) ? LET
1795 : VAR; 1791 : VAR;
1796 VariableProxy* proxy = NewUnresolved(variable_name); 1792 VariableProxy* proxy = NewUnresolved(variable_name);
1797 Declaration* declaration = 1793 Declaration* declaration =
1798 factory()->NewFunctionDeclaration(proxy, fun, scope(), pos); 1794 factory()->NewFunctionDeclaration(proxy, fun, scope(), pos);
1799 Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized, 1795 Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized,
1800 CHECK_OK); 1796 CHECK_OK);
1801 if (names) names->Add(variable_name, zone()); 1797 if (names) names->Add(variable_name, zone());
1802 EmptyStatement* empty = factory()->NewEmptyStatement(kNoSourcePosition);
1803 // Async functions don't undergo sloppy mode block scoped hoisting, and don't 1798 // Async functions don't undergo sloppy mode block scoped hoisting, and don't
1804 // allow duplicates in a block. Both are represented by the 1799 // allow duplicates in a block. Both are represented by the
1805 // sloppy_block_function_map. Don't add them to the map for async functions. 1800 // sloppy_block_function_map. Don't add them to the map for async functions.
1806 // Generators are also supposed to be prohibited; currently doing this behind 1801 // Generators are also supposed to be prohibited; currently doing this behind
1807 // a flag and UseCounting violations to assess web compatibility. 1802 // a flag and UseCounting violations to assess web compatibility.
1808 if (is_sloppy(language_mode()) && !scope()->is_declaration_scope() && 1803 if (is_sloppy(language_mode()) && !scope()->is_declaration_scope() &&
1809 !is_async && !(allow_harmony_restrictive_generators() && is_generator)) { 1804 !is_async && !(allow_harmony_restrictive_generators() && is_generator)) {
1810 SloppyBlockFunctionStatement* delegate = 1805 SloppyBlockFunctionStatement* delegate =
1811 factory()->NewSloppyBlockFunctionStatement(empty, scope()); 1806 factory()->NewSloppyBlockFunctionStatement(scope());
1812 DeclarationScope* target_scope = GetDeclarationScope(); 1807 DeclarationScope* target_scope = GetDeclarationScope();
1813 target_scope->DeclareSloppyBlockFunction(variable_name, delegate); 1808 target_scope->DeclareSloppyBlockFunction(variable_name, delegate);
1814 return delegate; 1809 return delegate;
1815 } 1810 }
1816 return empty; 1811 return factory()->NewEmptyStatement(kNoSourcePosition);
1817 } 1812 }
1818 1813
1819 Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names, 1814 Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names,
1820 bool default_export, bool* ok) { 1815 bool default_export, bool* ok) {
1821 // ClassDeclaration :: 1816 // ClassDeclaration ::
1822 // 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}' 1817 // 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}'
1823 // 'class' ('extends' LeftHandExpression)? '{' ClassBody '}' 1818 // 'class' ('extends' LeftHandExpression)? '{' ClassBody '}'
1824 // 1819 //
1825 // The anonymous form is allowed iff [default_export] is true. 1820 // The anonymous form is allowed iff [default_export] is true.
1826 // 1821 //
(...skipping 2539 matching lines...) Expand 10 before | Expand all | Expand 10 after
4366 4361
4367 if (!parameters.is_simple) { 4362 if (!parameters.is_simple) {
4368 DCHECK_NOT_NULL(inner_scope); 4363 DCHECK_NOT_NULL(inner_scope);
4369 DCHECK_EQ(function_scope, scope()); 4364 DCHECK_EQ(function_scope, scope());
4370 DCHECK_EQ(function_scope, inner_scope->outer_scope()); 4365 DCHECK_EQ(function_scope, inner_scope->outer_scope());
4371 DCHECK_EQ(body, inner_block->statements()); 4366 DCHECK_EQ(body, inner_block->statements());
4372 SetLanguageMode(function_scope, inner_scope->language_mode()); 4367 SetLanguageMode(function_scope, inner_scope->language_mode());
4373 Block* init_block = BuildParameterInitializationBlock(parameters, CHECK_OK); 4368 Block* init_block = BuildParameterInitializationBlock(parameters, CHECK_OK);
4374 4369
4375 if (is_sloppy(inner_scope->language_mode())) { 4370 if (is_sloppy(inner_scope->language_mode())) {
4376 InsertSloppyBlockFunctionVarBindings(inner_scope, function_scope, 4371 InsertSloppyBlockFunctionVarBindings(inner_scope, CHECK_OK);
4377 CHECK_OK);
4378 } 4372 }
4379 4373
4380 // TODO(littledan): Merge the two rejection blocks into one 4374 // TODO(littledan): Merge the two rejection blocks into one
4381 if (IsAsyncFunction(kind)) { 4375 if (IsAsyncFunction(kind)) {
4382 init_block = BuildRejectPromiseOnException(init_block, CHECK_OK); 4376 init_block = BuildRejectPromiseOnException(init_block, CHECK_OK);
4383 } 4377 }
4384 4378
4385 DCHECK_NOT_NULL(init_block); 4379 DCHECK_NOT_NULL(init_block);
4386 4380
4387 inner_scope->set_end_position(scanner()->location().end_pos); 4381 inner_scope->set_end_position(scanner()->location().end_pos);
4388 if (inner_scope->FinalizeBlockScope() != nullptr) { 4382 if (inner_scope->FinalizeBlockScope() != nullptr) {
4389 CheckConflictingVarDeclarations(inner_scope, CHECK_OK); 4383 CheckConflictingVarDeclarations(inner_scope, CHECK_OK);
4390 InsertShadowingVarBindingInitializers(inner_block); 4384 InsertShadowingVarBindingInitializers(inner_block);
4391 } 4385 }
4392 inner_scope = nullptr; 4386 inner_scope = nullptr;
4393 4387
4394 result->Add(init_block, zone()); 4388 result->Add(init_block, zone());
4395 result->Add(inner_block, zone()); 4389 result->Add(inner_block, zone());
4396 } else { 4390 } else {
4397 DCHECK_EQ(inner_scope, function_scope); 4391 DCHECK_EQ(inner_scope, function_scope);
4398 if (is_sloppy(function_scope->language_mode())) { 4392 if (is_sloppy(function_scope->language_mode())) {
4399 InsertSloppyBlockFunctionVarBindings(function_scope, nullptr, CHECK_OK); 4393 InsertSloppyBlockFunctionVarBindings(function_scope, CHECK_OK);
4400 } 4394 }
4401 } 4395 }
4402 4396
4403 if (function_type == FunctionLiteral::kNamedExpression) { 4397 if (function_type == FunctionLiteral::kNamedExpression) {
4404 // Now that we know the language mode, we can create the const assignment 4398 // Now that we know the language mode, we can create the const assignment
4405 // in the previously reserved spot. 4399 // in the previously reserved spot.
4406 DCHECK_EQ(function_scope, scope()); 4400 DCHECK_EQ(function_scope, scope());
4407 Variable* fvar = function_scope->DeclareFunctionVar(function_name); 4401 Variable* fvar = function_scope->DeclareFunctionVar(function_name);
4408 VariableProxy* fproxy = factory()->NewVariableProxy(fvar); 4402 VariableProxy* fproxy = factory()->NewVariableProxy(fvar);
4409 result->Set(kFunctionNameAssignmentIndex, 4403 result->Set(kFunctionNameAssignmentIndex,
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
4476 scope()->SetScopeName(name); 4470 scope()->SetScopeName(name);
4477 #endif 4471 #endif
4478 4472
4479 VariableProxy* proxy = nullptr; 4473 VariableProxy* proxy = nullptr;
4480 if (name != nullptr) { 4474 if (name != nullptr) {
4481 proxy = NewUnresolved(name); 4475 proxy = NewUnresolved(name);
4482 // TODO(verwaest): declare via block_state. 4476 // TODO(verwaest): declare via block_state.
4483 Declaration* declaration = 4477 Declaration* declaration =
4484 factory()->NewVariableDeclaration(proxy, block_state.scope(), pos); 4478 factory()->NewVariableDeclaration(proxy, block_state.scope(), pos);
4485 Declare(declaration, DeclarationDescriptor::NORMAL, CONST, 4479 Declare(declaration, DeclarationDescriptor::NORMAL, CONST,
4486 DefaultInitializationFlag(CONST), CHECK_OK); 4480 Variable::DefaultInitializationFlag(CONST), CHECK_OK);
4487 } 4481 }
4488 4482
4489 Expression* extends = nullptr; 4483 Expression* extends = nullptr;
4490 if (Check(Token::EXTENDS)) { 4484 if (Check(Token::EXTENDS)) {
4491 block_state.set_start_position(scanner()->location().end_pos); 4485 block_state.set_start_position(scanner()->location().end_pos);
4492 ExpressionClassifier extends_classifier(this); 4486 ExpressionClassifier extends_classifier(this);
4493 extends = ParseLeftHandSideExpression(CHECK_OK); 4487 extends = ParseLeftHandSideExpression(CHECK_OK);
4494 CheckNoTailCallExpressions(CHECK_OK); 4488 CheckNoTailCallExpressions(CHECK_OK);
4495 RewriteNonPattern(CHECK_OK); 4489 RewriteNonPattern(CHECK_OK);
4496 impl()->AccumulateFormalParameterContainmentErrors(); 4490 impl()->AccumulateFormalParameterContainmentErrors();
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
4679 VariableProxy* from = factory()->NewVariableProxy(parameter); 4673 VariableProxy* from = factory()->NewVariableProxy(parameter);
4680 Expression* assignment = 4674 Expression* assignment =
4681 factory()->NewAssignment(Token::ASSIGN, to, from, kNoSourcePosition); 4675 factory()->NewAssignment(Token::ASSIGN, to, from, kNoSourcePosition);
4682 Statement* statement = 4676 Statement* statement =
4683 factory()->NewExpressionStatement(assignment, kNoSourcePosition); 4677 factory()->NewExpressionStatement(assignment, kNoSourcePosition);
4684 inner_block->statements()->InsertAt(0, statement, zone()); 4678 inner_block->statements()->InsertAt(0, statement, zone());
4685 } 4679 }
4686 } 4680 }
4687 4681
4688 void Parser::InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope, 4682 void Parser::InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope,
4689 Scope* complex_params_scope,
4690 bool* ok) { 4683 bool* ok) {
4691 // For each variable which is used as a function declaration in a sloppy 4684 scope->HoistSloppyBlockFunctions(factory(), ok);
nickie 2016/09/05 11:53:21 If I understand it right, this separation of logic
marja 2016/09/06 12:16:10 Yes, this is correct, and this is also why this CL
4692 // block, 4685 DCHECK(*ok);
4686 if (!*ok) return;
nickie 2016/09/05 11:53:21 Because of the DCHECK in HoistSloppyBlockFunctions
marja 2016/09/06 12:16:10 Will do (I'll do all review comments at one go, as
marja 2016/09/07 08:21:22 Done.
4687
4693 SloppyBlockFunctionMap* map = scope->sloppy_block_function_map(); 4688 SloppyBlockFunctionMap* map = scope->sloppy_block_function_map();
4694 for (ZoneHashMap::Entry* p = map->Start(); p != nullptr; p = map->Next(p)) { 4689 for (ZoneHashMap::Entry* p = map->Start(); p != nullptr; p = map->Next(p)) {
4695 AstRawString* name = static_cast<AstRawString*>(p->key); 4690 // Write in assignments to var for each block-scoped function declaration
4696 4691 auto delegates = static_cast<SloppyBlockFunctionStatement*>(p->value);
4697 // If the variable wouldn't conflict with a lexical declaration 4692 for (SloppyBlockFunctionStatement* delegate = delegates;
4698 // or parameter, 4693 delegate != nullptr; delegate = delegate->next()) {
4699 4694 if (delegate->to() == nullptr) {
4700 // Check if there's a conflict with a parameter.
4701 // This depends on the fact that functions always have a scope solely to
4702 // hold complex parameters, and the names local to that scope are
4703 // precisely the names of the parameters. IsDeclaredParameter(name) does
4704 // not hold for names declared by complex parameters, nor are those
4705 // bindings necessarily declared lexically, so we have to check for them
4706 // explicitly. On the other hand, if there are not complex parameters,
4707 // it is sufficient to just check IsDeclaredParameter.
4708 if (complex_params_scope != nullptr) {
4709 if (complex_params_scope->LookupLocal(name) != nullptr) {
4710 continue; 4695 continue;
4711 } 4696 }
4712 } else { 4697 Expression* assignment = factory()->NewAssignment(
4713 if (scope->IsDeclaredParameter(name)) { 4698 Token::ASSIGN, delegate->to(), delegate->from(), kNoSourcePosition);
4714 continue;
4715 }
4716 }
4717
4718 bool var_created = false;
4719
4720 // Write in assignments to var for each block-scoped function declaration
4721 auto delegates = static_cast<SloppyBlockFunctionStatement*>(p->value);
4722
4723 DeclarationScope* decl_scope = scope;
4724 while (decl_scope->is_eval_scope()) {
4725 decl_scope = decl_scope->outer_scope()->GetDeclarationScope();
4726 }
4727 Scope* outer_scope = decl_scope->outer_scope();
4728
4729 for (SloppyBlockFunctionStatement* delegate = delegates;
4730 delegate != nullptr; delegate = delegate->next()) {
4731 // Check if there's a conflict with a lexical declaration
4732 Scope* query_scope = delegate->scope()->outer_scope();
4733 Variable* var = nullptr;
4734 bool should_hoist = true;
4735
4736 // Note that we perform this loop for each delegate named 'name',
4737 // which may duplicate work if those delegates share scopes.
4738 // It is not sufficient to just do a Lookup on query_scope: for
4739 // example, that does not prevent hoisting of the function in
4740 // `{ let e; try {} catch (e) { function e(){} } }`
4741 do {
4742 var = query_scope->LookupLocal(name);
4743 if (var != nullptr && IsLexicalVariableMode(var->mode())) {
4744 should_hoist = false;
4745 break;
4746 }
4747 query_scope = query_scope->outer_scope();
4748 } while (query_scope != outer_scope);
4749
4750 if (!should_hoist) continue;
4751
4752 // Declare a var-style binding for the function in the outer scope
4753 if (!var_created) {
4754 var_created = true;
4755 VariableProxy* proxy = scope->NewUnresolved(factory(), name);
4756 Declaration* declaration =
4757 factory()->NewVariableDeclaration(proxy, scope, kNoSourcePosition);
4758 Declare(declaration, DeclarationDescriptor::NORMAL, VAR,
4759 DefaultInitializationFlag(VAR), ok, scope);
4760 DCHECK(*ok); // Based on the preceding check, this should not fail
4761 if (!*ok) return;
4762 }
4763
4764 // Read from the local lexical scope and write to the function scope
4765 VariableProxy* to = scope->NewUnresolved(factory(), name);
4766 VariableProxy* from = delegate->scope()->NewUnresolved(factory(), name);
4767 Expression* assignment =
4768 factory()->NewAssignment(Token::ASSIGN, to, from, kNoSourcePosition);
4769 Statement* statement = 4699 Statement* statement =
4770 factory()->NewExpressionStatement(assignment, kNoSourcePosition); 4700 factory()->NewExpressionStatement(assignment, kNoSourcePosition);
4771 delegate->set_statement(statement); 4701 delegate->set_statement(statement);
4772 } 4702 }
4773 } 4703 }
4774 } 4704 }
4775 4705
4776 4706
4777 // ---------------------------------------------------------------------------- 4707 // ----------------------------------------------------------------------------
4778 // Parser support 4708 // Parser support
(...skipping 1674 matching lines...) Expand 10 before | Expand all | Expand 10 after
6453 node->Print(Isolate::Current()); 6383 node->Print(Isolate::Current());
6454 } 6384 }
6455 #endif // DEBUG 6385 #endif // DEBUG
6456 6386
6457 #undef CHECK_OK 6387 #undef CHECK_OK
6458 #undef CHECK_OK_VOID 6388 #undef CHECK_OK_VOID
6459 #undef CHECK_FAILED 6389 #undef CHECK_FAILED
6460 6390
6461 } // namespace internal 6391 } // namespace internal
6462 } // namespace v8 6392 } // namespace v8
OLDNEW
« no previous file with comments | « src/parsing/parser.h ('k') | src/parsing/pattern-rewriter.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698