From 42f30f4ded2b1ca0c4caa7639e6206e93c78ee70 Mon Sep 17 00:00:00 2001 From: dslomov Date: Mon, 22 Jun 2015 05:06:55 -0700 Subject: [PATCH] [destructuring] Implement parameter pattern matching. Scoping for initializers is yet incorrect. Defaults are not supported. R=arv@chromium.org,rossberg@chromium.org BUG=v8:811 LOG=N Review URL: https://codereview.chromium.org/1189743003 Cr-Commit-Position: refs/heads/master@{#29184} --- src/parser.cc | 137 +++++++++++++++++--------- src/parser.h | 105 ++++++++++++++------ src/pattern-rewriter.cc | 3 +- src/preparser.cc | 14 +-- src/preparser.h | 152 ++++++++++++++++++----------- src/scopes.cc | 15 ++- test/cctest/test-parsing.cc | 111 ++++++++++++++++++++- test/mjsunit/harmony/destructuring.js | 42 ++++++++ test/mjsunit/regress/regress-1130.js | 2 +- test/mjsunit/regress/regress-436896.js | 2 +- test/webkit/fast/js/arguments-expected.txt | 2 +- 11 files changed, 442 insertions(+), 143 deletions(-) diff --git a/src/parser.cc b/src/parser.cc index 7e0d936..6138592 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1182,7 +1182,7 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info, NewScope(scope_, ARROW_SCOPE, FunctionKind::kArrowFunction); scope->set_start_position(shared_info->start_position()); ExpressionClassifier formals_classifier; - bool has_rest = false; + ParserFormalParameterParsingState parsing_state(scope); { // Parsing patterns as variable reference expression creates // NewUnresolved references in current scope. Entrer arrow function @@ -1190,17 +1190,19 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info, BlockState block_state(&scope_, scope); if (Check(Token::LPAREN)) { // '(' StrictFormalParameters ')' - ParseFormalParameterList(scope, &has_rest, &formals_classifier, &ok); + ParseFormalParameterList(&parsing_state, &formals_classifier, &ok); if (ok) ok = Check(Token::RPAREN); } else { // BindingIdentifier - ParseFormalParameter(scope, has_rest, &formals_classifier, &ok); + const bool is_rest = false; + ParseFormalParameter(is_rest, &parsing_state, &formals_classifier, + &ok); } } if (ok) { Expression* expression = - ParseArrowFunctionLiteral(scope, has_rest, formals_classifier, &ok); + ParseArrowFunctionLiteral(parsing_state, formals_classifier, &ok); if (ok) { // Scanning must end at the same position that was recorded // previously. If not, parsing has been interrupted due to a stack @@ -1547,7 +1549,7 @@ ZoneList* Parser::ParseNamedImports(int pos, bool* ok) { VariableProxy* proxy = NewUnresolved(local_name, IMPORT); ImportDeclaration* declaration = factory()->NewImportDeclaration(proxy, import_name, NULL, scope_, pos); - Declare(declaration, true, CHECK_OK); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); result->Add(declaration, zone()); if (peek() == Token::RBRACE) break; Expect(Token::COMMA, CHECK_OK); @@ -1595,7 +1597,8 @@ Statement* Parser::ParseImportDeclaration(bool* ok) { VariableProxy* proxy = NewUnresolved(local_name, IMPORT); import_default_declaration = factory()->NewImportDeclaration( proxy, ast_value_factory()->default_string(), NULL, scope_, pos); - Declare(import_default_declaration, true, CHECK_OK); + Declare(import_default_declaration, DeclarationDescriptor::NORMAL, true, + CHECK_OK); } const AstRawString* module_instance_binding = NULL; @@ -1982,7 +1985,9 @@ VariableProxy* Parser::NewUnresolved(const AstRawString* name, } -Variable* Parser::Declare(Declaration* declaration, bool resolve, bool* ok) { +Variable* Parser::Declare(Declaration* declaration, + DeclarationDescriptor::Kind declaration_kind, + bool resolve, bool* ok) { VariableProxy* proxy = declaration->proxy(); DCHECK(proxy->raw_name() != NULL); const AstRawString* name = proxy->raw_name(); @@ -2041,11 +2046,15 @@ Variable* Parser::Declare(Declaration* declaration, bool resolve, bool* ok) { if (is_strict(language_mode())) { // In harmony we treat re-declarations as early errors. See // ES5 16 for a definition of early errors. - ParserTraits::ReportMessage(MessageTemplate::kVarRedeclaration, name); + if (declaration_kind == DeclarationDescriptor::NORMAL) { + ParserTraits::ReportMessage(MessageTemplate::kVarRedeclaration, name); + } else { + ParserTraits::ReportMessage(MessageTemplate::kStrictParamDupe); + } *ok = false; return nullptr; } - Expression* expression = NewThrowTypeError( + Expression* expression = NewThrowSyntaxError( MessageTemplate::kVarRedeclaration, name, declaration->position()); declaration_scope->SetIllegalRedeclaration(expression); } else if (mode == VAR) { @@ -2155,7 +2164,7 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) { VariableProxy* proxy = NewUnresolved(name, VAR); Declaration* declaration = factory()->NewVariableDeclaration(proxy, VAR, scope_, pos); - Declare(declaration, true, CHECK_OK); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); NativeFunctionLiteral* lit = factory()->NewNativeFunctionLiteral( name, extension_, RelocInfo::kNoPosition); return factory()->NewExpressionStatement( @@ -2200,7 +2209,7 @@ Statement* Parser::ParseFunctionDeclaration( VariableProxy* proxy = NewUnresolved(name, mode); Declaration* declaration = factory()->NewFunctionDeclaration(proxy, mode, fun, scope_, pos); - Declare(declaration, true, CHECK_OK); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); if (names) names->Add(name, zone()); return factory()->NewEmptyStatement(RelocInfo::kNoPosition); } @@ -2241,7 +2250,8 @@ Statement* Parser::ParseClassDeclaration(ZoneList* names, Declaration* declaration = factory()->NewVariableDeclaration( proxy, mode, scope_, pos, is_class_declaration, scope_->class_declaration_group_start()); - Variable* outer_class_variable = Declare(declaration, true, CHECK_OK); + Variable* outer_class_variable = + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); proxy->var()->set_initializer_position(position()); // This is needed because a class ("class Name { }") creates two bindings (one // in the outer scope, and one in the class scope). The method is a function @@ -2397,6 +2407,7 @@ void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, // BindingPattern '=' AssignmentExpression parsing_result->descriptor.parser = this; + parsing_result->descriptor.declaration_kind = DeclarationDescriptor::NORMAL; parsing_result->descriptor.declaration_pos = peek_position(); parsing_result->descriptor.initialization_pos = peek_position(); parsing_result->descriptor.mode = VAR; @@ -3328,7 +3339,7 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( VariableProxy* proxy = NewUnresolved(names->at(i), mode); Declaration* declaration = factory()->NewVariableDeclaration( proxy, mode, scope_, RelocInfo::kNoPosition); - Declare(declaration, true, CHECK_OK); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); inner_vars.Add(declaration->proxy()->var(), zone()); VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i)); Assignment* assignment = @@ -3784,9 +3795,10 @@ Handle CompileTimeValue::GetElements(Handle value) { void ParserTraits::ParseArrowFunctionFormalParameters( - Scope* scope, Expression* expr, const Scanner::Location& params_loc, - bool* has_rest, Scanner::Location* duplicate_loc, bool* ok) { - if (scope->num_parameters() >= Code::kMaxArguments) { + ParserFormalParameterParsingState* parsing_state, Expression* expr, + const Scanner::Location& params_loc, Scanner::Location* duplicate_loc, + bool* ok) { + if (parsing_state->scope->num_parameters() >= Code::kMaxArguments) { ReportMessageAt(params_loc, MessageTemplate::kMalformedArrowFunParamList); *ok = false; return; @@ -3812,7 +3824,7 @@ void ParserTraits::ParseArrowFunctionFormalParameters( DCHECK_EQ(binop->op(), Token::COMMA); Expression* left = binop->left(); Expression* right = binop->right(); - ParseArrowFunctionFormalParameters(scope, left, params_loc, has_rest, + ParseArrowFunctionFormalParameters(parsing_state, left, params_loc, duplicate_loc, ok); if (!*ok) return; // LHS of comma expression should be unparenthesized. @@ -3820,30 +3832,25 @@ void ParserTraits::ParseArrowFunctionFormalParameters( } // Only the right-most expression may be a rest parameter. - DCHECK(!*has_rest); + DCHECK(!parsing_state->has_rest); + bool is_rest = false; if (expr->IsSpread()) { - *has_rest = true; + is_rest = true; expr = expr->AsSpread()->expression(); } - if (!expr->IsVariableProxy()) { - // TODO(dslomov): support pattern desugaring - return; + if (expr->IsVariableProxy()) { + // When the formal parameter was originally seen, it was parsed as a + // VariableProxy and recorded as unresolved in the scope. Here we undo that + // parse-time side-effect for parameters that are single-names (not + // patterns; for patterns that happens uniformly in + // PatternRewriter::VisitVariableProxy). + parser_->scope_->RemoveUnresolved(expr->AsVariableProxy()); } - DCHECK(!expr->AsVariableProxy()->is_this()); - - const AstRawString* raw_name = expr->AsVariableProxy()->raw_name(); - Scanner::Location param_location(expr->position(), - expr->position() + raw_name->length()); - - // When the formal parameter was originally seen, it was parsed as a - // VariableProxy and recorded as unresolved in the scope. Here we undo that - // parse-time side-effect. - parser_->scope_->RemoveUnresolved(expr->AsVariableProxy()); ExpressionClassifier classifier; - DeclareFormalParameter(scope, expr, &classifier, *has_rest); + DeclareFormalParameter(parsing_state, expr, &classifier, is_rest); if (!duplicate_loc->IsValid()) { *duplicate_loc = classifier.duplicate_formal_parameter_error().location; } @@ -3918,7 +3925,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ZoneList* body = NULL; int materialized_literal_count = -1; int expected_property_count = -1; - ExpressionClassifier formals_classifier; + DuplicateFinder duplicate_finder(scanner()->unicode_cache()); + ExpressionClassifier formals_classifier(&duplicate_finder); FunctionLiteral::EagerCompileHint eager_compile_hint = parenthesized_function_ ? FunctionLiteral::kShouldEagerCompile : FunctionLiteral::kShouldLazyCompile; @@ -3944,18 +3952,18 @@ FunctionLiteral* Parser::ParseFunctionLiteral( function_state.set_generator_object_variable(temp); } - bool has_rest = false; Expect(Token::LPAREN, CHECK_OK); int start_position = scanner()->location().beg_pos; scope_->set_start_position(start_position); - num_parameters = ParseFormalParameterList(scope, &has_rest, - &formals_classifier, CHECK_OK); + ParserFormalParameterParsingState parsing_state(scope); + num_parameters = + ParseFormalParameterList(&parsing_state, &formals_classifier, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); int formals_end_position = scanner()->location().end_pos; - CheckArityRestrictions(num_parameters, arity_restriction, has_rest, - start_position, formals_end_position, CHECK_OK); - + CheckArityRestrictions(num_parameters, arity_restriction, + parsing_state.has_rest, start_position, + formals_end_position, CHECK_OK); Expect(Token::LBRACE, CHECK_OK); // If we have a named function expression, we add a local variable @@ -4050,8 +4058,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral( } } if (!is_lazily_parsed) { - body = ParseEagerFunctionBody(function_name, pos, fvar, fvar_init_op, - kind, CHECK_OK); + body = ParseEagerFunctionBody(function_name, pos, parsing_state, fvar, + fvar_init_op, kind, CHECK_OK); materialized_literal_count = function_state.materialized_literal_count(); expected_property_count = function_state.expected_property_count(); @@ -4071,7 +4079,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral( CheckFunctionName(language_mode(), kind, function_name, name_is_strict_reserved, function_name_location, CHECK_OK); - const bool use_strict_params = has_rest || IsConciseMethod(kind); + const bool use_strict_params = + !parsing_state.is_simple_parameter_list || IsConciseMethod(kind); const bool allow_duplicate_parameters = is_sloppy(language_mode()) && !use_strict_params; ValidateFormalParameters(&formals_classifier, language_mode(), @@ -4233,8 +4242,40 @@ Statement* Parser::BuildAssertIsCoercible(Variable* var) { } +Block* Parser::BuildParameterInitializationBlock( + const ParserFormalParameterParsingState& formal_parameters, bool* ok) { + DCHECK(scope_->is_function_scope()); + Block* init_block = nullptr; + for (auto parameter : formal_parameters.params) { + if (parameter.pattern == nullptr) continue; + if (init_block == nullptr) { + init_block = factory()->NewBlock(NULL, 1, true, RelocInfo::kNoPosition); + } + + DeclarationDescriptor descriptor; + descriptor.declaration_kind = DeclarationDescriptor::PARAMETER; + descriptor.parser = this; + descriptor.declaration_scope = scope_; + descriptor.scope = scope_; + descriptor.mode = LET; + descriptor.is_const = false; + descriptor.needs_init = true; + descriptor.declaration_pos = parameter.pattern->position(); + descriptor.initialization_pos = parameter.pattern->position(); + descriptor.init_op = Token::INIT_LET; + DeclarationParsingResult::Declaration decl( + parameter.pattern, parameter.pattern->position(), + factory()->NewVariableProxy(parameter.var)); + PatternRewriter::DeclareAndInitializeVariables(init_block, &descriptor, + &decl, nullptr, CHECK_OK); + } + return init_block; +} + + ZoneList* Parser::ParseEagerFunctionBody( - const AstRawString* function_name, int pos, Variable* fvar, + const AstRawString* function_name, int pos, + const ParserFormalParameterParsingState& formal_parameters, Variable* fvar, Token::Value fvar_init_op, FunctionKind kind, bool* ok) { // Everything inside an eagerly parsed function will be parsed eagerly // (see comment above). @@ -4258,6 +4299,12 @@ ZoneList* Parser::ParseEagerFunctionBody( AddAssertIsConstruct(body, pos); } + auto init_block = + BuildParameterInitializationBlock(formal_parameters, CHECK_OK); + if (init_block != nullptr) { + body->Add(init_block, zone()); + } + // For generators, allocate and yield an iterator on function entry. if (IsGeneratorFunction(kind)) { ZoneList* arguments = @@ -4384,7 +4431,7 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name, Declaration* declaration = factory()->NewVariableDeclaration( proxy, CONST, block_scope, pos, is_class_declaration, scope_->class_declaration_group_start()); - Declare(declaration, true, CHECK_OK); + Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK); } Expression* extends = NULL; diff --git a/src/parser.h b/src/parser.h index 39d0d92..103bafe 100644 --- a/src/parser.h +++ b/src/parser.h @@ -538,6 +538,27 @@ class RegExpParser BASE_EMBEDDED { class Parser; class SingletonLogger; + +struct ParserFormalParameterParsingState + : public PreParserFormalParameterParsingState { + struct Parameter { + Parameter(Variable* var, Expression* pattern) + : var(var), pattern(pattern) {} + Variable* var; + Expression* pattern; + }; + + explicit ParserFormalParameterParsingState(Scope* scope) + : PreParserFormalParameterParsingState(scope), params(4, scope->zone()) {} + + ZoneList params; + + void AddParameter(Variable* var, Expression* pattern) { + params.Add(Parameter(var, pattern), scope->zone()); + } +}; + + class ParserTraits { public: struct Type { @@ -560,7 +581,7 @@ class ParserTraits { typedef ZoneList* ExpressionList; typedef ZoneList* PropertyList; typedef const v8::internal::AstRawString* FormalParameter; - typedef Scope FormalParameterScope; + typedef ParserFormalParameterParsingState FormalParameterParsingState; typedef ZoneList* StatementList; // For constructing objects returned by the traversing functions. @@ -751,17 +772,21 @@ class ParserTraits { ZoneList* NewStatementList(int size, Zone* zone) { return new(zone) ZoneList(size, zone); } + + V8_INLINE void AddParameterInitializationBlock( + const ParserFormalParameterParsingState& formal_parameters, + ZoneList* body, bool* ok); + V8_INLINE Scope* NewScope(Scope* parent_scope, ScopeType scope_type, FunctionKind kind = kNormalFunction); - V8_INLINE void DeclareFormalParameter(Scope* scope, Expression* name, - ExpressionClassifier* classifier, - bool is_rest); - void ParseArrowFunctionFormalParameters(Scope* scope, Expression* params, - const Scanner::Location& params_loc, - bool* has_rest, - Scanner::Location* duplicate_loc, - bool* ok); + V8_INLINE void DeclareFormalParameter( + ParserFormalParameterParsingState* parsing_state, Expression* name, + ExpressionClassifier* classifier, bool is_rest); + void ParseArrowFunctionFormalParameters( + ParserFormalParameterParsingState* scope, Expression* params, + const Scanner::Location& params_loc, Scanner::Location* duplicate_loc, + bool* ok); // Temporary glue; these functions will move to ParserBase. Expression* ParseV8Intrinsic(bool* ok); @@ -774,8 +799,9 @@ class ParserTraits { int* materialized_literal_count, int* expected_property_count, bool* ok, Scanner::BookmarkScope* bookmark = nullptr); V8_INLINE ZoneList* ParseEagerFunctionBody( - const AstRawString* name, int pos, Variable* fvar, - Token::Value fvar_init_op, FunctionKind kind, bool* ok); + const AstRawString* name, int pos, + const ParserFormalParameterParsingState& formal_parameters, + Variable* fvar, Token::Value fvar_init_op, FunctionKind kind, bool* ok); ClassLiteral* ParseClassLiteral(const AstRawString* name, Scanner::Location class_name_location, @@ -936,6 +962,7 @@ class Parser : public ParserBase { bool* ok); struct DeclarationDescriptor { + enum Kind { NORMAL, PARAMETER }; Parser* parser; Scope* declaration_scope; Scope* scope; @@ -945,6 +972,7 @@ class Parser : public ParserBase { int declaration_pos; int initialization_pos; Token::Value init_op; + Kind declaration_kind; }; struct DeclarationParsingResult { @@ -1095,7 +1123,9 @@ class Parser : public ParserBase { // Parser support VariableProxy* NewUnresolved(const AstRawString* name, VariableMode mode); - Variable* Declare(Declaration* declaration, bool resolve, bool* ok); + Variable* Declare(Declaration* declaration, + DeclarationDescriptor::Kind declaration_kind, bool resolve, + bool* ok); bool TargetStackContainsLabel(const AstRawString* label); BreakableStatement* LookupBreakTarget(const AstRawString* label, bool* ok); @@ -1121,10 +1151,14 @@ class Parser : public ParserBase { PreParser::PreParseResult ParseLazyFunctionBodyWithPreParser( SingletonLogger* logger, Scanner::BookmarkScope* bookmark = nullptr); + Block* BuildParameterInitializationBlock( + const ParserFormalParameterParsingState& formal_parameters, bool* ok); + // Consumes the ending }. ZoneList* ParseEagerFunctionBody( - const AstRawString* function_name, int pos, Variable* fvar, - Token::Value fvar_init_op, FunctionKind kind, bool* ok); + const AstRawString* function_name, int pos, + const ParserFormalParameterParsingState& formal_parameters, + Variable* fvar, Token::Value fvar_init_op, FunctionKind kind, bool* ok); void ThrowPendingError(Isolate* isolate, Handle