Use ExpressionClassifier to identify valid arrow function formals

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

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

Cr-Commit-Position: refs/heads/master@{#28391}
This commit is contained in:
wingo 2015-05-13 04:45:04 -07:00 committed by Commit bot
parent 6eea252463
commit e73594c7fb
6 changed files with 332 additions and 238 deletions

View File

@ -1138,22 +1138,20 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info,
if (shared_info->is_arrow()) { if (shared_info->is_arrow()) {
Scope* scope = NewScope(scope_, ARROW_SCOPE); Scope* scope = NewScope(scope_, ARROW_SCOPE);
scope->set_start_position(shared_info->start_position()); scope->set_start_position(shared_info->start_position());
FormalParameterErrorLocations error_locs; ExpressionClassifier formals_classifier;
bool has_rest = false; bool has_rest = false;
if (Check(Token::LPAREN)) { if (Check(Token::LPAREN)) {
// '(' StrictFormalParameters ')' // '(' StrictFormalParameters ')'
ParseFormalParameterList(scope, &error_locs, &has_rest, &ok); ParseFormalParameterList(scope, &has_rest, &formals_classifier, &ok);
if (ok) ok = Check(Token::RPAREN); if (ok) ok = Check(Token::RPAREN);
} else { } else {
// BindingIdentifier // BindingIdentifier
ParseFormalParameter(scope, &error_locs, has_rest, &ok); ParseFormalParameter(scope, has_rest, &formals_classifier, &ok);
} }
if (ok) { if (ok) {
ExpressionClassifier classifier; Expression* expression =
Expression* expression = ParseArrowFunctionLiteral( ParseArrowFunctionLiteral(scope, has_rest, formals_classifier, &ok);
scope, error_locs, has_rest, &classifier, &ok);
ValidateExpression(&classifier, &ok);
if (ok) { if (ok) {
// Scanning must end at the same position that was recorded // Scanning must end at the same position that was recorded
// previously. If not, parsing has been interrupted due to a stack // previously. If not, parsing has been interrupted due to a stack
@ -3720,7 +3718,7 @@ Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) {
void ParserTraits::DeclareArrowFunctionParameters( void ParserTraits::DeclareArrowFunctionParameters(
Scope* scope, Expression* expr, const Scanner::Location& params_loc, Scope* scope, Expression* expr, const Scanner::Location& params_loc,
FormalParameterErrorLocations* error_locs, bool* ok) { Scanner::Location* duplicate_loc, bool* ok) {
if (scope->num_parameters() >= Code::kMaxArguments) { if (scope->num_parameters() >= Code::kMaxArguments) {
ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list"); ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list");
*ok = false; *ok = false;
@ -3739,6 +3737,7 @@ void ParserTraits::DeclareArrowFunctionParameters(
// need to match the pre-parser's behavior. // need to match the pre-parser's behavior.
if (expr->IsBinaryOperation()) { if (expr->IsBinaryOperation()) {
BinaryOperation* binop = expr->AsBinaryOperation(); BinaryOperation* binop = expr->AsBinaryOperation();
// TODO(wingo): These checks are now unnecessary, given the classifier.
if (binop->op() != Token::COMMA) { if (binop->op() != Token::COMMA) {
ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list"); ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list");
*ok = false; *ok = false;
@ -3751,7 +3750,7 @@ void ParserTraits::DeclareArrowFunctionParameters(
*ok = false; *ok = false;
return; return;
} }
DeclareArrowFunctionParameters(scope, left, params_loc, error_locs, ok); DeclareArrowFunctionParameters(scope, left, params_loc, duplicate_loc, ok);
if (!*ok) return; if (!*ok) return;
// LHS of comma expression should be unparenthesized. // LHS of comma expression should be unparenthesized.
expr = right; expr = right;
@ -3774,13 +3773,6 @@ void ParserTraits::DeclareArrowFunctionParameters(
return; 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;
// When the formal parameter was originally seen, it was parsed as a // When the formal parameter was originally seen, it was parsed as a
// VariableProxy and recorded as unresolved in the scope. Here we undo that // VariableProxy and recorded as unresolved in the scope. Here we undo that
// parse-time side-effect. // parse-time side-effect.
@ -3789,22 +3781,15 @@ void ParserTraits::DeclareArrowFunctionParameters(
bool is_rest = false; bool is_rest = false;
bool is_duplicate = DeclareFormalParameter(scope, raw_name, is_rest); bool is_duplicate = DeclareFormalParameter(scope, raw_name, is_rest);
if (is_duplicate) { if (is_duplicate && !duplicate_loc->IsValid()) {
// Arrow function parameter lists are parsed as StrictFormalParameters, *duplicate_loc = param_location;
// 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;
} }
} }
void ParserTraits::ParseArrowFunctionFormalParameters( void ParserTraits::ParseArrowFunctionFormalParameters(
Scope* scope, Expression* params, const Scanner::Location& params_loc, Scope* scope, Expression* params, const Scanner::Location& params_loc,
FormalParameterErrorLocations* error_locs, bool* is_rest, bool* ok) { bool* is_rest, Scanner::Location* duplicate_loc, bool* ok) {
// Too many parentheses around expression: // Too many parentheses around expression:
// (( ... )) => ... // (( ... )) => ...
if (params->is_multi_parenthesized()) { if (params->is_multi_parenthesized()) {
@ -3814,7 +3799,7 @@ void ParserTraits::ParseArrowFunctionFormalParameters(
return; return;
} }
DeclareArrowFunctionParameters(scope, params, params_loc, error_locs, ok); DeclareArrowFunctionParameters(scope, params, params_loc, duplicate_loc, ok);
} }
@ -3887,7 +3872,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;
FormalParameterErrorLocations error_locs; ExpressionClassifier formals_classifier;
FunctionLiteral::EagerCompileHint eager_compile_hint = FunctionLiteral::EagerCompileHint eager_compile_hint =
parenthesized_function_ ? FunctionLiteral::kShouldEagerCompile parenthesized_function_ ? FunctionLiteral::kShouldEagerCompile
: FunctionLiteral::kShouldLazyCompile; : FunctionLiteral::kShouldLazyCompile;
@ -3917,8 +3902,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
int start_position = scanner()->location().beg_pos; int start_position = scanner()->location().beg_pos;
scope_->set_start_position(start_position); scope_->set_start_position(start_position);
num_parameters = num_parameters = ParseFormalParameterList(scope, &has_rest,
ParseFormalParameterList(scope, &error_locs, &has_rest, CHECK_OK); &formals_classifier, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos; int formals_end_position = scanner()->location().end_pos;
@ -4041,8 +4026,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
name_is_strict_reserved, function_name_location, name_is_strict_reserved, function_name_location,
CHECK_OK); CHECK_OK);
const bool use_strict_params = has_rest || IsConciseMethod(kind); const bool use_strict_params = has_rest || IsConciseMethod(kind);
CheckFunctionParameterNames(language_mode(), use_strict_params, error_locs, const bool allow_duplicate_parameters =
CHECK_OK); is_sloppy(language_mode()) && !use_strict_params;
ValidateFormalParameters(&formals_classifier, language_mode(),
allow_duplicate_parameters, 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(),
@ -4051,9 +4038,11 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
} }
} }
bool has_duplicate_parameters =
!formals_classifier.is_valid_formal_parameter_list_without_duplicates();
FunctionLiteral::ParameterFlag duplicate_parameters = FunctionLiteral::ParameterFlag duplicate_parameters =
error_locs.duplicate.IsValid() ? FunctionLiteral::kHasDuplicateParameters has_duplicate_parameters ? FunctionLiteral::kHasDuplicateParameters
: FunctionLiteral::kNoDuplicateParameters; : 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,

View File

@ -760,11 +760,13 @@ class ParserTraits {
void DeclareArrowFunctionParameters(Scope* scope, Expression* expr, void DeclareArrowFunctionParameters(Scope* scope, Expression* expr,
const Scanner::Location& params_loc, const Scanner::Location& params_loc,
FormalParameterErrorLocations* error_locs, Scanner::Location* duplicate_loc,
bool* ok); bool* ok);
void ParseArrowFunctionFormalParameters( void ParseArrowFunctionFormalParameters(Scope* scope, Expression* params,
Scope* scope, Expression* params, const Scanner::Location& params_loc, const Scanner::Location& params_loc,
FormalParameterErrorLocations* error_locs, bool* is_rest, bool* ok); bool* is_rest,
Scanner::Location* duplicate_loc,
bool* ok);
// Temporary glue; these functions will move to ParserBase. // Temporary glue; these functions will move to ParserBase.
Expression* ParseV8Intrinsic(bool* ok); Expression* ParseV8Intrinsic(bool* ok);

View File

@ -1024,7 +1024,7 @@ 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);
FormalParameterErrorLocations error_locs; ExpressionClassifier formals_classifier;
bool is_rest = false; bool is_rest = false;
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
@ -1033,8 +1033,8 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
int num_parameters; int num_parameters;
{ {
DuplicateFinder duplicate_finder(scanner()->unicode_cache()); DuplicateFinder duplicate_finder(scanner()->unicode_cache());
num_parameters = ParseFormalParameterList(&duplicate_finder, &error_locs, num_parameters = ParseFormalParameterList(&duplicate_finder, &is_rest,
&is_rest, CHECK_OK); &formals_classifier, CHECK_OK);
} }
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos; int formals_end_position = scanner()->location().end_pos;
@ -1060,9 +1060,11 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
// function, since the function can declare itself strict. // function, since the function can declare itself strict.
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 strict_formal_parameters = is_rest || IsConciseMethod(kind);
CheckFunctionParameterNames(language_mode(), use_strict_params, error_locs, const bool allow_duplicate_parameters =
CHECK_OK); is_sloppy(language_mode()) && !strict_formal_parameters;
ValidateFormalParameters(&formals_classifier, language_mode(),
allow_duplicate_parameters, 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;

View File

@ -18,25 +18,6 @@ namespace v8 {
namespace internal { namespace internal {
// When parsing the formal parameters of a function, we usually don't yet know
// if the function will be strict, so we cannot yet produce errors for
// parameter names or duplicates. Instead, we remember the locations of these
// errors if they occur and produce the errors later.
class FormalParameterErrorLocations BASE_EMBEDDED {
public:
FormalParameterErrorLocations()
: eval_or_arguments(Scanner::Location::invalid()),
undefined(Scanner::Location::invalid()),
duplicate(Scanner::Location::invalid()),
reserved(Scanner::Location::invalid()) {}
Scanner::Location eval_or_arguments;
Scanner::Location undefined;
Scanner::Location duplicate;
Scanner::Location reserved;
};
// Common base class shared between parser and pre-parser. Traits encapsulate // Common base class shared between parser and pre-parser. Traits encapsulate
// the differences between Parser and PreParser: // the differences between Parser and PreParser:
@ -527,37 +508,6 @@ class ParserBase : public Traits {
} }
} }
// Checking the parameter names of a function literal. This has to be done
// after parsing the function, since the function can declare itself strict.
void CheckFunctionParameterNames(LanguageMode language_mode,
bool strict_params,
const FormalParameterErrorLocations& locs,
bool* ok) {
if (is_sloppy(language_mode) && !strict_params) return;
if (is_strict(language_mode) && locs.eval_or_arguments.IsValid()) {
Traits::ReportMessageAt(locs.eval_or_arguments, "strict_eval_arguments");
*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;
return;
}
// TODO(arv): When we add support for destructuring in setters we also need
// to check for duplicate names.
if (locs.duplicate.IsValid()) {
Traits::ReportMessageAt(locs.duplicate, "strict_param_dupe");
*ok = false;
return;
}
}
// Determine precedence of given token. // Determine precedence of given token.
static int Precedence(Token::Value token, bool accept_IN) { static int Precedence(Token::Value token, bool accept_IN) {
if (token == Token::IN && !accept_IN) if (token == Token::IN && !accept_IN)
@ -615,6 +565,26 @@ class ParserBase : public Traits {
return !assignment_pattern_error_.HasError(); return !assignment_pattern_error_.HasError();
} }
bool is_valid_arrow_formal_parameters() const {
return !arrow_formal_parameters_error_.HasError();
}
bool is_valid_formal_parameter_list_without_duplicates() const {
return !duplicate_formal_parameter_error_.HasError();
}
// Note: callers should also check
// is_valid_formal_parameter_list_without_duplicates().
bool is_valid_strict_mode_formal_parameters() const {
return !strict_mode_formal_parameter_error_.HasError();
}
// Note: callers should also check is_valid_strict_mode_formal_parameters()
// and is_valid_formal_parameter_list_without_duplicates().
bool is_valid_strong_mode_formal_parameters() const {
return !strong_mode_formal_parameter_error_.HasError();
}
const Error& expression_error() const { return expression_error_; } const Error& expression_error() const { return expression_error_; }
const Error& binding_pattern_error() const { const Error& binding_pattern_error() const {
@ -625,6 +595,22 @@ class ParserBase : public Traits {
return assignment_pattern_error_; return assignment_pattern_error_;
} }
const Error& arrow_formal_parameters_error() const {
return arrow_formal_parameters_error_;
}
const Error& duplicate_formal_parameter_error() const {
return duplicate_formal_parameter_error_;
}
const Error& strict_mode_formal_parameter_error() const {
return strict_mode_formal_parameter_error_;
}
const Error& strong_mode_formal_parameter_error() const {
return strong_mode_formal_parameter_error_;
}
void RecordExpressionError(const Scanner::Location& loc, void RecordExpressionError(const Scanner::Location& loc,
const char* message, const char* arg = nullptr) { const char* message, const char* arg = nullptr) {
if (!is_valid_expression()) return; if (!is_valid_expression()) return;
@ -651,10 +637,98 @@ class ParserBase : public Traits {
assignment_pattern_error_.arg = arg; assignment_pattern_error_.arg = arg;
} }
void RecordArrowFormalParametersError(const Scanner::Location& loc,
const char* message,
const char* arg = nullptr) {
if (!is_valid_arrow_formal_parameters()) return;
arrow_formal_parameters_error_.location = loc;
arrow_formal_parameters_error_.message = message;
arrow_formal_parameters_error_.arg = arg;
}
void RecordDuplicateFormalParameterError(const Scanner::Location& loc) {
if (!is_valid_formal_parameter_list_without_duplicates()) return;
duplicate_formal_parameter_error_.location = loc;
duplicate_formal_parameter_error_.message = "strict_param_dupe";
duplicate_formal_parameter_error_.arg = nullptr;
}
// Record a binding that would be invalid in strict mode. Confusingly this
// is not the same as StrictFormalParameterList, which simply forbids
// duplicate bindings.
void RecordStrictModeFormalParameterError(const Scanner::Location& loc,
const char* message,
const char* arg = nullptr) {
if (!is_valid_strict_mode_formal_parameters()) return;
strict_mode_formal_parameter_error_.location = loc;
strict_mode_formal_parameter_error_.message = message;
strict_mode_formal_parameter_error_.arg = arg;
}
void RecordStrongModeFormalParameterError(const Scanner::Location& loc,
const char* message,
const char* arg = nullptr) {
if (!is_valid_strong_mode_formal_parameters()) return;
strong_mode_formal_parameter_error_.location = loc;
strong_mode_formal_parameter_error_.message = message;
strong_mode_formal_parameter_error_.arg = arg;
}
enum TargetProduction {
ExpressionProduction = 1 << 0,
BindingPatternProduction = 1 << 1,
AssignmentPatternProduction = 1 << 2,
FormalParametersProduction = 1 << 3,
ArrowFormalParametersProduction = 1 << 4,
StandardProductions = (ExpressionProduction | BindingPatternProduction |
AssignmentPatternProduction),
AllProductions = (StandardProductions | FormalParametersProduction |
ArrowFormalParametersProduction)
};
void Accumulate(const ExpressionClassifier& inner,
unsigned productions = StandardProductions) {
if (productions & ExpressionProduction && is_valid_expression()) {
expression_error_ = inner.expression_error_;
}
if (productions & BindingPatternProduction &&
is_valid_binding_pattern()) {
binding_pattern_error_ = inner.binding_pattern_error_;
}
if (productions & AssignmentPatternProduction &&
is_valid_assignment_pattern()) {
assignment_pattern_error_ = inner.assignment_pattern_error_;
}
if (productions & FormalParametersProduction) {
if (is_valid_formal_parameter_list_without_duplicates()) {
duplicate_formal_parameter_error_ =
inner.duplicate_formal_parameter_error_;
}
if (is_valid_strict_mode_formal_parameters()) {
strict_mode_formal_parameter_error_ =
inner.strict_mode_formal_parameter_error_;
}
if (is_valid_strong_mode_formal_parameters()) {
strong_mode_formal_parameter_error_ =
inner.strong_mode_formal_parameter_error_;
}
}
if (productions & ArrowFormalParametersProduction &&
is_valid_arrow_formal_parameters()) {
// The result continues to be a valid arrow formal parameters if the
// inner expression is a valid binding pattern.
arrow_formal_parameters_error_ = inner.binding_pattern_error_;
}
}
private: private:
Error expression_error_; Error expression_error_;
Error binding_pattern_error_; Error binding_pattern_error_;
Error assignment_pattern_error_; Error assignment_pattern_error_;
Error arrow_formal_parameters_error_;
Error duplicate_formal_parameter_error_;
Error strict_mode_formal_parameter_error_;
Error strong_mode_formal_parameter_error_;
}; };
void ReportClassifierError( void ReportClassifierError(
@ -686,9 +760,47 @@ class ParserBase : public Traits {
} }
} }
void ValidateFormalParameters(const ExpressionClassifier* classifier,
LanguageMode language_mode,
bool allow_duplicates, bool* ok) {
if (!allow_duplicates &&
!classifier->is_valid_formal_parameter_list_without_duplicates()) {
ReportClassifierError(classifier->duplicate_formal_parameter_error());
*ok = false;
} else if (is_strict(language_mode) &&
!classifier->is_valid_strict_mode_formal_parameters()) {
ReportClassifierError(classifier->strict_mode_formal_parameter_error());
*ok = false;
} else if (is_strong(language_mode) &&
!classifier->is_valid_strong_mode_formal_parameters()) {
ReportClassifierError(classifier->strong_mode_formal_parameter_error());
*ok = false;
}
}
void ValidateArrowFormalParameters(const ExpressionClassifier* classifier,
ExpressionT expr, bool* ok) {
if (classifier->is_valid_binding_pattern()) {
// A simple arrow formal parameter: IDENTIFIER => BODY.
if (!this->IsIdentifier(expr)) {
Traits::ReportMessageAt(scanner()->location(), "unexpected_token",
Token::String(scanner()->current_token()));
*ok = false;
}
} else if (!classifier->is_valid_arrow_formal_parameters()) {
ReportClassifierError(classifier->arrow_formal_parameters_error());
*ok = false;
}
}
void BindingPatternUnexpectedToken(ExpressionClassifier* classifier) { void BindingPatternUnexpectedToken(ExpressionClassifier* classifier) {
classifier->RecordBindingPatternError( classifier->RecordBindingPatternError(
scanner()->location(), "unexpected_token", Token::String(peek())); scanner()->peek_location(), "unexpected_token", Token::String(peek()));
}
void ArrowFormalParametersUnexpectedToken(ExpressionClassifier* classifier) {
classifier->RecordArrowFormalParametersError(
scanner()->peek_location(), "unexpected_token", Token::String(peek()));
} }
// Recursive descent functions: // Recursive descent functions:
@ -750,9 +862,9 @@ class ParserBase : public Traits {
ExpressionT ParseMemberExpression(ExpressionClassifier* classifier, bool* ok); ExpressionT ParseMemberExpression(ExpressionClassifier* classifier, bool* ok);
ExpressionT ParseMemberExpressionContinuation( ExpressionT ParseMemberExpressionContinuation(
ExpressionT expression, ExpressionClassifier* classifier, bool* ok); ExpressionT expression, ExpressionClassifier* classifier, bool* ok);
ExpressionT ParseArrowFunctionLiteral( ExpressionT ParseArrowFunctionLiteral(Scope* function_scope, bool has_rest,
Scope* function_scope, const FormalParameterErrorLocations& error_locs, const ExpressionClassifier& classifier,
bool has_rest, ExpressionClassifier* classifier, bool* ok); bool* ok);
ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, ExpressionT ParseTemplateLiteral(ExpressionT tag, int start,
ExpressionClassifier* classifier, bool* ok); ExpressionClassifier* classifier, bool* ok);
void AddTemplateExpression(ExpressionT); void AddTemplateExpression(ExpressionT);
@ -763,12 +875,10 @@ class ParserBase : public Traits {
ExpressionT ParseStrongSuperCallExpression(ExpressionClassifier* classifier, ExpressionT ParseStrongSuperCallExpression(ExpressionClassifier* classifier,
bool* ok); bool* ok);
void ParseFormalParameter(FormalParameterScopeT* scope, void ParseFormalParameter(FormalParameterScopeT* scope, bool is_rest,
FormalParameterErrorLocations* locs, bool is_rest, ExpressionClassifier* classifier, bool* ok);
bool* ok); int ParseFormalParameterList(FormalParameterScopeT* scope, bool* has_rest,
int ParseFormalParameterList(FormalParameterScopeT* scope, ExpressionClassifier* classifier, bool* ok);
FormalParameterErrorLocations* locs,
bool* has_rest, bool* ok);
void CheckArityRestrictions( void CheckArityRestrictions(
int param_count, FunctionLiteral::ArityRestriction arity_restriction, int param_count, FunctionLiteral::ArityRestriction arity_restriction,
int formals_start_pos, int formals_end_pos, bool* ok); int formals_start_pos, int formals_end_pos, bool* ok);
@ -973,14 +1083,7 @@ class PreParserExpression {
static PreParserExpression BinaryOperation(PreParserExpression left, static PreParserExpression BinaryOperation(PreParserExpression left,
Token::Value op, Token::Value op,
PreParserExpression right) { PreParserExpression right) {
ValidArrowParam valid_arrow_param_list = return PreParserExpression(TypeField::encode(kBinaryOperationExpression));
(op == Token::COMMA && !left.is_single_parenthesized() &&
!right.is_single_parenthesized())
? std::min(left.ValidateArrowParams(), right.ValidateArrowParams())
: kInvalidArrowParam;
return PreParserExpression(
TypeField::encode(kBinaryOperationExpression) |
IsValidArrowParamListField::encode(valid_arrow_param_list));
} }
static PreParserExpression StringLiteral() { static PreParserExpression StringLiteral() {
@ -1073,30 +1176,6 @@ class PreParserExpression {
return IsIdentifier() || IsProperty(); return IsIdentifier() || IsProperty();
} }
bool IsValidArrowParamList(FormalParameterErrorLocations* locs,
const Scanner::Location& params_loc) const {
ValidArrowParam valid = ValidateArrowParams();
if (ParenthesizationField::decode(code_) == kMultiParenthesizedExpression) {
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;
}
}
// At the moment PreParser doesn't track these expression types. // At the moment PreParser doesn't track these expression types.
bool IsFunctionLiteral() const { return false; } bool IsFunctionLiteral() const { return false; }
bool IsCallNew() const { return false; } bool IsCallNew() const { return false; }
@ -1160,43 +1239,9 @@ class PreParserExpression {
kNoTemplateTagExpression 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
};
explicit PreParserExpression(uint32_t expression_code) explicit PreParserExpression(uint32_t expression_code)
: code_(expression_code) {} : code_(expression_code) {}
V8_INLINE ValidArrowParam ValidateArrowParams() const {
if (IsBinaryOperation()) {
return IsValidArrowParamListField::decode(code_);
}
if (!IsIdentifier()) {
return kInvalidArrowParam;
}
PreParserIdentifier ident = AsIdentifier();
// In strict mode, eval and arguments are not valid formal parameter names.
if (ident.IsEval() || ident.IsArguments()) {
return kInvalidStrictEvalArgumentsArrowParam;
}
// 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;
}
return kValidArrowParam;
}
// The first five bits are for the Type and Parenthesization. // The first five bits are for the Type and Parenthesization.
typedef BitField<Type, 0, 3> TypeField; typedef BitField<Type, 0, 3> TypeField;
typedef BitField<Parenthesization, TypeField::kNext, 2> ParenthesizationField; typedef BitField<Parenthesization, TypeField::kNext, 2> ParenthesizationField;
@ -1207,8 +1252,6 @@ class PreParserExpression {
ExpressionTypeField; ExpressionTypeField;
typedef BitField<bool, ParenthesizationField::kNext, 1> IsUseStrictField; typedef BitField<bool, ParenthesizationField::kNext, 1> IsUseStrictField;
typedef BitField<bool, IsUseStrictField::kNext, 1> IsUseStrongField; typedef BitField<bool, IsUseStrictField::kNext, 1> IsUseStrongField;
typedef BitField<ValidArrowParam, ParenthesizationField::kNext, 3>
IsValidArrowParamListField;
typedef BitField<PreParserIdentifier::Type, ParenthesizationField::kNext, 10> typedef BitField<PreParserIdentifier::Type, ParenthesizationField::kNext, 10>
IdentifierTypeField; IdentifierTypeField;
@ -1696,8 +1739,8 @@ class PreParserTraits {
V8_INLINE void ParseArrowFunctionFormalParameters( V8_INLINE void ParseArrowFunctionFormalParameters(
Scope* scope, PreParserExpression expression, Scope* scope, PreParserExpression expression,
const Scanner::Location& params_loc, const Scanner::Location& params_loc, bool* is_rest,
FormalParameterErrorLocations* error_locs, bool* is_rest, bool* ok); Scanner::Location* duplicate_loc, bool* ok);
struct TemplateLiteralState {}; struct TemplateLiteralState {};
@ -1928,15 +1971,10 @@ bool PreParserTraits::DeclareFormalParameter(
void PreParserTraits::ParseArrowFunctionFormalParameters( void PreParserTraits::ParseArrowFunctionFormalParameters(
Scope* scope, PreParserExpression params, Scope* scope, PreParserExpression params,
const Scanner::Location& params_loc, const Scanner::Location& params_loc, bool* is_rest,
FormalParameterErrorLocations* error_locs, bool* is_rest, bool* ok) { Scanner::Location* duplicate_loc, bool* ok) {
// TODO(wingo): Detect duplicated identifiers in paramlists. Detect parameter // TODO(wingo): Detect duplicated identifiers in paramlists. Detect parameter
// lists that are too long. // lists that are too long.
if (!params.IsValidArrowParamList(error_locs, params_loc)) {
*ok = false;
ReportMessageAt(params_loc, "malformed_arrow_function_parameter_list");
return;
}
} }
@ -2061,27 +2099,51 @@ ParserBase<Traits>::ParseAndClassifyIdentifier(ExpressionClassifier* classifier,
Token::Value next = Next(); Token::Value next = Next();
if (next == Token::IDENTIFIER) { if (next == Token::IDENTIFIER) {
IdentifierT name = this->GetSymbol(scanner()); IdentifierT name = this->GetSymbol(scanner());
if (is_strict(language_mode()) && this->IsEvalOrArguments(name)) { // When this function is used to read a formal parameter, we don't always
classifier->RecordBindingPatternError(scanner()->location(), // know whether the function is going to be strict or sloppy. Indeed for
"strict_eval_arguments"); // arrow functions we don't always know that the identifier we are reading
// is actually a formal parameter. Therefore besides the errors that we
// must detect because we know we're in strict mode, we also record any
// error that we might make in the future once we know the language mode.
if (this->IsEval(name)) {
classifier->RecordStrictModeFormalParameterError(scanner()->location(),
"strict_eval_arguments");
if (is_strict(language_mode())) {
classifier->RecordBindingPatternError(scanner()->location(),
"strict_eval_arguments");
}
} }
if (is_strong(language_mode()) && this->IsUndefined(name)) { if (this->IsArguments(name)) {
// TODO(dslomov): allow 'undefined' in nested patterns. scope_->RecordArgumentsUsage();
classifier->RecordBindingPatternError(scanner()->location(), classifier->RecordStrictModeFormalParameterError(scanner()->location(),
"strong_undefined"); "strict_eval_arguments");
classifier->RecordAssignmentPatternError(scanner()->location(), if (is_strict(language_mode())) {
"strong_undefined"); classifier->RecordBindingPatternError(scanner()->location(),
"strict_eval_arguments");
}
if (is_strong(language_mode())) {
classifier->RecordExpressionError(scanner()->location(),
"strong_arguments");
}
} }
if (is_strong(language_mode()) && this->IsArguments(name)) { if (this->IsUndefined(name)) {
classifier->RecordExpressionError(scanner()->location(), classifier->RecordStrongModeFormalParameterError(scanner()->location(),
"strong_arguments"); "strong_undefined");
if (is_strong(language_mode())) {
// TODO(dslomov): allow 'undefined' in nested patterns.
classifier->RecordBindingPatternError(scanner()->location(),
"strong_undefined");
classifier->RecordAssignmentPatternError(scanner()->location(),
"strong_undefined");
}
} }
if (this->IsArguments(name)) scope_->RecordArgumentsUsage();
return name; return name;
} else if (is_sloppy(language_mode()) && } else if (is_sloppy(language_mode()) &&
(next == Token::FUTURE_STRICT_RESERVED_WORD || (next == Token::FUTURE_STRICT_RESERVED_WORD ||
next == Token::LET || next == Token::STATIC || next == Token::LET || next == Token::STATIC ||
(next == Token::YIELD && !is_generator()))) { (next == Token::YIELD && !is_generator()))) {
classifier->RecordStrictModeFormalParameterError(
scanner()->location(), "unexpected_strict_reserved");
return this->GetSymbol(scanner()); return this->GetSymbol(scanner());
} else { } else {
this->ReportUnexpectedToken(next); this->ReportUnexpectedToken(next);
@ -2270,24 +2332,41 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
break; break;
case Token::LBRACK: case Token::LBRACK:
if (!allow_harmony_destructuring()) {
BindingPatternUnexpectedToken(classifier);
}
result = this->ParseArrayLiteral(classifier, CHECK_OK); result = this->ParseArrayLiteral(classifier, CHECK_OK);
break; break;
case Token::LBRACE: case Token::LBRACE:
if (!allow_harmony_destructuring()) {
BindingPatternUnexpectedToken(classifier);
}
result = this->ParseObjectLiteral(classifier, CHECK_OK); result = this->ParseObjectLiteral(classifier, CHECK_OK);
break; break;
case Token::LPAREN: case Token::LPAREN:
// Arrow function formal parameters are either a single identifier or a
// list of BindingPattern productions enclosed in parentheses.
// Parentheses are not valid on the LHS of a BindingPattern, so we use the
// is_valid_binding_pattern() check to detect multiple levels of
// parenthesization.
if (!classifier->is_valid_binding_pattern()) {
ArrowFormalParametersUnexpectedToken(classifier);
}
BindingPatternUnexpectedToken(classifier); BindingPatternUnexpectedToken(classifier);
Consume(Token::LPAREN); Consume(Token::LPAREN);
if (allow_harmony_arrow_functions() && Check(Token::RPAREN)) { if (allow_harmony_arrow_functions() && Check(Token::RPAREN)) {
// As a primary expression, the only thing that can follow "()" is "=>". // As a primary expression, the only thing that can follow "()" is "=>".
classifier->RecordBindingPatternError(scanner()->location(),
"unexpected_token",
Token::String(Token::RPAREN));
Scope* scope = this->NewScope(scope_, ARROW_SCOPE); Scope* scope = this->NewScope(scope_, ARROW_SCOPE);
scope->set_start_position(beg_pos); scope->set_start_position(beg_pos);
FormalParameterErrorLocations error_locs; ExpressionClassifier args_classifier;
bool has_rest = false; bool has_rest = false;
result = this->ParseArrowFunctionLiteral(scope, error_locs, has_rest, result = this->ParseArrowFunctionLiteral(scope, has_rest,
classifier, CHECK_OK); args_classifier, CHECK_OK);
} else { } else {
// Heuristically try to detect immediately called functions before // Heuristically try to detect immediately called functions before
// seeing the call parentheses. // seeing the call parentheses.
@ -2364,13 +2443,18 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
// AssignmentExpression // AssignmentExpression
// Expression ',' AssignmentExpression // Expression ',' AssignmentExpression
ExpressionClassifier binding_classifier;
ExpressionT result = ExpressionT result =
this->ParseAssignmentExpression(accept_IN, classifier, CHECK_OK); this->ParseAssignmentExpression(accept_IN, &binding_classifier, CHECK_OK);
classifier->Accumulate(binding_classifier,
ExpressionClassifier::AllProductions);
while (peek() == Token::COMMA) { while (peek() == Token::COMMA) {
Expect(Token::COMMA, CHECK_OK); Expect(Token::COMMA, CHECK_OK);
int pos = position(); int pos = position();
ExpressionT right = ExpressionT right = this->ParseAssignmentExpression(
this->ParseAssignmentExpression(accept_IN, classifier, CHECK_OK); accept_IN, &binding_classifier, CHECK_OK);
classifier->Accumulate(binding_classifier,
ExpressionClassifier::AllProductions);
result = factory()->NewBinaryOperation(Token::COMMA, result, right, pos); result = factory()->NewBinaryOperation(Token::COMMA, result, right, pos);
} }
return result; return result;
@ -2756,29 +2840,52 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
if (fni_ != NULL) fni_->Enter(); if (fni_ != NULL) fni_->Enter();
ParserBase<Traits>::Checkpoint checkpoint(this); ParserBase<Traits>::Checkpoint checkpoint(this);
ExpressionT expression = ExpressionClassifier arrow_formals_classifier;
this->ParseConditionalExpression(accept_IN, classifier, CHECK_OK); if (peek() != Token::LPAREN) {
// The expression we are going to read is not a parenthesized arrow function
// formal parameter list.
ArrowFormalParametersUnexpectedToken(&arrow_formals_classifier);
}
ExpressionT expression = this->ParseConditionalExpression(
accept_IN, &arrow_formals_classifier, CHECK_OK);
classifier->Accumulate(arrow_formals_classifier);
if (allow_harmony_arrow_functions() && peek() == Token::ARROW) { if (allow_harmony_arrow_functions() && peek() == Token::ARROW) {
checkpoint.Restore(); checkpoint.Restore();
FormalParameterErrorLocations error_locs; BindingPatternUnexpectedToken(classifier);
ValidateArrowFormalParameters(&arrow_formals_classifier, expression,
CHECK_OK);
Scanner::Location loc(lhs_location.beg_pos, scanner()->location().end_pos); Scanner::Location loc(lhs_location.beg_pos, scanner()->location().end_pos);
bool has_rest = false; bool has_rest = false;
Scope* scope = this->NewScope(scope_, ARROW_SCOPE); Scope* scope = this->NewScope(scope_, ARROW_SCOPE);
scope->set_start_position(lhs_location.beg_pos); scope->set_start_position(lhs_location.beg_pos);
this->ParseArrowFunctionFormalParameters(scope, expression, loc, Scanner::Location duplicate_loc = Scanner::Location::invalid();
&error_locs, &has_rest, CHECK_OK); this->ParseArrowFunctionFormalParameters(scope, expression, loc, &has_rest,
expression = this->ParseArrowFunctionLiteral(scope, error_locs, has_rest, &duplicate_loc, CHECK_OK);
classifier, CHECK_OK); if (duplicate_loc.IsValid()) {
arrow_formals_classifier.RecordDuplicateFormalParameterError(
duplicate_loc);
}
expression = this->ParseArrowFunctionLiteral(
scope, has_rest, arrow_formals_classifier, CHECK_OK);
return expression; return expression;
} }
// "expression" was not itself an arrow function parameter list, but it might
// form part of one. Propagate speculative formal parameter error locations.
classifier->Accumulate(arrow_formals_classifier,
ExpressionClassifier::FormalParametersProduction);
if (!Token::IsAssignmentOp(peek())) { if (!Token::IsAssignmentOp(peek())) {
if (fni_ != NULL) fni_->Leave(); if (fni_ != NULL) fni_->Leave();
// Parsed conditional expression only (no assignment). // Parsed conditional expression only (no assignment).
return expression; return expression;
} }
if (!allow_harmony_destructuring()) {
BindingPatternUnexpectedToken(classifier);
}
expression = this->CheckAndRewriteReferenceExpression( expression = this->CheckAndRewriteReferenceExpression(
expression, lhs_location, "invalid_lhs_in_assignment", CHECK_OK); expression, lhs_location, "invalid_lhs_in_assignment", CHECK_OK);
expression = this->MarkExpressionAsAssigned(expression); expression = this->MarkExpressionAsAssigned(expression);
@ -2880,6 +2987,7 @@ ParserBase<Traits>::ParseConditionalExpression(bool accept_IN,
ExpressionT expression = ExpressionT expression =
this->ParseBinaryExpression(4, accept_IN, classifier, CHECK_OK); this->ParseBinaryExpression(4, accept_IN, classifier, CHECK_OK);
if (peek() != Token::CONDITIONAL) return expression; if (peek() != Token::CONDITIONAL) return expression;
BindingPatternUnexpectedToken(classifier);
Consume(Token::CONDITIONAL); Consume(Token::CONDITIONAL);
// In parsing the first assignment expression in conditional // In parsing the first assignment expression in conditional
// expressions we always accept the 'in' keyword; see ECMA-262, // expressions we always accept the 'in' keyword; see ECMA-262,
@ -2903,6 +3011,7 @@ ParserBase<Traits>::ParseBinaryExpression(int prec, bool accept_IN,
for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) { for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) {
// prec1 >= 4 // prec1 >= 4
while (Precedence(peek(), accept_IN) == prec1) { while (Precedence(peek(), accept_IN) == prec1) {
BindingPatternUnexpectedToken(classifier);
Token::Value op = Next(); Token::Value op = Next();
Scanner::Location op_location = scanner()->location(); Scanner::Location op_location = scanner()->location();
int pos = position(); int pos = position();
@ -3469,37 +3578,26 @@ ParserBase<Traits>::ParseMemberExpressionContinuation(
template <class Traits> template <class Traits>
void ParserBase<Traits>::ParseFormalParameter( void ParserBase<Traits>::ParseFormalParameter(FormalParameterScopeT* scope,
FormalParameterScopeT* scope, FormalParameterErrorLocations* locs, bool is_rest,
bool is_rest, bool* ok) { ExpressionClassifier* classifier,
bool* ok) {
// FormalParameter[Yield,GeneratorParameter] : // FormalParameter[Yield,GeneratorParameter] :
// BindingElement[?Yield, ?GeneratorParameter] // BindingElement[?Yield, ?GeneratorParameter]
bool is_strict_reserved; IdentifierT name = ParseAndClassifyIdentifier(classifier, ok);
IdentifierT name =
ParseIdentifierOrStrictReservedWord(&is_strict_reserved, ok);
if (!*ok) return; if (!*ok) return;
// Store locations for possible future error reports.
if (!locs->eval_or_arguments.IsValid() && this->IsEvalOrArguments(name)) {
locs->eval_or_arguments = scanner()->location();
}
if (!locs->undefined.IsValid() && this->IsUndefined(name)) {
locs->undefined = scanner()->location();
}
if (!locs->reserved.IsValid() && is_strict_reserved) {
locs->reserved = scanner()->location();
}
bool was_declared = Traits::DeclareFormalParameter(scope, name, is_rest); bool was_declared = Traits::DeclareFormalParameter(scope, name, is_rest);
if (!locs->duplicate.IsValid() && was_declared) { if (was_declared) {
locs->duplicate = scanner()->location(); classifier->RecordDuplicateFormalParameterError(scanner()->location());
} }
} }
template <class Traits> template <class Traits>
int ParserBase<Traits>::ParseFormalParameterList( int ParserBase<Traits>::ParseFormalParameterList(
FormalParameterScopeT* scope, FormalParameterErrorLocations* locs, FormalParameterScopeT* scope, bool* is_rest,
bool* is_rest, bool* ok) { ExpressionClassifier* classifier, bool* ok) {
// FormalParameters[Yield,GeneratorParameter] : // FormalParameters[Yield,GeneratorParameter] :
// [empty] // [empty]
// FormalParameterList[?Yield, ?GeneratorParameter] // FormalParameterList[?Yield, ?GeneratorParameter]
@ -3524,7 +3622,7 @@ int ParserBase<Traits>::ParseFormalParameterList(
return -1; return -1;
} }
*is_rest = allow_harmony_rest_params() && Check(Token::ELLIPSIS); *is_rest = allow_harmony_rest_params() && Check(Token::ELLIPSIS);
ParseFormalParameter(scope, locs, *is_rest, ok); ParseFormalParameter(scope, *is_rest, classifier, ok);
if (!*ok) return -1; if (!*ok) return -1;
} while (!*is_rest && Check(Token::COMMA)); } while (!*is_rest && Check(Token::COMMA));
@ -3567,8 +3665,8 @@ void ParserBase<Traits>::CheckArityRestrictions(
template <class Traits> template <class Traits>
typename ParserBase<Traits>::ExpressionT typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::ParseArrowFunctionLiteral( ParserBase<Traits>::ParseArrowFunctionLiteral(
Scope* scope, const FormalParameterErrorLocations& error_locs, Scope* scope, bool has_rest, const ExpressionClassifier& formals_classifier,
bool has_rest, ExpressionClassifier* classifier, bool* ok) { bool* ok) {
if (peek() == Token::ARROW && scanner_->HasAnyLineTerminatorBeforeNext()) { if (peek() == Token::ARROW && scanner_->HasAnyLineTerminatorBeforeNext()) {
// ASI inserts `;` after arrow parameters if a line terminator is found. // ASI inserts `;` after arrow parameters if a line terminator is found.
// `=> ...` is never a valid expression, so report as syntax error. // `=> ...` is never a valid expression, so report as syntax error.
@ -3590,9 +3688,6 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
FunctionState function_state(&function_state_, &scope_, scope, FunctionState function_state(&function_state_, &scope_, scope,
kArrowFunction, &function_factory); kArrowFunction, &function_factory);
if (peek() == Token::ARROW) {
BindingPatternUnexpectedToken(classifier);
}
Expect(Token::ARROW, CHECK_OK); Expect(Token::ARROW, CHECK_OK);
if (peek() == Token::LBRACE) { if (peek() == Token::LBRACE) {
@ -3617,8 +3712,10 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
// Single-expression body // Single-expression body
int pos = position(); int pos = position();
parenthesized_function_ = false; parenthesized_function_ = false;
ExpressionClassifier classifier;
ExpressionT expression = ExpressionT expression =
ParseAssignmentExpression(true, classifier, CHECK_OK); ParseAssignmentExpression(true, &classifier, CHECK_OK);
ValidateExpression(&classifier, CHECK_OK);
body = this->NewStatementList(1, zone()); body = this->NewStatementList(1, zone());
body->Add(factory()->NewReturnStatement(expression, pos), zone()); body->Add(factory()->NewReturnStatement(expression, pos), zone());
materialized_literal_count = function_state.materialized_literal_count(); materialized_literal_count = function_state.materialized_literal_count();
@ -3633,9 +3730,9 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
// which is not the same as "parameters of a strict function"; it only means // which is not the same as "parameters of a strict function"; it only means
// that duplicates are not allowed. Of course, the arrow function may // that duplicates are not allowed. Of course, the arrow function may
// itself be strict as well. // itself be strict as well.
const bool use_strict_params = true; const bool allow_duplicate_parameters = false;
this->CheckFunctionParameterNames(language_mode(), use_strict_params, this->ValidateFormalParameters(&formals_classifier, language_mode(),
error_locs, CHECK_OK); allow_duplicate_parameters, CHECK_OK);
// Validate strict mode. // Validate strict mode.
if (is_strict(language_mode())) { if (is_strict(language_mode())) {

View File

@ -3563,6 +3563,10 @@ TEST(ErrorsArrowFunctions) {
"(foo ? bar : baz) => {}", "(foo ? bar : baz) => {}",
"(a, foo ? bar : baz) => {}", "(a, foo ? bar : baz) => {}",
"(foo ? bar : baz, a) => {}", "(foo ? bar : baz, a) => {}",
"(a.b, c) => {}",
"(c, a.b) => {}",
"(a['b'], c) => {}",
"(c, a['b']) => {}",
NULL NULL
}; };

View File

@ -28,7 +28,7 @@
// Test the case when exception is thrown from the parser when lazy // Test the case when exception is thrown from the parser when lazy
// compiling a function. // compiling a function.
// Flags: --stack_size=32 // Flags: --stack_size=42
// NOTE: stack size constant above has been empirically chosen. // NOTE: stack size constant above has been empirically chosen.
// If the test starts to fail in Genesis, consider increasing this constant. // If the test starts to fail in Genesis, consider increasing this constant.