| Index: src/parser.cc
|
| diff --git a/src/parser.cc b/src/parser.cc
|
| index 5f794c23c999a740ad1e0f0c9bb2c60286bf8a6e..53aa2ee2462ba2b55f4d150df5118d3a711097cf 100644
|
| --- a/src/parser.cc
|
| +++ b/src/parser.cc
|
| @@ -787,7 +787,16 @@ Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name,
|
| Scope* scope,
|
| AstNodeFactory* factory) {
|
| if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name);
|
| - return scope->NewUnresolved(factory, name, start_position, end_position);
|
| +
|
| + // 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);
|
| }
|
|
|
|
|
| @@ -852,6 +861,7 @@ 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) {
|
| @@ -1132,40 +1142,27 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info,
|
| bool ok = true;
|
|
|
| if (shared_info->is_arrow()) {
|
| - FormalParameterErrorLocations error_locs;
|
| - bool has_rest = false;
|
| - ZoneList<const AstRawString*>* params;
|
| - if (Check(Token::LPAREN)) {
|
| - // '(' StrictFormalParameters ')'
|
| - params = ParseFormalParameterList(&error_locs, &has_rest, &ok);
|
| - if (ok) ok = Check(Token::RPAREN);
|
| - } else {
|
| - // BindingIdentifier
|
| - params = NewFormalParameterList(1, zone());
|
| - DuplicateFinder* null_duplicate_finder = nullptr;
|
| - const AstRawString* single_param =
|
| - ParseFormalParameter(null_duplicate_finder, &error_locs, &ok);
|
| - if (ok) params->Add(single_param, zone());
|
| - }
|
| -
|
| + // 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);
|
| if (ok) {
|
| - Expression* expression = ParseArrowFunctionLiteral(
|
| - shared_info->start_position(), params, 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;
|
| - }
|
| + // 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;
|
| }
|
| }
|
| } else if (shared_info->is_default_constructor()) {
|
| @@ -3737,141 +3734,77 @@ Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) {
|
| }
|
|
|
|
|
| -void ParserTraits::RecordArrowFunctionParameter(
|
| - ZoneList<const AstRawString*>* params, VariableProxy* proxy,
|
| - FormalParameterErrorLocations* error_locs, bool* ok) {
|
| - const AstRawString* raw_name = proxy->raw_name();
|
| - Scanner::Location param_location(proxy->position(),
|
| - proxy->position() + raw_name->length());
|
| -
|
| - if (proxy->is_this()) {
|
| - ReportMessageAt(param_location, "this_formal_parameter");
|
| - *ok = false;
|
| - return;
|
| - }
|
| -
|
| - 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;
|
| -
|
| - // TODO(wingo): Fix quadratic check. (Scope::IsDeclaredParameter has the same
|
| - // issue.)
|
| - for (int i = 0; i < params->length(); i++) {
|
| - // Eagerly report the error here; duplicate formal parameter names are never
|
| - // allowed in arrow functions.
|
| - if (raw_name == params->at(i)) {
|
| - ReportMessageAt(param_location,
|
| - "duplicate_arrow_function_formal_parameter");
|
| - *ok = false;
|
| - return;
|
| - }
|
| - }
|
| -
|
| - // 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(proxy);
|
| -
|
| - params->Add(raw_name, parser_->zone());
|
| -}
|
| -
|
| -
|
| -// 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.
|
| -ZoneList<const AstRawString*>*
|
| -ParserTraits::ParseArrowFunctionFormalParameterList(
|
| - Expression* params, const Scanner::Location& params_loc,
|
| - FormalParameterErrorLocations* error_locs, bool* is_rest, bool* ok) {
|
| - ZoneList<const AstRawString*>* result =
|
| - NewFormalParameterList(4, parser_->zone());
|
| -
|
| - DCHECK_NOT_NULL(params);
|
| +bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression,
|
| + Scope* scope, int* num_params,
|
| + Scanner::Location* undefined_loc,
|
| + Scanner::Location* dupe_loc) {
|
| + // Case for empty parameter lists:
|
| + // () => ...
|
| + if (expression == NULL) return true;
|
|
|
| // 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 NULL;
|
| - }
|
| + if (expression->is_multi_parenthesized()) return false;
|
|
|
| - // ArrowFunctionFormals ::
|
| - // VariableProxy
|
| - // Binary(Token::COMMA, ArrowFunctionFormals, VariableProxy)
|
| - //
|
| - // To stay iterative we'll process arguments in right-to-left order, then
|
| - // reverse the list in place.
|
| - //
|
| - // 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.
|
| - while (params->IsBinaryOperation()) {
|
| - BinaryOperation* binop = params->AsBinaryOperation();
|
| - Expression* left = binop->left();
|
| - Expression* right = binop->right();
|
| - if (binop->op() != Token::COMMA) {
|
| - ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list");
|
| - *ok = false;
|
| - return NULL;
|
| - }
|
| - // RHS of comma expression should be an unparenthesized variable proxy.
|
| - if (right->is_parenthesized() || !right->IsVariableProxy()) {
|
| - ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list");
|
| - *ok = false;
|
| - return NULL;
|
| + // Case for a single parameter:
|
| + // (foo) => ...
|
| + // foo => ...
|
| + if (expression->IsVariableProxy()) {
|
| + if (expression->AsVariableProxy()->is_this()) return false;
|
| +
|
| + const AstRawString* raw_name = expression->AsVariableProxy()->raw_name();
|
| + if (traits->IsEvalOrArguments(raw_name) ||
|
| + traits->IsFutureStrictReserved(raw_name))
|
| + return false;
|
| + if (traits->IsUndefined(raw_name) && !undefined_loc->IsValid()) {
|
| + *undefined_loc = Scanner::Location(
|
| + expression->position(), expression->position() + raw_name->length());
|
| }
|
| - RecordArrowFunctionParameter(result, right->AsVariableProxy(), error_locs,
|
| - CHECK_OK);
|
| - // LHS of comma expression should be unparenthesized.
|
| - params = left;
|
| - if (params->is_parenthesized()) {
|
| - ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list");
|
| - *ok = false;
|
| - return NULL;
|
| + if (scope->IsDeclared(raw_name)) {
|
| + *dupe_loc = Scanner::Location(
|
| + expression->position(), expression->position() + raw_name->length());
|
| + return false;
|
| }
|
|
|
| - if (result->length() > Code::kMaxArguments) {
|
| - ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list");
|
| - *ok = false;
|
| - return NULL;
|
| - }
|
| - }
|
| + // 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());
|
|
|
| - if (params->IsVariableProxy()) {
|
| - RecordArrowFunctionParameter(result, params->AsVariableProxy(), error_locs,
|
| - CHECK_OK);
|
| - } else {
|
| - ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list");
|
| - *ok = false;
|
| - return NULL;
|
| + scope->DeclareParameter(raw_name, VAR);
|
| + ++(*num_params);
|
| + return true;
|
| }
|
|
|
| - // Reverse in place.
|
| - std::reverse(result->begin(), result->end());
|
| + // 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;
|
|
|
| - return result;
|
| + return CheckAndDeclareArrowParameter(traits, binop->left(), scope,
|
| + num_params, undefined_loc, dupe_loc) &&
|
| + CheckAndDeclareArrowParameter(traits, binop->right(), scope,
|
| + num_params, undefined_loc, dupe_loc);
|
| + }
|
| +
|
| + // Any other kind of expression is not a valid parameter list.
|
| + return false;
|
| }
|
|
|
|
|
| -int ParserTraits::DeclareFormalParameters(ZoneList<const AstRawString*>* params,
|
| - Scope* scope, bool has_rest) {
|
| - for (int i = 0; i < params->length(); i++) {
|
| - const AstRawString* param_name = params->at(i);
|
| - int is_rest = has_rest && i == params->length() - 1;
|
| - Variable* var = scope->DeclareParameter(param_name, VAR, is_rest);
|
| - if (is_sloppy(scope->language_mode())) {
|
| - // TODO(sigurds) Mark every parameter as maybe assigned. This is a
|
| - // conservative approximation necessary to account for parameters
|
| - // that are assigned via the arguments array.
|
| - var->set_maybe_assigned();
|
| - }
|
| - }
|
| - return params->length();
|
| +int ParserTraits::DeclareArrowParametersFromExpression(
|
| + Expression* expression, Scope* scope, Scanner::Location* undefined_loc,
|
| + Scanner::Location* dupe_loc, 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,
|
| + undefined_loc, dupe_loc);
|
| + return num_params;
|
| }
|
|
|
|
|
| @@ -3970,25 +3903,77 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
|
| function_state.set_generator_object_variable(temp);
|
| }
|
|
|
| - FormalParameterErrorLocations error_locs;
|
| - bool has_rest = false;
|
| + // FormalParameterList ::
|
| + // '(' (Identifier)*[','] ')'
|
| Expect(Token::LPAREN, CHECK_OK);
|
| - int start_position = scanner()->location().beg_pos;
|
| - ZoneList<const AstRawString*>* params =
|
| - ParseFormalParameterList(&error_locs, &has_rest, CHECK_OK);
|
| - Expect(Token::RPAREN, CHECK_OK);
|
| - int formals_end_position = scanner()->location().end_pos;
|
| + scope->set_start_position(scanner()->location().beg_pos);
|
| +
|
| + // We don't yet know if the function will be strict, so we cannot yet
|
| + // produce errors for parameter names or duplicates. However, we remember
|
| + // the locations of these errors if they occur and produce the errors later.
|
| + Scanner::Location eval_args_loc = Scanner::Location::invalid();
|
| + Scanner::Location dupe_loc = Scanner::Location::invalid();
|
| + Scanner::Location reserved_loc = Scanner::Location::invalid();
|
| +
|
| + // Similarly for strong mode.
|
| + Scanner::Location undefined_loc = Scanner::Location::invalid();
|
| +
|
| + bool is_rest = false;
|
| + bool done = arity_restriction == FunctionLiteral::GETTER_ARITY ||
|
| + (peek() == Token::RPAREN &&
|
| + arity_restriction != FunctionLiteral::SETTER_ARITY);
|
| + while (!done) {
|
| + bool is_strict_reserved = false;
|
| + is_rest = peek() == Token::ELLIPSIS && allow_harmony_rest_params();
|
| + if (is_rest) {
|
| + Consume(Token::ELLIPSIS);
|
| + }
|
|
|
| - CheckArityRestrictions(params->length(), arity_restriction, start_position,
|
| - formals_end_position, CHECK_OK);
|
| + const AstRawString* param_name =
|
| + ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
|
|
|
| - scope->set_start_position(start_position);
|
| + // Store locations for possible future error reports.
|
| + if (!eval_args_loc.IsValid() && IsEvalOrArguments(param_name)) {
|
| + eval_args_loc = scanner()->location();
|
| + }
|
| + if (!undefined_loc.IsValid() && IsUndefined(param_name)) {
|
| + undefined_loc = scanner()->location();
|
| + }
|
| + if (!reserved_loc.IsValid() && is_strict_reserved) {
|
| + reserved_loc = scanner()->location();
|
| + }
|
| + if (!dupe_loc.IsValid() &&
|
| + scope_->IsDeclaredParameter(param_name)) {
|
| + duplicate_parameters = FunctionLiteral::kHasDuplicateParameters;
|
| + dupe_loc = scanner()->location();
|
| + }
|
|
|
| - num_parameters = DeclareFormalParameters(params, scope_, has_rest);
|
| - if (error_locs.duplicate.IsValid()) {
|
| - duplicate_parameters = FunctionLiteral::kHasDuplicateParameters;
|
| - }
|
| + Variable* var = scope_->DeclareParameter(param_name, VAR, is_rest);
|
| + if (is_sloppy(scope->language_mode())) {
|
| + // TODO(sigurds) Mark every parameter as maybe assigned. This is a
|
| + // conservative approximation necessary to account for parameters
|
| + // that are assigned via the arguments array.
|
| + var->set_maybe_assigned();
|
| + }
|
|
|
| + num_parameters++;
|
| + if (num_parameters > Code::kMaxArguments) {
|
| + ReportMessage("too_many_parameters");
|
| + *ok = false;
|
| + return NULL;
|
| + }
|
| + if (arity_restriction == FunctionLiteral::SETTER_ARITY) break;
|
| + done = (peek() == Token::RPAREN);
|
| + if (!done) {
|
| + if (is_rest) {
|
| + ReportMessageAt(scanner()->peek_location(), "param_after_rest");
|
| + *ok = false;
|
| + return NULL;
|
| + }
|
| + Expect(Token::COMMA, CHECK_OK);
|
| + }
|
| + }
|
| + Expect(Token::RPAREN, CHECK_OK);
|
|
|
| Expect(Token::LBRACE, CHECK_OK);
|
|
|
| @@ -4070,9 +4055,10 @@ 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);
|
| - CheckFunctionParameterNames(language_mode(), use_strict_params, error_locs,
|
| - CHECK_OK);
|
| + const bool use_strict_params = is_rest || IsConciseMethod(kind);
|
| + CheckFunctionParameterNames(language_mode(), use_strict_params,
|
| + eval_args_loc, undefined_loc, dupe_loc,
|
| + reserved_loc, CHECK_OK);
|
|
|
| if (is_strict(language_mode())) {
|
| CheckStrictOctalLiteral(scope->start_position(), scope->end_position(),
|
|
|