Commit 636cb4f3 authored by wingo's avatar wingo Committed by Commit bot

Factor formal argument parsing into ParserBase

This commit is a precursor to making lazy arrow function parsing use
similar logic to function(){} argument parsing.

Originally landed in these three CLs:

  https://codereview.chromium.org/1078093002
  https://codereview.chromium.org/1083623002
  https://codereview.chromium.org/1083953002

These were rolled out due to a performance regression on CodeLoad.  This
patchset will fix that by avoiding creation of a DuplicateFinder in the
full parser.

R=marja@chromium.org
BUG=
LOG=N

Review URL: https://codereview.chromium.org/1100713002

Cr-Commit-Position: refs/heads/master@{#27960}
parent 0a8f8a95
...@@ -164,7 +164,9 @@ var kMessages = { ...@@ -164,7 +164,9 @@ var kMessages = {
array_not_subclassable: ["Subclassing Arrays is not currently supported."], array_not_subclassable: ["Subclassing Arrays is not currently supported."],
for_in_loop_initializer: ["for-in loop variable declaration may not have an initializer."], for_in_loop_initializer: ["for-in loop variable declaration may not have an initializer."],
for_of_loop_initializer: ["for-of loop variable declaration may not have an initializer."], 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."] 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."]
}; };
......
...@@ -3734,8 +3734,7 @@ Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) { ...@@ -3734,8 +3734,7 @@ Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) {
bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression, bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression,
Scope* scope, int* num_params, Scope* scope, int* num_params,
Scanner::Location* undefined_loc, FormalParameterErrorLocations* locs) {
Scanner::Location* dupe_loc) {
// Case for empty parameter lists: // Case for empty parameter lists:
// () => ... // () => ...
if (expression == NULL) return true; if (expression == NULL) return true;
...@@ -3754,21 +3753,24 @@ bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression, ...@@ -3754,21 +3753,24 @@ bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression,
if (traits->IsEvalOrArguments(raw_name) || if (traits->IsEvalOrArguments(raw_name) ||
traits->IsFutureStrictReserved(raw_name)) traits->IsFutureStrictReserved(raw_name))
return false; return false;
if (traits->IsUndefined(raw_name) && !undefined_loc->IsValid()) { if (traits->IsUndefined(raw_name) && !locs->undefined.IsValid()) {
*undefined_loc = Scanner::Location( locs->undefined = Scanner::Location(
expression->position(), expression->position() + raw_name->length()); expression->position(), expression->position() + raw_name->length());
} }
if (scope->IsDeclared(raw_name)) {
*dupe_loc = Scanner::Location(
expression->position(), expression->position() + raw_name->length());
return false;
}
// When the variable was seen, it was recorded as unresolved in the outer // When the variable was seen, it was recorded as unresolved in the outer
// scope. But it's really not unresolved. // scope. But it's really not unresolved.
scope->outer_scope()->RemoveUnresolved(expression->AsVariableProxy()); scope->outer_scope()->RemoveUnresolved(expression->AsVariableProxy());
scope->DeclareParameter(raw_name, VAR); 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;
}
++(*num_params); ++(*num_params);
return true; return true;
} }
...@@ -3782,9 +3784,9 @@ bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression, ...@@ -3782,9 +3784,9 @@ bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression,
return false; return false;
return CheckAndDeclareArrowParameter(traits, binop->left(), scope, return CheckAndDeclareArrowParameter(traits, binop->left(), scope,
num_params, undefined_loc, dupe_loc) && num_params, locs) &&
CheckAndDeclareArrowParameter(traits, binop->right(), scope, CheckAndDeclareArrowParameter(traits, binop->right(), scope,
num_params, undefined_loc, dupe_loc); num_params, locs);
} }
// Any other kind of expression is not a valid parameter list. // Any other kind of expression is not a valid parameter list.
...@@ -3793,15 +3795,15 @@ bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression, ...@@ -3793,15 +3795,15 @@ bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression,
int ParserTraits::DeclareArrowParametersFromExpression( int ParserTraits::DeclareArrowParametersFromExpression(
Expression* expression, Scope* scope, Scanner::Location* undefined_loc, Expression* expression, Scope* scope, FormalParameterErrorLocations* locs,
Scanner::Location* dupe_loc, bool* ok) { bool* ok) {
int num_params = 0; int num_params = 0;
// Always reset the flag: It only needs to be set for the first expression // 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 // parsed as arrow function parameter list, because only top-level functions
// are parsed lazily. // are parsed lazily.
parser_->parsing_lazy_arrow_parameters_ = false; parser_->parsing_lazy_arrow_parameters_ = false;
*ok = CheckAndDeclareArrowParameter(this, expression, scope, &num_params, *ok =
undefined_loc, dupe_loc); CheckAndDeclareArrowParameter(this, expression, scope, &num_params, locs);
return num_params; return num_params;
} }
...@@ -3875,8 +3877,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -3875,8 +3877,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
int materialized_literal_count = -1; int materialized_literal_count = -1;
int expected_property_count = -1; int expected_property_count = -1;
int handler_count = 0; int handler_count = 0;
FunctionLiteral::ParameterFlag duplicate_parameters = FormalParameterErrorLocations error_locs;
FunctionLiteral::kNoDuplicateParameters;
FunctionLiteral::IsParenthesizedFlag parenthesized = parenthesized_function_ FunctionLiteral::IsParenthesizedFlag parenthesized = parenthesized_function_
? FunctionLiteral::kIsParenthesized ? FunctionLiteral::kIsParenthesized
: FunctionLiteral::kNotParenthesized; : FunctionLiteral::kNotParenthesized;
...@@ -3901,77 +3902,17 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -3901,77 +3902,17 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
function_state.set_generator_object_variable(temp); function_state.set_generator_object_variable(temp);
} }
// FormalParameterList :: bool has_rest = false;
// '(' (Identifier)*[','] ')'
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
scope->set_start_position(scanner()->location().beg_pos); int start_position = scanner()->location().beg_pos;
scope_->set_start_position(start_position);
// We don't yet know if the function will be strict, so we cannot yet num_parameters =
// produce errors for parameter names or duplicates. However, we remember ParseFormalParameterList(scope, &error_locs, &has_rest, CHECK_OK);
// 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);
}
const AstRawString* param_name =
ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
// 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();
}
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::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos;
CheckArityRestrictions(num_parameters, arity_restriction, start_position,
formals_end_position, CHECK_OK);
Expect(Token::LBRACE, CHECK_OK); Expect(Token::LBRACE, CHECK_OK);
...@@ -4053,10 +3994,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -4053,10 +3994,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
CheckFunctionName(language_mode(), kind, function_name, CheckFunctionName(language_mode(), kind, function_name,
name_is_strict_reserved, function_name_location, name_is_strict_reserved, function_name_location,
CHECK_OK); CHECK_OK);
const bool use_strict_params = is_rest || IsConciseMethod(kind); const bool use_strict_params = has_rest || IsConciseMethod(kind);
CheckFunctionParameterNames(language_mode(), use_strict_params, CheckFunctionParameterNames(language_mode(), use_strict_params, error_locs,
eval_args_loc, undefined_loc, dupe_loc, CHECK_OK);
reserved_loc, CHECK_OK);
if (is_strict(language_mode())) { if (is_strict(language_mode())) {
CheckStrictOctalLiteral(scope->start_position(), scope->end_position(), CheckStrictOctalLiteral(scope->start_position(), scope->end_position(),
...@@ -4075,6 +4015,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -4075,6 +4015,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
} }
} }
FunctionLiteral::ParameterFlag duplicate_parameters =
error_locs.duplicate.IsValid() ? FunctionLiteral::kHasDuplicateParameters
: FunctionLiteral::kNoDuplicateParameters;
FunctionLiteral* function_literal = factory()->NewFunctionLiteral( FunctionLiteral* function_literal = factory()->NewFunctionLiteral(
function_name, ast_value_factory(), scope, body, function_name, ast_value_factory(), scope, body,
materialized_literal_count, expected_property_count, handler_count, materialized_literal_count, expected_property_count, handler_count,
......
...@@ -557,6 +557,8 @@ class ParserTraits { ...@@ -557,6 +557,8 @@ class ParserTraits {
typedef ObjectLiteral::Property* ObjectLiteralProperty; typedef ObjectLiteral::Property* ObjectLiteralProperty;
typedef ZoneList<v8::internal::Expression*>* ExpressionList; typedef ZoneList<v8::internal::Expression*>* ExpressionList;
typedef ZoneList<ObjectLiteral::Property*>* PropertyList; typedef ZoneList<ObjectLiteral::Property*>* PropertyList;
typedef const v8::internal::AstRawString* FormalParameter;
typedef Scope FormalParameterScope;
typedef ZoneList<v8::internal::Statement*>* StatementList; typedef ZoneList<v8::internal::Statement*>* StatementList;
// For constructing objects returned by the traversing functions. // For constructing objects returned by the traversing functions.
...@@ -705,6 +707,7 @@ class ParserTraits { ...@@ -705,6 +707,7 @@ class ParserTraits {
static ZoneList<Expression*>* NullExpressionList() { static ZoneList<Expression*>* NullExpressionList() {
return NULL; return NULL;
} }
static const AstRawString* EmptyFormalParameter() { return NULL; }
// Non-NULL empty string. // Non-NULL empty string.
V8_INLINE const AstRawString* EmptyIdentifierString(); V8_INLINE const AstRawString* EmptyIdentifierString();
...@@ -745,10 +748,22 @@ class ParserTraits { ...@@ -745,10 +748,22 @@ class ParserTraits {
// Utility functions // Utility functions
int DeclareArrowParametersFromExpression(Expression* expression, Scope* scope, int DeclareArrowParametersFromExpression(Expression* expression, Scope* scope,
Scanner::Location* undefined_loc, FormalParameterErrorLocations* locs,
Scanner::Location* dupe_loc,
bool* ok); bool* ok);
bool DeclareFormalParameter(Scope* scope, const AstRawString* name,
bool is_rest) {
bool is_duplicate = false;
Variable* var = scope->DeclareParameter(name, VAR, is_rest, &is_duplicate);
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 is_duplicate;
}
// Temporary glue; these functions will move to ParserBase. // Temporary glue; these functions will move to ParserBase.
Expression* ParseV8Intrinsic(bool* ok); Expression* ParseV8Intrinsic(bool* ok);
FunctionLiteral* ParseFunctionLiteral( FunctionLiteral* ParseFunctionLiteral(
......
...@@ -926,62 +926,23 @@ PreParser::Expression PreParser::ParseFunctionLiteral( ...@@ -926,62 +926,23 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
PreParserFactory factory(NULL); PreParserFactory factory(NULL);
FunctionState function_state(&function_state_, &scope_, function_scope, kind, FunctionState function_state(&function_state_, &scope_, function_scope, kind,
&factory); &factory);
// FormalParameterList :: FormalParameterErrorLocations error_locs;
// '(' (Identifier)*[','] ')'
Expect(Token::LPAREN, CHECK_OK);
int start_position = position();
DuplicateFinder duplicate_finder(scanner()->unicode_cache());
// 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 is_rest = false;
bool done = arity_restriction == FunctionLiteral::GETTER_ARITY || Expect(Token::LPAREN, CHECK_OK);
(peek() == Token::RPAREN && int start_position = scanner()->location().beg_pos;
arity_restriction != FunctionLiteral::SETTER_ARITY); function_scope->set_start_position(start_position);
while (!done) { int num_parameters;
bool is_strict_reserved = false; {
is_rest = peek() == Token::ELLIPSIS && allow_harmony_rest_params(); DuplicateFinder duplicate_finder(scanner()->unicode_cache());
if (is_rest) { num_parameters = ParseFormalParameterList(&duplicate_finder, &error_locs,
Consume(Token::ELLIPSIS); &is_rest, CHECK_OK);
}
Identifier param_name =
ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
if (!eval_args_loc.IsValid() && param_name.IsEvalOrArguments()) {
eval_args_loc = scanner()->location();
}
if (!undefined_loc.IsValid() && param_name.IsUndefined()) {
undefined_loc = scanner()->location();
}
if (!reserved_loc.IsValid() && is_strict_reserved) {
reserved_loc = scanner()->location();
}
int prev_value = scanner()->FindSymbol(&duplicate_finder, 1);
if (!dupe_loc.IsValid() && prev_value != 0) {
dupe_loc = scanner()->location();
}
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 Expression::Default();
}
Expect(Token::COMMA, CHECK_OK);
}
} }
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos;
CheckArityRestrictions(num_parameters, arity_restriction, start_position,
formals_end_position, CHECK_OK);
// See Parser::ParseFunctionLiteral for more information about lazy parsing // See Parser::ParseFunctionLiteral for more information about lazy parsing
// and lazy compilation. // and lazy compilation.
...@@ -1002,8 +963,8 @@ PreParser::Expression PreParser::ParseFunctionLiteral( ...@@ -1002,8 +963,8 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
CheckFunctionName(language_mode(), kind, function_name, CheckFunctionName(language_mode(), kind, function_name,
name_is_strict_reserved, function_name_location, CHECK_OK); name_is_strict_reserved, function_name_location, CHECK_OK);
const bool use_strict_params = is_rest || IsConciseMethod(kind); const bool use_strict_params = is_rest || IsConciseMethod(kind);
CheckFunctionParameterNames(language_mode(), use_strict_params, eval_args_loc, CheckFunctionParameterNames(language_mode(), use_strict_params, error_locs,
undefined_loc, dupe_loc, reserved_loc, CHECK_OK); CHECK_OK);
if (is_strict(language_mode())) { if (is_strict(language_mode())) {
int end_position = scanner()->location().end_pos; int end_position = scanner()->location().end_pos;
......
This diff is collapsed.
...@@ -452,7 +452,7 @@ Variable* Scope::Lookup(const AstRawString* name) { ...@@ -452,7 +452,7 @@ Variable* Scope::Lookup(const AstRawString* name) {
Variable* Scope::DeclareParameter(const AstRawString* name, VariableMode mode, Variable* Scope::DeclareParameter(const AstRawString* name, VariableMode mode,
bool is_rest) { bool is_rest, bool* is_duplicate) {
DCHECK(!already_resolved()); DCHECK(!already_resolved());
DCHECK(is_function_scope()); DCHECK(is_function_scope());
Variable* var = variables_.Declare(this, name, mode, Variable::NORMAL, Variable* var = variables_.Declare(this, name, mode, Variable::NORMAL,
...@@ -462,6 +462,8 @@ Variable* Scope::DeclareParameter(const AstRawString* name, VariableMode mode, ...@@ -462,6 +462,8 @@ Variable* Scope::DeclareParameter(const AstRawString* name, VariableMode mode,
rest_parameter_ = var; rest_parameter_ = var;
rest_index_ = num_parameters(); rest_index_ = num_parameters();
} }
// TODO(wingo): Avoid O(n^2) check.
*is_duplicate = IsDeclaredParameter(name);
params_.Add(var, zone()); params_.Add(var, zone());
return var; return var;
} }
......
...@@ -125,7 +125,7 @@ class Scope: public ZoneObject { ...@@ -125,7 +125,7 @@ class Scope: public ZoneObject {
// parameters the rightmost one 'wins'. However, the implementation // parameters the rightmost one 'wins'. However, the implementation
// expects all parameters to be declared and from left to right. // expects all parameters to be declared and from left to right.
Variable* DeclareParameter(const AstRawString* name, VariableMode mode, Variable* DeclareParameter(const AstRawString* name, VariableMode mode,
bool is_rest = false); bool is_rest, bool* is_duplicate);
// Declare a local variable in this scope. If the variable has been // Declare a local variable in this scope. If the variable has been
// declared before, the previously declared variable is returned. // declared before, the previously declared variable is returned.
......
// 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-rest-parameters
function foo(...b, a) { return a }
*%(basename)s:7: SyntaxError: Rest parameter must be last formal parameter
function foo(...b, a) { return a }
^
SyntaxError: Rest parameter must be last formal parameter
// 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.
function foo(b, eval) { "use strict"; return b }
*%(basename)s:5: SyntaxError: Unexpected eval or arguments in strict mode
function foo(b, eval) { "use strict"; return b }
^^^^
SyntaxError: Unexpected eval or arguments in strict mode
// 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.
function foo(b, a, a,) { return a }
*%(basename)s:5: SyntaxError: Unexpected token )
function foo(b, a, a,) { return a }
^
SyntaxError: Unexpected token )
// 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.
"use strict";
function foo(b, a, a, d) { return a }
*%(basename)s:6: SyntaxError: Strict mode function may not have duplicate parameter names
function foo(b, a, a, d) { return a }
^
SyntaxError: Strict mode function may not have duplicate parameter names
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment