From 443e645a2d4e4d7ffa8d4229f0fe5b2e449c75bb Mon Sep 17 00:00:00 2001 From: "marja@chromium.org" Date: Tue, 4 Feb 2014 12:19:53 +0000 Subject: [PATCH] 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 --- src/messages.js | 1 - src/parser.cc | 8 +- src/preparser.cc | 10 +- test/cctest/test-parsing.cc | 235 ++++++++++++++++++++++++++++++++++++-------- 4 files changed, 202 insertions(+), 52 deletions(-) diff --git a/src/messages.js b/src/messages.js index 41e1d97..85351fb 100644 --- a/src/messages.js +++ b/src/messages.js @@ -170,7 +170,6 @@ var kMessages = { 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_reserved_word: ["Use of future reserved word 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."], diff --git a/src/parser.cc b/src/parser.cc index 303c5ee..50551ea 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -4321,17 +4321,13 @@ FunctionLiteral* Parser::ParseFunctionLiteral( return NULL; } if (name_is_strict_reserved) { - int start_pos = scope->start_position(); - 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", + ReportMessageAt(function_name_location, "unexpected_strict_reserved", Vector::empty()); *ok = false; return NULL; } if (reserved_loc.IsValid()) { - ReportMessageAt(reserved_loc, "strict_reserved_word", + ReportMessageAt(reserved_loc, "unexpected_strict_reserved", Vector::empty()); *ok = false; return NULL; diff --git a/src/preparser.cc b/src/preparser.cc index c8a59bf..21010ae 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -121,7 +121,9 @@ void PreParser::ReportUnexpectedToken(Token::Value token) { return ReportMessageAt(source_location, "unexpected_reserved", NULL); case Token::FUTURE_STRICT_RESERVED_WORD: return ReportMessageAt(source_location, - "unexpected_strict_reserved", NULL); + is_classic_mode() ? "unexpected_token_identifier" + : "unexpected_strict_reserved", + NULL); default: const char* name = Token::String(token); ReportMessageAt(source_location, "unexpected_token", name); @@ -304,7 +306,7 @@ PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) { // as name of strict function. const char* type = "strict_function_name"; if (identifier.IsFutureStrictReserved() || identifier.IsYield()) { - type = "strict_reserved_word"; + type = "unexpected_strict_reserved"; } ReportMessageAt(location, type, NULL); *ok = false; @@ -1511,7 +1513,7 @@ PreParser::Identifier PreParser::ParseIdentifier(bool* ok) { if (!is_classic_mode()) { Scanner::Location location = scanner()->location(); ReportMessageAt(location.beg_pos, location.end_pos, - "strict_reserved_word", NULL); + "unexpected_strict_reserved", NULL); *ok = false; } // FALLTHROUGH @@ -1565,7 +1567,7 @@ void PreParser::StrictModeIdentifierViolation(Scanner::Location location, if (identifier.IsFutureReserved()) { type = "reserved_word"; } else if (identifier.IsFutureStrictReserved() || identifier.IsYield()) { - type = "strict_reserved_word"; + type = "unexpected_strict_reserved"; } if (!is_classic_mode()) { ReportMessageAt(location, type, NULL); diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index c2651dc..e5cddbf 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -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 source) { v8::ScriptData* preparse = v8::ScriptData::PreCompile(source); CHECK(!preparse->HasError()); @@ -1381,6 +1435,8 @@ void VerifyPreParseAndParseErrorMessages(v8::Handle source, CHECK_EQ(error_location_end, try_catch.Message()->GetEndPosition()); } +} // namespace + TEST(ErrorsEvalAndArguments) { // Tests that both preparsing and parsing produce the right kind of errors for @@ -1392,48 +1448,9 @@ TEST(ErrorsEvalAndArguments) { v8::Local 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); - 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* 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[] = { + ParseErrorTestCase test_cases[] = { {"var eval = 42;", 4, 8, strict_var_name_preparse, strict_var_name_parse}, {"var arguments = 42;", 4, 13, strict_var_name_preparse, strict_var_name_parse}, @@ -1472,13 +1489,105 @@ TEST(ErrorsEvalAndArguments) { for (int i = 0; test_cases[i].source; ++i) { v8::Handle source = v8::String::NewFromUtf8(isolate, test_cases[i].source); - VerifyPreParseAndParseNoError(source); v8::Handle 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 + // "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 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 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 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 source = + v8::String::NewFromUtf8(isolate, test_cases[i].source); + VerifyPreParseAndParseNoError(source); + + v8::Handle 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, @@ -1486,4 +1595,48 @@ TEST(ErrorsEvalAndArguments) { 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 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 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); + } } -- 2.7.4