From 8be0499fced0c41fa2c00a4b53d234413941f056 Mon Sep 17 00:00:00 2001 From: wingo Date: Tue, 21 Apr 2015 07:44:18 -0700 Subject: [PATCH] Allow eval/arguments in arrow functions Originally landed in https://codereview.chromium.org/1061983004; re-landing after re-landing formal parameter parsing refactors. R=marja@chromium.org BUG=v8:4020 LOG=N Review URL: https://codereview.chromium.org/1077153005 Cr-Commit-Position: refs/heads/master@{#27971} --- src/messages.js | 4 +- src/parser.cc | 208 ++++++++++++++++++++++++-------------------- src/parser.h | 16 ++-- src/preparser.h | 164 +++++++++++++++++----------------- test/cctest/test-parsing.cc | 91 ++++++++++++++++--- 5 files changed, 287 insertions(+), 196 deletions(-) diff --git a/src/messages.js b/src/messages.js index 048732d..04e107d 100644 --- a/src/messages.js +++ b/src/messages.js @@ -166,7 +166,9 @@ var kMessages = { for_of_loop_initializer: ["for-of loop variable declaration may not have an initializer."], for_inof_loop_multi_bindings: ["Invalid left-hand side in ", "%0", " loop: Must have a single binding."], bad_getter_arity: ["Getter must not have any formal parameters."], - bad_setter_arity: ["Setter must have exactly one formal parameter."] + bad_setter_arity: ["Setter must have exactly one formal parameter."], + this_formal_parameter: ["'this' is not a valid formal parameter name"], + duplicate_arrow_function_formal_parameter: ["Arrow function may not have duplicate parameter names"] }; diff --git a/src/parser.cc b/src/parser.cc index b7bead7..c73b6d8 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -787,16 +787,7 @@ Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name, Scope* scope, AstNodeFactory* factory) { if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name); - - // Arrow function parameters are parsed as an expression. When - // parsing lazily, it is enough to create a VariableProxy in order - // for Traits::DeclareArrowParametersFromExpression() to be able to - // pick the names of the parameters. - return parser_->parsing_lazy_arrow_parameters_ - ? factory->NewVariableProxy(name, Variable::NORMAL, start_position, - end_position) - : scope->NewUnresolved(factory, name, start_position, - end_position); + return scope->NewUnresolved(factory, name, start_position, end_position); } @@ -861,7 +852,6 @@ Parser::Parser(ParseInfo* info) target_stack_(NULL), compile_options_(info->compile_options()), cached_parse_data_(NULL), - parsing_lazy_arrow_parameters_(false), total_preparse_skipped_(0), pre_parse_timer_(NULL), parsing_on_main_thread_(true) { @@ -1142,27 +1132,37 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info, bool ok = true; if (shared_info->is_arrow()) { - // The first expression being parsed is the parameter list of the arrow - // function. Setting this avoids prevents ExpressionFromIdentifier() - // from creating unresolved variables in already-resolved scopes. - parsing_lazy_arrow_parameters_ = true; - Expression* expression = ParseExpression(false, &ok); + Scope* scope = NewScope(scope_, ARROW_SCOPE); + scope->set_start_position(shared_info->start_position()); + FormalParameterErrorLocations error_locs; + bool has_rest = false; + if (Check(Token::LPAREN)) { + // '(' StrictFormalParameters ')' + ParseFormalParameterList(scope, &error_locs, &has_rest, &ok); + if (ok) ok = Check(Token::RPAREN); + } else { + // BindingIdentifier + ParseFormalParameter(scope, &error_locs, has_rest, &ok); + } + if (ok) { - // Scanning must end at the same position that was recorded - // previously. If not, parsing has been interrupted due to a - // stack overflow, at which point the partially parsed arrow - // function concise body happens to be a valid expression. This - // is a problem only for arrow functions with single statement - // bodies, since there is no end token such as "}" for normal - // functions. - if (scanner()->location().end_pos == shared_info->end_position()) { - // The pre-parser saw an arrow function here, so the full parser - // must produce a FunctionLiteral. - DCHECK(expression->IsFunctionLiteral()); - result = expression->AsFunctionLiteral(); - } else { - result = NULL; - ok = false; + Expression* expression = + ParseArrowFunctionLiteral(scope, error_locs, has_rest, &ok); + if (ok) { + // Scanning must end at the same position that was recorded + // previously. If not, parsing has been interrupted due to a stack + // overflow, at which point the partially parsed arrow function + // concise body happens to be a valid expression. This is a problem + // only for arrow functions with single expression bodies, since there + // is no end token such as "}" for normal functions. + if (scanner()->location().end_pos == shared_info->end_position()) { + // The pre-parser saw an arrow function here, so the full parser + // must produce a FunctionLiteral. + DCHECK(expression->IsFunctionLiteral()); + result = expression->AsFunctionLiteral(); + } else { + ok = false; + } } } } else if (shared_info->is_default_constructor()) { @@ -3732,79 +3732,103 @@ Handle CompileTimeValue::GetElements(Handle value) { } -bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression, - Scope* scope, int* num_params, - FormalParameterErrorLocations* locs) { - // Case for empty parameter lists: - // () => ... - if (expression == NULL) return true; - - // Too many parentheses around expression: - // (( ... )) => ... - if (expression->is_multi_parenthesized()) return false; - - // Case for a single parameter: - // (foo) => ... - // foo => ... - if (expression->IsVariableProxy()) { - if (expression->AsVariableProxy()->is_this()) return false; +void ParserTraits::DeclareArrowFunctionParameters( + Scope* scope, Expression* expr, const Scanner::Location& params_loc, + FormalParameterErrorLocations* error_locs, bool* ok) { + if (scope->num_parameters() >= Code::kMaxArguments) { + ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list"); + *ok = false; + return; + } - const AstRawString* raw_name = expression->AsVariableProxy()->raw_name(); - if (traits->IsEvalOrArguments(raw_name) || - traits->IsFutureStrictReserved(raw_name)) - return false; - if (traits->IsUndefined(raw_name) && !locs->undefined.IsValid()) { - locs->undefined = Scanner::Location( - expression->position(), expression->position() + raw_name->length()); + // ArrowFunctionFormals :: + // Binary(Token::COMMA, ArrowFunctionFormals, VariableProxy) + // VariableProxy + // + // As we need to visit the parameters in left-to-right order, we recurse on + // the left-hand side of comma expressions. + // + // Sadly, for the various malformed_arrow_function_parameter_list errors, we + // can't be more specific on the error message or on the location because we + // need to match the pre-parser's behavior. + if (expr->IsBinaryOperation()) { + BinaryOperation* binop = expr->AsBinaryOperation(); + if (binop->op() != Token::COMMA) { + ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list"); + *ok = false; + return; } - - // When the variable was seen, it was recorded as unresolved in the outer - // scope. But it's really not unresolved. - scope->outer_scope()->RemoveUnresolved(expression->AsVariableProxy()); - - bool is_rest = false; - bool is_duplicate = false; - scope->DeclareParameter(raw_name, VAR, is_rest, &is_duplicate); - if (is_duplicate) { - locs->duplicate = Scanner::Location( - expression->position(), expression->position() + raw_name->length()); - return false; + Expression* left = binop->left(); + Expression* right = binop->right(); + if (left->is_parenthesized() || right->is_parenthesized()) { + ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list"); + *ok = false; + return; } + DeclareArrowFunctionParameters(scope, left, params_loc, error_locs, ok); + if (!*ok) return; + // LHS of comma expression should be unparenthesized. + expr = right; + } - ++(*num_params); - return true; + // TODO(wingo): Support rest parameters. + if (!expr->IsVariableProxy()) { + ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list"); + *ok = false; + return; } - // Case for more than one parameter: - // (foo, bar [, ...]) => ... - if (expression->IsBinaryOperation()) { - BinaryOperation* binop = expression->AsBinaryOperation(); - if (binop->op() != Token::COMMA || binop->left()->is_parenthesized() || - binop->right()->is_parenthesized()) - return false; + const AstRawString* raw_name = expr->AsVariableProxy()->raw_name(); + Scanner::Location param_location(expr->position(), + expr->position() + raw_name->length()); - return CheckAndDeclareArrowParameter(traits, binop->left(), scope, - num_params, locs) && - CheckAndDeclareArrowParameter(traits, binop->right(), scope, - num_params, locs); + if (expr->AsVariableProxy()->is_this()) { + ReportMessageAt(param_location, "this_formal_parameter"); + *ok = false; + return; } - // Any other kind of expression is not a valid parameter list. - return false; + if (!error_locs->eval_or_arguments.IsValid() && IsEvalOrArguments(raw_name)) + error_locs->eval_or_arguments = param_location; + if (!error_locs->reserved.IsValid() && IsFutureStrictReserved(raw_name)) + error_locs->reserved = param_location; + if (!error_locs->undefined.IsValid() && IsUndefined(raw_name)) + error_locs->undefined = param_location; + + // 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()); + + bool is_rest = false; + bool is_duplicate = DeclareFormalParameter(scope, raw_name, is_rest); + + if (is_duplicate) { + // Arrow function parameter lists are parsed as StrictFormalParameters, + // which means that they cannot have duplicates. Note that this is a subset + // of the restrictions placed on parameters to functions whose body is + // strict. + ReportMessageAt(param_location, + "duplicate_arrow_function_formal_parameter"); + *ok = false; + return; + } } -int ParserTraits::DeclareArrowParametersFromExpression( - Expression* expression, Scope* scope, FormalParameterErrorLocations* locs, - bool* ok) { - int num_params = 0; - // Always reset the flag: It only needs to be set for the first expression - // parsed as arrow function parameter list, because only top-level functions - // are parsed lazily. - parser_->parsing_lazy_arrow_parameters_ = false; - *ok = - CheckAndDeclareArrowParameter(this, expression, scope, &num_params, locs); - return num_params; +void ParserTraits::ParseArrowFunctionFormalParameters( + Scope* scope, Expression* params, const Scanner::Location& params_loc, + FormalParameterErrorLocations* error_locs, bool* is_rest, bool* ok) { + // Too many parentheses around expression: + // (( ... )) => ... + if (params->is_multi_parenthesized()) { + // TODO(wingo): Make a better message. + ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list"); + *ok = false; + return; + } + + DeclareArrowFunctionParameters(scope, params, params_loc, error_locs, ok); } diff --git a/src/parser.h b/src/parser.h index a985d71..3b3144e 100644 --- a/src/parser.h +++ b/src/parser.h @@ -696,7 +696,6 @@ class ParserTraits { static Expression* EmptyExpression() { return NULL; } - static Expression* EmptyArrowParamList() { return NULL; } static Literal* EmptyLiteral() { return NULL; } @@ -746,11 +745,6 @@ class ParserTraits { V8_INLINE Scope* NewScope(Scope* parent_scope, ScopeType scope_type, FunctionKind kind = kNormalFunction); - // Utility functions - int DeclareArrowParametersFromExpression(Expression* expression, Scope* scope, - FormalParameterErrorLocations* locs, - bool* ok); - bool DeclareFormalParameter(Scope* scope, const AstRawString* name, bool is_rest) { bool is_duplicate = false; @@ -764,6 +758,14 @@ class ParserTraits { return is_duplicate; } + void DeclareArrowFunctionParameters(Scope* scope, Expression* expr, + const Scanner::Location& params_loc, + FormalParameterErrorLocations* error_locs, + bool* ok); + void ParseArrowFunctionFormalParameters( + Scope* scope, Expression* params, const Scanner::Location& params_loc, + FormalParameterErrorLocations* error_locs, bool* is_rest, bool* ok); + // Temporary glue; these functions will move to ParserBase. Expression* ParseV8Intrinsic(bool* ok); FunctionLiteral* ParseFunctionLiteral( @@ -1060,8 +1062,6 @@ class Parser : public ParserBase { ScriptCompiler::CompileOptions compile_options_; ParseData* cached_parse_data_; - bool parsing_lazy_arrow_parameters_; // for lazily parsed arrow functions. - PendingCompilationErrorHandler pending_error_handler_; // Other information which will be stored in Parser and moved to Isolate after diff --git a/src/preparser.h b/src/preparser.h index e112e4a..0643c30 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -527,6 +527,11 @@ class ParserBase : public Traits { *ok = false; return; } + if (is_strict(language_mode) && locs.reserved.IsValid()) { + Traits::ReportMessageAt(locs.reserved, "unexpected_strict_reserved"); + *ok = false; + return; + } if (is_strong(language_mode) && locs.undefined.IsValid()) { Traits::ReportMessageAt(locs.undefined, "strong_undefined"); *ok = false; @@ -539,11 +544,6 @@ class ParserBase : public Traits { *ok = false; return; } - if (locs.reserved.IsValid()) { - Traits::ReportMessageAt(locs.reserved, "unexpected_strict_reserved"); - *ok = false; - return; - } } // Determine precedence of given token. @@ -621,8 +621,9 @@ class ParserBase : public Traits { ExpressionT ParseMemberExpression(bool* ok); ExpressionT ParseMemberExpressionContinuation(ExpressionT expression, bool* ok); - ExpressionT ParseArrowFunctionLiteral(int start_pos, ExpressionT params_ast, - bool* ok); + ExpressionT ParseArrowFunctionLiteral( + Scope* function_scope, const FormalParameterErrorLocations& error_locs, + bool has_rest, bool* ok); ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool* ok); void AddTemplateExpression(ExpressionT); ExpressionT ParseSuperExpression(bool is_new, bool* ok); @@ -846,12 +847,6 @@ class PreParserExpression { IsValidArrowParamListField::encode(valid_arrow_param_list)); } - static PreParserExpression EmptyArrowParamList() { - // Any expression for which IsValidArrowParamList() returns true - // will work here. - return FromIdentifier(PreParserIdentifier::Default()); - } - static PreParserExpression StringLiteral() { return PreParserExpression(TypeField::encode(kStringLiteralExpression)); } @@ -942,20 +937,27 @@ class PreParserExpression { return IsIdentifier() || IsProperty(); } - bool IsValidArrowParamList(FormalParameterErrorLocations* locs) const { + bool IsValidArrowParamList(FormalParameterErrorLocations* locs, + const Scanner::Location& params_loc) const { ValidArrowParam valid = ValidateArrowParams(); if (ParenthesizationField::decode(code_) == kMultiParenthesizedExpression) { return false; } - if (valid == kValidArrowParam) { - return true; - } else if (valid == kInvalidStrongArrowParam) { - // Return true for now regardless of strong mode for compatibility with - // parser. - locs->undefined = Scanner::Location(); - return true; - } else { - return false; + switch (valid) { + case kInvalidArrowParam: + return false; + case kInvalidStrongArrowParam: + locs->undefined = params_loc; + return true; + case kInvalidStrictReservedArrowParam: + locs->reserved = params_loc; + return true; + case kInvalidStrictEvalArgumentsArrowParam: + locs->eval_or_arguments = params_loc; + return true; + default: + DCHECK_EQ(valid, kValidArrowParam); + return true; } } @@ -1022,8 +1024,12 @@ class PreParserExpression { kNoTemplateTagExpression }; + // These validity constraints are ordered such that a value of N implies lack + // of errors M < N. enum ValidArrowParam { kInvalidArrowParam, + kInvalidStrictEvalArgumentsArrowParam, + kInvalidStrictReservedArrowParam, kInvalidStrongArrowParam, kValidArrowParam }; @@ -1039,13 +1045,16 @@ class PreParserExpression { return kInvalidArrowParam; } PreParserIdentifier ident = AsIdentifier(); - // A valid identifier can be an arrow function parameter - // except for eval, arguments, yield, and reserved keywords. - if (ident.IsEval() || ident.IsArguments() || - ident.IsFutureStrictReserved()) { - return kInvalidArrowParam; + // In strict mode, eval and arguments are not valid formal parameter names. + if (ident.IsEval() || ident.IsArguments()) { + return kInvalidStrictEvalArgumentsArrowParam; } - // In strong mode, 'undefined' is similarly restricted. + // In strict mode, future reserved words are not valid either, and as they + // produce different errors we allot them their own error code. + if (ident.IsFutureStrictReserved()) { + return kInvalidStrictReservedArrowParam; + } + // In strong mode, 'undefined' isn't a valid formal parameter name either. if (ident.IsUndefined()) { return kInvalidStrongArrowParam; } @@ -1062,7 +1071,7 @@ class PreParserExpression { ExpressionTypeField; typedef BitField IsUseStrictField; typedef BitField IsUseStrongField; - typedef BitField + typedef BitField IsValidArrowParamListField; typedef BitField IdentifierTypeField; @@ -1463,9 +1472,6 @@ class PreParserTraits { static PreParserExpression EmptyExpression() { return PreParserExpression::Default(); } - static PreParserExpression EmptyArrowParamList() { - return PreParserExpression::EmptyArrowParamList(); - } static PreParserExpression EmptyLiteral() { return PreParserExpression::Default(); } @@ -1553,15 +1559,10 @@ class PreParserTraits { Variable* fvar, Token::Value fvar_init_op, FunctionKind kind, bool* ok); - // Utility functions - V8_INLINE int DeclareArrowParametersFromExpression( - PreParserExpression expression, Scope* scope, - FormalParameterErrorLocations* locs, bool* ok) { - // TODO(wingo): Detect duplicated identifiers in paramlists. Detect eval or - // arguments. Detect reserved words. - *ok = expression.IsValidArrowParamList(locs); - return 0; - } + V8_INLINE void ParseArrowFunctionFormalParameters( + Scope* scope, PreParserExpression expression, + const Scanner::Location& params_loc, + FormalParameterErrorLocations* error_locs, bool* is_rest, bool* ok); struct TemplateLiteralState {}; @@ -1786,6 +1787,20 @@ bool PreParserTraits::DeclareFormalParameter( } +void PreParserTraits::ParseArrowFunctionFormalParameters( + Scope* scope, PreParserExpression params, + const Scanner::Location& params_loc, + FormalParameterErrorLocations* error_locs, bool* is_rest, bool* ok) { + // TODO(wingo): Detect duplicated identifiers in paramlists. Detect parameter + // lists that are too long. + if (!params.IsValidArrowParamList(error_locs, params_loc)) { + *ok = false; + ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list"); + return; + } +} + + PreParserStatementList PreParser::ParseEagerFunctionBody( PreParserIdentifier function_name, int pos, Variable* fvar, Token::Value fvar_init_op, FunctionKind kind, bool* ok) { @@ -2081,12 +2096,14 @@ ParserBase::ParsePrimaryExpression(bool* ok) { case Token::LPAREN: Consume(Token::LPAREN); - if (allow_harmony_arrow_functions() && peek() == Token::RPAREN) { - // Arrow functions are the only expression type constructions - // for which an empty parameter list "()" is valid input. - Consume(Token::RPAREN); - result = this->ParseArrowFunctionLiteral( - beg_pos, this->EmptyArrowParamList(), CHECK_OK); + if (allow_harmony_arrow_functions() && Check(Token::RPAREN)) { + // As a primary expression, the only thing that can follow "()" is "=>". + Scope* scope = this->NewScope(scope_, ARROW_SCOPE); + scope->set_start_position(beg_pos); + FormalParameterErrorLocations error_locs; + bool has_rest = false; + result = this->ParseArrowFunctionLiteral(scope, error_locs, has_rest, + CHECK_OK); } else { // Heuristically try to detect immediately called functions before // seeing the call parentheses. @@ -2544,8 +2561,15 @@ ParserBase::ParseAssignmentExpression(bool accept_IN, bool* ok) { if (allow_harmony_arrow_functions() && peek() == Token::ARROW) { checkpoint.Restore(); - expression = this->ParseArrowFunctionLiteral(lhs_location.beg_pos, - expression, CHECK_OK); + FormalParameterErrorLocations error_locs; + Scanner::Location loc(lhs_location.beg_pos, scanner()->location().end_pos); + bool has_rest = false; + Scope* scope = this->NewScope(scope_, ARROW_SCOPE); + scope->set_start_position(lhs_location.beg_pos); + this->ParseArrowFunctionFormalParameters(scope, expression, loc, + &error_locs, &has_rest, CHECK_OK); + expression = + this->ParseArrowFunctionLiteral(scope, error_locs, has_rest, CHECK_OK); return expression; } @@ -3173,11 +3197,12 @@ void ParserBase::CheckArityRestrictions( } } + template typename ParserBase::ExpressionT -ParserBase::ParseArrowFunctionLiteral(int start_pos, - ExpressionT params_ast, - bool* ok) { +ParserBase::ParseArrowFunctionLiteral( + Scope* scope, const FormalParameterErrorLocations& error_locs, + bool has_rest, bool* ok) { if (peek() == Token::ARROW && scanner_->HasAnyLineTerminatorBeforeNext()) { // ASI inserts `;` after arrow parameters if a line terminator is found. // `=> ...` is never a valid expression, so report as syntax error. @@ -3187,9 +3212,8 @@ ParserBase::ParseArrowFunctionLiteral(int start_pos, return this->EmptyExpression(); } - Scope* scope = this->NewScope(scope_, ARROW_SCOPE); typename Traits::Type::StatementList body; - int num_parameters = -1; + int num_parameters = scope->num_parameters(); int materialized_literal_count = -1; int expected_property_count = -1; int handler_count = 0; @@ -3199,27 +3223,6 @@ ParserBase::ParseArrowFunctionLiteral(int start_pos, typename Traits::Type::Factory function_factory(ast_value_factory()); FunctionState function_state(&function_state_, &scope_, scope, kArrowFunction, &function_factory); - FormalParameterErrorLocations error_locs; - num_parameters = Traits::DeclareArrowParametersFromExpression( - params_ast, scope_, &error_locs, ok); - if (!*ok) { - ReportMessageAt( - Scanner::Location(start_pos, scanner()->location().beg_pos), - "malformed_arrow_function_parameter_list"); - return this->EmptyExpression(); - } - - if (error_locs.undefined.IsValid()) { - // Workaround for preparser not keeping track of positions. - error_locs.undefined = - Scanner::Location(start_pos, scanner()->location().end_pos); - } - if (num_parameters > Code::kMaxArguments) { - ReportMessageAt(Scanner::Location(params_ast->position(), position()), - "too_many_parameters"); - *ok = false; - return this->EmptyExpression(); - } Expect(Token::ARROW, CHECK_OK); @@ -3255,7 +3258,6 @@ ParserBase::ParseArrowFunctionLiteral(int start_pos, } super_loc = function_state.super_call_location(); - scope->set_start_position(start_pos); scope->set_end_position(scanner()->location().end_pos); // Arrow function formal parameters are parsed as StrictFormalParameterList, @@ -3268,8 +3270,8 @@ ParserBase::ParseArrowFunctionLiteral(int start_pos, // Validate strict mode. if (is_strict(language_mode())) { - CheckStrictOctalLiteral(start_pos, scanner()->location().end_pos, - CHECK_OK); + CheckStrictOctalLiteral(scope->start_position(), + scanner()->location().end_pos, CHECK_OK); this->CheckConflictingVarDeclarations(scope, CHECK_OK); } } @@ -3280,9 +3282,9 @@ ParserBase::ParseArrowFunctionLiteral(int start_pos, num_parameters, FunctionLiteral::kNoDuplicateParameters, FunctionLiteral::ANONYMOUS_EXPRESSION, FunctionLiteral::kIsFunction, FunctionLiteral::kNotParenthesized, FunctionKind::kArrowFunction, - start_pos); + scope->start_position()); - function_literal->set_function_token_position(start_pos); + function_literal->set_function_token_position(scope->start_position()); if (super_loc.IsValid()) function_state_->set_super_call_location(super_loc); if (fni_ != NULL) this->InferFunctionName(fni_, function_literal); diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index 9a58543..86e042e 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -3502,20 +3502,23 @@ TEST(ErrorsArrowFunctions) { "(x, (y, z)) => 0", "((x, y), z) => 0", - // Parameter lists are always validated as strict, so those are errors. - "eval => {}", - "arguments => {}", - "yield => {}", - "interface => {}", - "(eval) => {}", - "(arguments) => {}", - "(yield) => {}", - "(interface) => {}", - "(eval, bar) => {}", - "(bar, eval) => {}", - "(bar, arguments) => {}", - "(bar, yield) => {}", - "(bar, interface) => {}", + // Arrow function formal parameters are parsed as StrictFormalParameters, + // which confusingly only implies that there are no duplicates. Words + // reserved in strict mode, and eval or arguments, are indeed valid in + // sloppy mode. + "eval => { 'use strict'; 0 }", + "arguments => { 'use strict'; 0 }", + "yield => { 'use strict'; 0 }", + "interface => { 'use strict'; 0 }", + "(eval) => { 'use strict'; 0 }", + "(arguments) => { 'use strict'; 0 }", + "(yield) => { 'use strict'; 0 }", + "(interface) => { 'use strict'; 0 }", + "(eval, bar) => { 'use strict'; 0 }", + "(bar, eval) => { 'use strict'; 0 }", + "(bar, arguments) => { 'use strict'; 0 }", + "(bar, yield) => { 'use strict'; 0 }", + "(bar, interface) => { 'use strict'; 0 }", // TODO(aperez): Detecting duplicates does not work in PreParser. // "(bar, bar) => {}", @@ -3622,6 +3625,66 @@ TEST(NoErrorsArrowFunctions) { } +TEST(ArrowFunctionsSloppyParameterNames) { + const char* strong_context_data[][2] = { + {"'use strong'; ", ";"}, + {"'use strong'; bar ? (", ") : baz;"}, + {"'use strong'; bar ? baz : (", ");"}, + {"'use strong'; bar, ", ";"}, + {"'use strong'; ", ", bar;"}, + {NULL, NULL} + }; + + const char* strict_context_data[][2] = { + {"'use strict'; ", ";"}, + {"'use strict'; bar ? (", ") : baz;"}, + {"'use strict'; bar ? baz : (", ");"}, + {"'use strict'; bar, ", ";"}, + {"'use strict'; ", ", bar;"}, + {NULL, NULL} + }; + + const char* sloppy_context_data[][2] = { + {"", ";"}, + {"bar ? (", ") : baz;"}, + {"bar ? baz : (", ");"}, + {"bar, ", ";"}, + {"", ", bar;"}, + {NULL, NULL} + }; + + const char* statement_data[] = { + "eval => {}", + "arguments => {}", + "yield => {}", + "interface => {}", + "(eval) => {}", + "(arguments) => {}", + "(yield) => {}", + "(interface) => {}", + "(eval, bar) => {}", + "(bar, eval) => {}", + "(bar, arguments) => {}", + "(bar, yield) => {}", + "(bar, interface) => {}", + "(interface, eval) => {}", + "(interface, arguments) => {}", + "(eval, interface) => {}", + "(arguments, interface) => {}", + NULL + }; + + static const ParserFlag always_flags[] = { kAllowHarmonyArrowFunctions, + kAllowStrongMode}; + RunParserSyncTest(strong_context_data, statement_data, kError, NULL, 0, + always_flags, arraysize(always_flags)); + RunParserSyncTest(strict_context_data, statement_data, kError, NULL, 0, + always_flags, arraysize(always_flags)); + RunParserSyncTest(sloppy_context_data, statement_data, kSuccess, NULL, 0, + always_flags, arraysize(always_flags)); +} + + TEST(SuperNoErrors) { // Tests that parser and preparser accept 'super' keyword in right places. const char* context_data[][2] = { -- 2.7.4