Commit 81504642 authored by marja@chromium.org's avatar marja@chromium.org

Make strict more error messages about "eval" and "arguments" less specific.

We used to have error messages which provide context, like "Variable name may
not be eval or arguments in strict mode", but for other illegal words we only
have non-context specific error messages like "Unexpected reserved word".

Providing the context makes the code unnecessarily complex, since every
individual place must remember to check for eval or arguments. This CL produces
a unified error message ("Unexpected eval or arguments in strict mode"), and puts
the error reporting to (Pre)Parser::ParseIdentifier.

Notes:

- The module feature is so experimental, that I decided to not allow "eval" or
"arguments" as module-related identifiers in the strict mode (even though this
check wasn't there before).

- Unfortunately, there were some inconsistencies, since it was the
responsibility of the caller of ParseIdentifier to check "eval" and "arguments"
and some places didn't have the check for no good reason. This CL is supposed to
keep backward compatibility and *not* introduce any new errors.

- ECMA allows "eval" and "arguments" as labels even in strict mode. (Syntax:
"LabelledStatement: Identifier : Statement", and no strict mode restrictions on
Identifier are listed.)

- Tests which compare error message strings will fail, and need to be updated.

BUG=3126
LOG=N
R=ulan@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19112 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 5028ebaa
......@@ -155,21 +155,15 @@ var kMessages = {
illegal_access: ["Illegal access"],
invalid_preparser_data: ["Invalid preparser data for function ", "%0"],
strict_mode_with: ["Strict mode code may not include a with statement"],
strict_catch_variable: ["Catch variable may not be eval or arguments in strict mode"],
strict_eval_arguments: ["Unexpected eval or arguments in strict mode"],
too_many_arguments: ["Too many arguments in function call (only 32766 allowed)"],
too_many_parameters: ["Too many parameters in function definition (only 32766 allowed)"],
too_many_variables: ["Too many variables declared (only 131071 allowed)"],
strict_param_name: ["Parameter name eval or arguments is not allowed in strict mode"],
strict_param_dupe: ["Strict mode function may not have duplicate parameter names"],
strict_var_name: ["Variable name may not be eval or arguments in strict mode"],
strict_function_name: ["Function name may not be eval or arguments in strict mode"],
strict_octal_literal: ["Octal literals are not allowed in strict mode."],
strict_duplicate_property: ["Duplicate data property in object literal not allowed in strict mode"],
accessor_data_property: ["Object literal may not have data and accessor property with the same name"],
accessor_get_set: ["Object literal may not have multiple get/set accessors with the same name"],
strict_lhs_assignment: ["Assignment to eval or arguments is not allowed in strict mode"],
strict_lhs_postfix: ["Postfix increment/decrement may not have eval or arguments operand in strict mode"],
strict_lhs_prefix: ["Prefix increment/decrement may not have eval or arguments operand in strict mode"],
strict_delete: ["Delete of an unqualified identifier in strict mode."],
strict_delete_property: ["Cannot delete property '", "%0", "' of ", "%1"],
strict_const: ["Use of const in strict mode."],
......
......@@ -983,7 +983,7 @@ Statement* Parser::ParseModuleDeclaration(ZoneStringList* names, bool* ok) {
// 'module' Identifier Module
int pos = peek_position();
Handle<String> name = ParseIdentifier(CHECK_OK);
Handle<String> name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
#ifdef DEBUG
if (FLAG_print_interface_details)
......@@ -1136,7 +1136,7 @@ Module* Parser::ParseModuleVariable(bool* ok) {
// Identifier
int pos = peek_position();
Handle<String> name = ParseIdentifier(CHECK_OK);
Handle<String> name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
#ifdef DEBUG
if (FLAG_print_interface_details)
PrintF("# Module variable %s ", name->ToAsciiArray());
......@@ -1261,13 +1261,14 @@ Statement* Parser::ParseExportDeclaration(bool* ok) {
switch (peek()) {
case Token::IDENTIFIER: {
int pos = position();
Handle<String> name = ParseIdentifier(CHECK_OK);
Handle<String> name =
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
// Handle 'module' as a context-sensitive keyword.
if (!name->IsOneByteEqualTo(STATIC_ASCII_VECTOR("module"))) {
names.Add(name, zone());
while (peek() == Token::COMMA) {
Consume(Token::COMMA);
name = ParseIdentifier(CHECK_OK);
name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
names.Add(name, zone());
}
ExpectSemicolon(CHECK_OK);
......@@ -1632,11 +1633,12 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
Statement* Parser::ParseNativeDeclaration(bool* ok) {
int pos = peek_position();
Expect(Token::FUNCTION, CHECK_OK);
Handle<String> name = ParseIdentifier(CHECK_OK);
// Allow "eval" or "arguments" for backward compatibility.
Handle<String> name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
bool done = (peek() == Token::RPAREN);
while (!done) {
ParseIdentifier(CHECK_OK);
ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
done = (peek() == Token::RPAREN);
if (!done) {
Expect(Token::COMMA, CHECK_OK);
......@@ -1900,16 +1902,9 @@ Block* Parser::ParseVariableDeclarations(
// Parse variable name.
if (nvars > 0) Consume(Token::COMMA);
name = ParseIdentifier(CHECK_OK);
name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
if (fni_ != NULL) fni_->PushVariableName(name);
// Strict mode variables may not be named eval or arguments
if (!declaration_scope->is_classic_mode() && IsEvalOrArguments(name)) {
ReportMessage("strict_var_name", Vector<const char*>::empty());
*ok = false;
return NULL;
}
// Declare variable.
// Note that we *always* must treat the initial value via a separate init
// assignment for variables and constants because the value must be assigned
......@@ -2224,7 +2219,8 @@ Statement* Parser::ParseContinueStatement(bool* ok) {
Token::Value tok = peek();
if (!scanner().HasAnyLineTerminatorBeforeNext() &&
tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
label = ParseIdentifier(CHECK_OK);
// ECMA allows "eval" or "arguments" as labels even in strict mode.
label = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
}
IterationStatement* target = NULL;
target = LookupContinueTarget(label, CHECK_OK);
......@@ -2255,7 +2251,8 @@ Statement* Parser::ParseBreakStatement(ZoneStringList* labels, bool* ok) {
Token::Value tok = peek();
if (!scanner().HasAnyLineTerminatorBeforeNext() &&
tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
label = ParseIdentifier(CHECK_OK);
// ECMA allows "eval" or "arguments" as labels even in strict mode.
label = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
}
// Parse labeled break statements that target themselves into
// empty statements, e.g. 'l1: l2: l3: break l2;'
......@@ -2485,13 +2482,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
Expect(Token::LPAREN, CHECK_OK);
catch_scope = NewScope(top_scope_, CATCH_SCOPE);
catch_scope->set_start_position(scanner().location().beg_pos);
name = ParseIdentifier(CHECK_OK);
if (!top_scope_->is_classic_mode() && IsEvalOrArguments(name)) {
ReportMessage("strict_catch_variable", Vector<const char*>::empty());
*ok = false;
return NULL;
}
name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
......@@ -2939,7 +2930,7 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) {
if (!top_scope_->is_classic_mode()) {
// Assignment to eval or arguments is disallowed in strict mode.
CheckStrictModeLValue(expression, "strict_lhs_assignment", CHECK_OK);
CheckStrictModeLValue(expression, CHECK_OK);
}
MarkAsLValue(expression);
......@@ -3219,7 +3210,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
if (!top_scope_->is_classic_mode()) {
// Prefix expression operand in strict mode may not be eval or arguments.
CheckStrictModeLValue(expression, "strict_lhs_prefix", CHECK_OK);
CheckStrictModeLValue(expression, CHECK_OK);
}
MarkAsLValue(expression);
......@@ -3253,7 +3244,7 @@ Expression* Parser::ParsePostfixExpression(bool* ok) {
if (!top_scope_->is_classic_mode()) {
// Postfix expression operand in strict mode may not be eval or arguments.
CheckStrictModeLValue(expression, "strict_lhs_postfix", CHECK_OK);
CheckStrictModeLValue(expression, CHECK_OK);
}
MarkAsLValue(expression);
......@@ -3563,7 +3554,8 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) {
case Token::IDENTIFIER:
case Token::YIELD:
case Token::FUTURE_STRICT_RESERVED_WORD: {
Handle<String> name = ParseIdentifier(CHECK_OK);
// Using eval or arguments in this context is OK even in strict mode.
Handle<String> name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
if (fni_ != NULL) fni_->PushVariableName(name);
// The name may refer to a module instance object, so its type is unknown.
#ifdef DEBUG
......@@ -4303,13 +4295,13 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
if (!top_scope_->is_classic_mode()) {
if (IsEvalOrArguments(function_name)) {
ReportMessageAt(function_name_location,
"strict_function_name",
"strict_eval_arguments",
Vector<const char*>::empty());
*ok = false;
return NULL;
}
if (name_loc.IsValid()) {
ReportMessageAt(name_loc, "strict_param_name",
ReportMessageAt(name_loc, "strict_eval_arguments",
Vector<const char*>::empty());
*ok = false;
return NULL;
......@@ -4398,7 +4390,8 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) {
int pos = peek_position();
Expect(Token::MOD, CHECK_OK);
Handle<String> name = ParseIdentifier(CHECK_OK);
// Allow "eval" or "arguments" for backward compatibility.
Handle<String> name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
ZoneList<Expression*>* args = ParseArguments(CHECK_OK);
if (extension_ != NULL) {
......@@ -4505,13 +4498,25 @@ Literal* Parser::GetLiteralTheHole(int position) {
// Parses an identifier that is valid for the current scope, in particular it
// fails on strict mode future reserved keywords in a strict scope.
Handle<String> Parser::ParseIdentifier(bool* ok) {
// fails on strict mode future reserved keywords in a strict scope. If
// allow_eval_or_arguments is kAllowEvalOrArguments, we allow "eval" or
// "arguments" as identifier even in strict mode (this is needed in cases like
// "var foo = eval;").
Handle<String> Parser::ParseIdentifier(
AllowEvalOrArgumentsAsIdentifier allow_eval_or_arguments,
bool* ok) {
Token::Value next = Next();
if (next == Token::IDENTIFIER ||
(top_scope_->is_classic_mode() &&
(next == Token::FUTURE_STRICT_RESERVED_WORD ||
(next == Token::YIELD && !is_generator())))) {
if (next == Token::IDENTIFIER) {
Handle<String> name = GetSymbol();
if (allow_eval_or_arguments == kDontAllowEvalOrArguments &&
!top_scope_->is_classic_mode() && IsEvalOrArguments(name)) {
ReportMessage("strict_eval_arguments", Vector<const char*>::empty());
*ok = false;
}
return name;
} else if (top_scope_->is_classic_mode() &&
(next == Token::FUTURE_STRICT_RESERVED_WORD ||
(next == Token::YIELD && !is_generator()))) {
return GetSymbol();
} else {
ReportUnexpectedToken(next);
......@@ -4566,7 +4571,6 @@ void Parser::MarkAsLValue(Expression* expression) {
// Checks LHS expression for assignment and prefix/postfix increment/decrement
// in strict mode.
void Parser::CheckStrictModeLValue(Expression* expression,
const char* error,
bool* ok) {
ASSERT(!top_scope_->is_classic_mode());
VariableProxy* lhs = expression != NULL
......@@ -4574,7 +4578,7 @@ void Parser::CheckStrictModeLValue(Expression* expression,
: NULL;
if (lhs != NULL && !lhs->is_this() && IsEvalOrArguments(lhs->name())) {
ReportMessage(error, Vector<const char*>::empty());
ReportMessage("strict_eval_arguments", Vector<const char*>::empty());
*ok = false;
}
}
......
......@@ -687,7 +687,7 @@ class Parser : public ParserBase {
Literal* GetLiteralUndefined(int position);
Literal* GetLiteralTheHole(int position);
Handle<String> ParseIdentifier(bool* ok);
Handle<String> ParseIdentifier(AllowEvalOrArgumentsAsIdentifier, bool* ok);
Handle<String> ParseIdentifierOrStrictReservedWord(
bool* is_strict_reserved, bool* ok);
Handle<String> ParseIdentifierName(bool* ok);
......@@ -702,7 +702,6 @@ class Parser : public ParserBase {
// Strict mode validation of LValue expressions
void CheckStrictModeLValue(Expression* expression,
const char* error,
bool* ok);
// For harmony block scoping mode: Check if the scope has conflicting var/let
......
......@@ -296,16 +296,19 @@ PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) {
Expect(Token::FUNCTION, CHECK_OK);
bool is_generator = allow_generators() && Check(Token::MUL);
Identifier identifier = ParseIdentifier(CHECK_OK);
Identifier identifier = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
Scanner::Location location = scanner()->location();
Expression function_value = ParseFunctionLiteral(is_generator, CHECK_OK);
// If we're in strict mode, ParseIdentifier will catch using eval, arguments
// or a strict reserved word as function name. However, if only the function
// is strict, we need to do an extra check.
if (function_value.IsStrictFunction() &&
!identifier.IsValidStrictVariable()) {
// Strict mode violation, using either reserved word or eval/arguments
// as name of strict function.
const char* type = "strict_function_name";
const char* type = "strict_eval_arguments";
if (identifier.IsFutureStrictReserved() || identifier.IsYield()) {
type = "unexpected_strict_reserved";
}
......@@ -446,14 +449,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
do {
// Parse variable name.
if (nvars > 0) Consume(Token::COMMA);
Identifier identifier = ParseIdentifier(CHECK_OK);
if (!is_classic_mode() && !identifier.IsValidStrictVariable()) {
StrictModeIdentifierViolation(scanner()->location(),
"strict_var_name",
identifier,
ok);
return Statement::Default();
}
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
nvars++;
if (peek() == Token::ASSIGN || require_initializer) {
Expect(Token::ASSIGN, CHECK_OK);
......@@ -519,7 +515,8 @@ PreParser::Statement PreParser::ParseContinueStatement(bool* ok) {
tok != Token::SEMICOLON &&
tok != Token::RBRACE &&
tok != Token::EOS) {
ParseIdentifier(CHECK_OK);
// ECMA allows "eval" or "arguments" as labels even in strict mode.
ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
}
ExpectSemicolon(CHECK_OK);
return Statement::Default();
......@@ -536,7 +533,8 @@ PreParser::Statement PreParser::ParseBreakStatement(bool* ok) {
tok != Token::SEMICOLON &&
tok != Token::RBRACE &&
tok != Token::EOS) {
ParseIdentifier(CHECK_OK);
// ECMA allows "eval" or "arguments" as labels even in strict mode.
ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
}
ExpectSemicolon(CHECK_OK);
return Statement::Default();
......@@ -753,14 +751,7 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
if (peek() == Token::CATCH) {
Consume(Token::CATCH);
Expect(Token::LPAREN, CHECK_OK);
Identifier id = ParseIdentifier(CHECK_OK);
if (!is_classic_mode() && !id.IsValidStrictVariable()) {
StrictModeIdentifierViolation(scanner()->location(),
"strict_catch_variable",
id,
ok);
return Statement::Default();
}
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
{ Scope::InsideWith iw(scope_);
ParseBlock(CHECK_OK);
......@@ -841,7 +832,7 @@ PreParser::Expression PreParser::ParseAssignmentExpression(bool accept_IN,
expression.AsIdentifier().IsEvalOrArguments()) {
Scanner::Location after = scanner()->location();
ReportMessageAt(before.beg_pos, after.end_pos,
"strict_lhs_assignment", NULL);
"strict_eval_arguments", NULL);
*ok = false;
return Expression::Default();
}
......@@ -935,7 +926,7 @@ PreParser::Expression PreParser::ParseUnaryExpression(bool* ok) {
expression.AsIdentifier().IsEvalOrArguments()) {
Scanner::Location after = scanner()->location();
ReportMessageAt(before.beg_pos, after.end_pos,
"strict_lhs_prefix", NULL);
"strict_eval_arguments", NULL);
*ok = false;
}
return Expression::Default();
......@@ -958,7 +949,7 @@ PreParser::Expression PreParser::ParsePostfixExpression(bool* ok) {
expression.AsIdentifier().IsEvalOrArguments()) {
Scanner::Location after = scanner()->location();
ReportMessageAt(before.beg_pos, after.end_pos,
"strict_lhs_postfix", NULL);
"strict_eval_arguments", NULL);
*ok = false;
return Expression::Default();
}
......@@ -1059,12 +1050,15 @@ PreParser::Expression PreParser::ParseMemberWithNewPrefixesExpression(
bool is_generator = allow_generators() && Check(Token::MUL);
Identifier identifier = Identifier::Default();
if (peek_any_identifier()) {
identifier = ParseIdentifier(CHECK_OK);
identifier = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
}
result = ParseFunctionLiteral(is_generator, CHECK_OK);
// If we're in strict mode, ParseIdentifier will catch using eval, arguments
// or a strict reserved word as function name. However, if only the function
// is strict, we need to do an extra check.
if (result.IsStrictFunction() && !identifier.IsValidStrictVariable()) {
StrictModeIdentifierViolation(scanner()->location(),
"strict_function_name",
"strict_eval_arguments",
identifier,
ok);
return Expression::Default();
......@@ -1137,7 +1131,8 @@ PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
case Token::FUTURE_STRICT_RESERVED_WORD:
case Token::YIELD:
case Token::IDENTIFIER: {
Identifier id = ParseIdentifier(CHECK_OK);
// Using eval or arguments in this context is OK even in strict mode.
Identifier id = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
result = Expression::FromIdentifier(id);
break;
}
......@@ -1354,13 +1349,7 @@ PreParser::Expression PreParser::ParseFunctionLiteral(bool is_generator,
bool done = (peek() == Token::RPAREN);
DuplicateFinder duplicate_finder(scanner()->unicode_cache());
while (!done) {
Identifier id = ParseIdentifier(CHECK_OK);
if (!id.IsValidStrictVariable()) {
StrictModeIdentifierViolation(scanner()->location(),
"strict_param_name",
id,
CHECK_OK);
}
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
int prev_value;
if (scanner()->is_literal_ascii()) {
prev_value =
......@@ -1434,7 +1423,8 @@ PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) {
*ok = false;
return Expression::Default();
}
ParseIdentifier(CHECK_OK);
// Allow "eval" or "arguments" for backward compatibility.
ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
ParseArguments(ok);
return Expression::Default();
......@@ -1493,12 +1483,26 @@ PreParser::Identifier PreParser::GetIdentifierSymbol() {
}
PreParser::Identifier PreParser::ParseIdentifier(bool* ok) {
// Parses an identifier that is valid for the current scope, in particular it
// fails on strict mode future reserved keywords in a strict scope. If
// allow_eval_or_arguments is kAllowEvalOrArguments, we allow "eval" or
// "arguments" as identifier even in strict mode (this is needed in cases like
// "var foo = eval;").
PreParser::Identifier PreParser::ParseIdentifier(
AllowEvalOrArgumentsAsIdentifier allow_eval_or_arguments,
bool* ok) {
Token::Value next = Next();
if (next == Token::IDENTIFIER ||
(is_classic_mode() &&
(next == Token::FUTURE_STRICT_RESERVED_WORD ||
(next == Token::YIELD && !scope_->is_generator())))) {
if (next == Token::IDENTIFIER) {
PreParser::Identifier name = GetIdentifierSymbol();
if (allow_eval_or_arguments == kDontAllowEvalOrArguments &&
!is_classic_mode() && name.IsEvalOrArguments()) {
StrictModeIdentifierViolation(
scanner()->location(), "strict_eval_arguments", name, ok);
}
return name;
} else if (is_classic_mode() &&
(next == Token::FUTURE_STRICT_RESERVED_WORD ||
(next == Token::YIELD && !scope_->is_generator()))) {
return GetIdentifierSymbol();
} else {
ReportUnexpectedToken(next);
......
......@@ -76,6 +76,11 @@ class ParserBase {
}
protected:
enum AllowEvalOrArgumentsAsIdentifier {
kAllowEvalOrArguments,
kDontAllowEvalOrArguments
};
Scanner* scanner() const { return scanner_; }
int position() { return scanner_->location().beg_pos; }
int peek_position() { return scanner_->peek_location().beg_pos; }
......@@ -640,7 +645,7 @@ class PreParser : public ParserBase {
Expression ParseFunctionLiteral(bool is_generator, bool* ok);
void ParseLazyFunctionLiteralBody(bool* ok);
Identifier ParseIdentifier(bool* ok);
Identifier ParseIdentifier(AllowEvalOrArgumentsAsIdentifier, bool* ok);
Identifier ParseIdentifierName(bool* ok);
Identifier ParseIdentifierNameOrGetOrSet(bool* is_get,
bool* is_set,
......
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