Tests and fixes for (pre)parse errors related to strict reserved words.

This contains the following fixes:

- We had strict_reserved_word and unexpected_strict_reserved, which one to use
was totally mixed in Parser and PreParser. Removed strict_reserved_word.
- When we saw a strict future reserved word when expecting something completely
different (such as "(" in "function foo interface"), Parser reports unexpected
identifier, whereas PreParser used to report unexpected strict reserved
word. Fixed PreParser to report unexpected identifier too.
- Unified parser and preparser error locations when the name of a function is a
strict reserved word. Now both point to the name.

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

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19067 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
marja@chromium.org 2014-02-04 12:19:53 +00:00
parent e5c7f4eff1
commit 443e645a2d
4 changed files with 203 additions and 53 deletions

View File

@ -170,7 +170,6 @@ var kMessages = {
strict_lhs_assignment: ["Assignment to eval or arguments is not allowed in strict mode"], 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_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_lhs_prefix: ["Prefix increment/decrement may not have eval or arguments operand in strict mode"],
strict_reserved_word: ["Use of future reserved word in strict mode"],
strict_delete: ["Delete of an unqualified identifier in strict mode."], strict_delete: ["Delete of an unqualified identifier in strict mode."],
strict_delete_property: ["Cannot delete property '", "%0", "' of ", "%1"], strict_delete_property: ["Cannot delete property '", "%0", "' of ", "%1"],
strict_const: ["Use of const in strict mode."], strict_const: ["Use of const in strict mode."],

View File

@ -4321,17 +4321,13 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
return NULL; return NULL;
} }
if (name_is_strict_reserved) { if (name_is_strict_reserved) {
int start_pos = scope->start_position(); ReportMessageAt(function_name_location, "unexpected_strict_reserved",
int position = function_token_pos != RelocInfo::kNoPosition
? function_token_pos : (start_pos > 0 ? start_pos - 1 : start_pos);
Scanner::Location location = Scanner::Location(position, start_pos);
ReportMessageAt(location, "strict_reserved_word",
Vector<const char*>::empty()); Vector<const char*>::empty());
*ok = false; *ok = false;
return NULL; return NULL;
} }
if (reserved_loc.IsValid()) { if (reserved_loc.IsValid()) {
ReportMessageAt(reserved_loc, "strict_reserved_word", ReportMessageAt(reserved_loc, "unexpected_strict_reserved",
Vector<const char*>::empty()); Vector<const char*>::empty());
*ok = false; *ok = false;
return NULL; return NULL;

View File

@ -121,7 +121,9 @@ void PreParser::ReportUnexpectedToken(Token::Value token) {
return ReportMessageAt(source_location, "unexpected_reserved", NULL); return ReportMessageAt(source_location, "unexpected_reserved", NULL);
case Token::FUTURE_STRICT_RESERVED_WORD: case Token::FUTURE_STRICT_RESERVED_WORD:
return ReportMessageAt(source_location, return ReportMessageAt(source_location,
"unexpected_strict_reserved", NULL); is_classic_mode() ? "unexpected_token_identifier"
: "unexpected_strict_reserved",
NULL);
default: default:
const char* name = Token::String(token); const char* name = Token::String(token);
ReportMessageAt(source_location, "unexpected_token", name); ReportMessageAt(source_location, "unexpected_token", name);
@ -304,7 +306,7 @@ PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) {
// as name of strict function. // as name of strict function.
const char* type = "strict_function_name"; const char* type = "strict_function_name";
if (identifier.IsFutureStrictReserved() || identifier.IsYield()) { if (identifier.IsFutureStrictReserved() || identifier.IsYield()) {
type = "strict_reserved_word"; type = "unexpected_strict_reserved";
} }
ReportMessageAt(location, type, NULL); ReportMessageAt(location, type, NULL);
*ok = false; *ok = false;
@ -1511,7 +1513,7 @@ PreParser::Identifier PreParser::ParseIdentifier(bool* ok) {
if (!is_classic_mode()) { if (!is_classic_mode()) {
Scanner::Location location = scanner()->location(); Scanner::Location location = scanner()->location();
ReportMessageAt(location.beg_pos, location.end_pos, ReportMessageAt(location.beg_pos, location.end_pos,
"strict_reserved_word", NULL); "unexpected_strict_reserved", NULL);
*ok = false; *ok = false;
} }
// FALLTHROUGH // FALLTHROUGH
@ -1565,7 +1567,7 @@ void PreParser::StrictModeIdentifierViolation(Scanner::Location location,
if (identifier.IsFutureReserved()) { if (identifier.IsFutureReserved()) {
type = "reserved_word"; type = "reserved_word";
} else if (identifier.IsFutureStrictReserved() || identifier.IsYield()) { } else if (identifier.IsFutureStrictReserved() || identifier.IsYield()) {
type = "strict_reserved_word"; type = "unexpected_strict_reserved";
} }
if (!is_classic_mode()) { if (!is_classic_mode()) {
ReportMessageAt(location, type, NULL); ReportMessageAt(location, type, NULL);

View File

@ -1347,6 +1347,60 @@ TEST(PreparserStrictOctal) {
} }
namespace {
const char* use_strict_prefix = "\"use strict\";\n";
const char* strict_catch_variable_preparse = "strict_catch_variable";
const char* strict_catch_variable_parse =
"SyntaxError: Catch variable may not be eval or arguments in strict mode";
const char* strict_function_name_preparse = "strict_function_name";
const char* strict_function_name_parse =
"SyntaxError: Function name may not be eval or arguments in strict mode";
const char* strict_lhs_assignment_preparse = "strict_lhs_assignment";
const char* strict_lhs_assignment_parse =
"SyntaxError: Assignment to eval or arguments is not allowed in strict "
"mode";
const char* strict_lhs_postfix_preparse = "strict_lhs_postfix";
const char* strict_lhs_postfix_parse =
"SyntaxError: Postfix increment/decrement may not have eval or arguments "
"operand in strict mode";
const char* strict_lhs_prefix_preparse = "strict_lhs_prefix";
const char* strict_lhs_prefix_parse =
"SyntaxError: Prefix increment/decrement may not have eval or arguments "
"operand in strict mode";
const char* strict_param_name_preparse = "strict_param_name";
const char* strict_param_name_parse =
"SyntaxError: Parameter name eval or arguments is not allowed in strict "
"mode";
const char* strict_var_name_preparse = "strict_var_name";
const char* strict_var_name_parse =
"SyntaxError: Variable name may not be eval or arguments in strict mode";
const char* unexpected_strict_reserved_preparse = "unexpected_strict_reserved";
const char* unexpected_strict_reserved_parse =
"SyntaxError: Unexpected strict mode reserved word";
const char* unexpected_token_identifier_preparse =
"unexpected_token_identifier";
const char* unexpected_token_identifier_parse =
"SyntaxError: Unexpected identifier";
struct ParseErrorTestCase {
const char* source;
int error_location_beg;
int error_location_end;
const char* preparse_error_message;
const char* parse_error_message;
};
void VerifyPreParseAndParseNoError(v8::Handle<v8::String> source) { void VerifyPreParseAndParseNoError(v8::Handle<v8::String> source) {
v8::ScriptData* preparse = v8::ScriptData::PreCompile(source); v8::ScriptData* preparse = v8::ScriptData::PreCompile(source);
CHECK(!preparse->HasError()); CHECK(!preparse->HasError());
@ -1381,6 +1435,8 @@ void VerifyPreParseAndParseErrorMessages(v8::Handle<v8::String> source,
CHECK_EQ(error_location_end, try_catch.Message()->GetEndPosition()); CHECK_EQ(error_location_end, try_catch.Message()->GetEndPosition());
} }
} // namespace
TEST(ErrorsEvalAndArguments) { TEST(ErrorsEvalAndArguments) {
// Tests that both preparsing and parsing produce the right kind of errors for // Tests that both preparsing and parsing produce the right kind of errors for
@ -1392,48 +1448,9 @@ TEST(ErrorsEvalAndArguments) {
v8::Local<v8::Context> context = v8::Context::New(isolate); v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context); v8::Context::Scope context_scope(context);
const char* use_strict_prefix = "\"use strict\";\n";
int prefix_length = i::StrLength(use_strict_prefix); int prefix_length = i::StrLength(use_strict_prefix);
const char* strict_var_name_preparse = "strict_var_name"; ParseErrorTestCase test_cases[] = {
const char* strict_var_name_parse =
"SyntaxError: Variable name may not be eval or arguments in strict mode";
const char* strict_catch_variable_preparse = "strict_catch_variable";
const char* strict_catch_variable_parse =
"SyntaxError: Catch variable may not be eval or arguments in strict mode";
const char* strict_function_name_preparse = "strict_function_name";
const char* strict_function_name_parse =
"SyntaxError: Function name may not be eval or arguments in strict mode";
const char* strict_param_name_preparse = "strict_param_name";
const char* strict_param_name_parse =
"SyntaxError: Parameter name eval or arguments is not allowed in strict "
"mode";
const char* strict_lhs_assignment_preparse = "strict_lhs_assignment";
const char* strict_lhs_assignment_parse =
"SyntaxError: Assignment to eval or arguments is not allowed in strict "
"mode";
const char* strict_lhs_prefix_preparse = "strict_lhs_prefix";
const char* strict_lhs_prefix_parse =
"SyntaxError: Prefix increment/decrement may not have eval or arguments "
"operand in strict mode";
const char* strict_lhs_postfix_preparse = "strict_lhs_postfix";
const char* strict_lhs_postfix_parse =
"SyntaxError: Postfix increment/decrement may not have eval or arguments "
"operand in strict mode";
struct TestCase {
const char* source;
int error_location_beg;
int error_location_end;
const char* preparse_error_message;
const char* parse_error_message;
} test_cases[] = {
{"var eval = 42;", 4, 8, strict_var_name_preparse, strict_var_name_parse}, {"var eval = 42;", 4, 8, strict_var_name_preparse, strict_var_name_parse},
{"var arguments = 42;", 4, 13, strict_var_name_preparse, {"var arguments = 42;", 4, 13, strict_var_name_preparse,
strict_var_name_parse}, strict_var_name_parse},
@ -1472,13 +1489,11 @@ TEST(ErrorsEvalAndArguments) {
for (int i = 0; test_cases[i].source; ++i) { for (int i = 0; test_cases[i].source; ++i) {
v8::Handle<v8::String> source = v8::Handle<v8::String> source =
v8::String::NewFromUtf8(isolate, test_cases[i].source); v8::String::NewFromUtf8(isolate, test_cases[i].source);
VerifyPreParseAndParseNoError(source); VerifyPreParseAndParseNoError(source);
v8::Handle<v8::String> strict_source = v8::String::Concat( v8::Handle<v8::String> strict_source = v8::String::Concat(
v8::String::NewFromUtf8(isolate, use_strict_prefix), v8::String::NewFromUtf8(isolate, use_strict_prefix),
v8::String::NewFromUtf8(isolate, test_cases[i].source)); v8::String::NewFromUtf8(isolate, test_cases[i].source));
VerifyPreParseAndParseErrorMessages( VerifyPreParseAndParseErrorMessages(
strict_source, strict_source,
test_cases[i].error_location_beg + prefix_length, test_cases[i].error_location_beg + prefix_length,
@ -1486,4 +1501,142 @@ TEST(ErrorsEvalAndArguments) {
test_cases[i].preparse_error_message, test_cases[i].preparse_error_message,
test_cases[i].parse_error_message); test_cases[i].parse_error_message);
} }
// Test cases which produce an error also in non-strict mode.
ParseErrorTestCase error_test_cases[] = {
// In this case we expect something completely different, "(", and get
// "eval". It's always "unexpected identifier, whether we are in strict mode
// or not.
{"function foo eval", 13, 17, unexpected_token_identifier_preparse,
unexpected_token_identifier_parse},
{"\"use strict\"; function foo eval", 27, 31,
unexpected_token_identifier_preparse, unexpected_token_identifier_parse},
{NULL, 0, 0, NULL, NULL}
};
for (int i = 0; error_test_cases[i].source; ++i) {
v8::Handle<v8::String> source =
v8::String::NewFromUtf8(isolate, error_test_cases[i].source);
VerifyPreParseAndParseErrorMessages(
source,
error_test_cases[i].error_location_beg,
error_test_cases[i].error_location_end,
error_test_cases[i].preparse_error_message,
error_test_cases[i].parse_error_message);
}
// Test cases where only a sub-scope is strict.
ParseErrorTestCase scoped_test_cases[] = {
{"var eval = 1; function foo() { \"use strict\"; var arguments = 2; }",
49, 58, strict_var_name_preparse, strict_var_name_parse},
{NULL, 0, 0, NULL, NULL}
};
for (int i = 0; scoped_test_cases[i].source; ++i) {
v8::Handle<v8::String> source =
v8::String::NewFromUtf8(isolate, scoped_test_cases[i].source);
VerifyPreParseAndParseErrorMessages(
source,
scoped_test_cases[i].error_location_beg,
scoped_test_cases[i].error_location_end,
scoped_test_cases[i].preparse_error_message,
scoped_test_cases[i].parse_error_message);
}
}
TEST(ErrorsFutureStrictReservedWords) {
// Tests that both preparsing and parsing produce the right kind of errors for
// using future strict reserved words as identifiers. Without the strict mode,
// it's ok to use future strict reserved words as identifiers. With the strict
// mode, it isn't.
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handles(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
const char* use_strict_prefix = "\"use strict\";\n";
int prefix_length = i::StrLength(use_strict_prefix);
ParseErrorTestCase test_cases[] = {
{"var interface = 42;", 4, 13, unexpected_strict_reserved_preparse,
unexpected_strict_reserved_parse},
{"var foo, interface;", 9, 18, unexpected_strict_reserved_preparse,
unexpected_strict_reserved_parse},
{"try { } catch (interface) { }", 15, 24,
unexpected_strict_reserved_preparse, unexpected_strict_reserved_parse},
{"function interface() { }", 9, 18, unexpected_strict_reserved_preparse,
unexpected_strict_reserved_parse},
{"function foo(interface) { }", 13, 22, unexpected_strict_reserved_preparse,
unexpected_strict_reserved_parse},
{"function foo(bar, interface) { }", 18, 27,
unexpected_strict_reserved_preparse, unexpected_strict_reserved_parse},
{"interface = 1;", 0, 9, unexpected_strict_reserved_preparse,
unexpected_strict_reserved_parse},
{"++interface;", 2, 11, unexpected_strict_reserved_preparse,
unexpected_strict_reserved_parse},
{"interface++;", 0, 9, unexpected_strict_reserved_preparse,
unexpected_strict_reserved_parse},
{NULL, 0, 0, NULL, NULL}
};
for (int i = 0; test_cases[i].source; ++i) {
v8::Handle<v8::String> source =
v8::String::NewFromUtf8(isolate, test_cases[i].source);
VerifyPreParseAndParseNoError(source);
v8::Handle<v8::String> strict_source = v8::String::Concat(
v8::String::NewFromUtf8(isolate, use_strict_prefix),
v8::String::NewFromUtf8(isolate, test_cases[i].source));
VerifyPreParseAndParseErrorMessages(
strict_source,
test_cases[i].error_location_beg + prefix_length,
test_cases[i].error_location_end + prefix_length,
test_cases[i].preparse_error_message,
test_cases[i].parse_error_message);
}
// Test cases which produce an error also in non-strict mode.
ParseErrorTestCase error_test_cases[] = {
// In this case we expect something completely different, "(", and get a
// strict reserved word. Note that this differs from the "eval or arguments"
// case; there we just report an unexpected identifier.
{"function foo interface", 13, 22,
unexpected_token_identifier_preparse,
unexpected_token_identifier_parse},
{"\"use strict\"; function foo interface", 27, 36,
unexpected_strict_reserved_preparse,
unexpected_strict_reserved_parse},
{NULL, 0, 0, NULL, NULL}
};
for (int i = 0; error_test_cases[i].source; ++i) {
v8::Handle<v8::String> source =
v8::String::NewFromUtf8(isolate, error_test_cases[i].source);
VerifyPreParseAndParseErrorMessages(
source,
error_test_cases[i].error_location_beg,
error_test_cases[i].error_location_end,
error_test_cases[i].preparse_error_message,
error_test_cases[i].parse_error_message);
}
// Test cases where only a sub-scope is strict.
ParseErrorTestCase scoped_test_cases[] = {
{"var interface = 1; function foo() { \"use strict\"; var interface = 2; }",
54, 63, unexpected_strict_reserved_preparse,
unexpected_strict_reserved_parse},
{NULL, 0, 0, NULL, NULL}
};
for (int i = 0; scoped_test_cases[i].source; ++i) {
v8::Handle<v8::String> source =
v8::String::NewFromUtf8(isolate, scoped_test_cases[i].source);
VerifyPreParseAndParseErrorMessages(
source,
scoped_test_cases[i].error_location_beg,
scoped_test_cases[i].error_location_end,
scoped_test_cases[i].preparse_error_message,
scoped_test_cases[i].parse_error_message);
}
} }