From: marja@chromium.org Date: Wed, 5 Feb 2014 15:30:01 +0000 (+0000) Subject: Better tests for the Parser / Preparser unification. X-Git-Tag: upstream/4.7.83~10865 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=14a90fcc57a1d7596e69d384989f7be1193cbff8;p=platform%2Fupstream%2Fv8.git Better tests for the Parser / Preparser unification. We need a way to assert that for a given source code snippet, an error *is* produced or *is not* produced. Otherwise we might accidentally create new errors or start accepting code which was previously not accepted. Just checking that Parser and PreParser produce the same result doesn't cut it. BUG=3126 LOG=N R=ulan@chromium.org Review URL: https://codereview.chromium.org/154243005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19107 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index bdf99b57d..303da3bc0 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -1099,6 +1099,13 @@ enum ParserFlag { }; +enum ParserSyncTestResult { + kSuccessOrError, + kSuccess, + kError +}; + + void SetParserFlags(i::ParserBase* parser, i::EnumSet flags) { parser->set_allow_lazy(flags.Contains(kAllowLazy)); parser->set_allow_natives_syntax(flags.Contains(kAllowNativesSyntax)); @@ -1112,7 +1119,8 @@ void SetParserFlags(i::ParserBase* parser, i::EnumSet flags) { void TestParserSyncWithFlags(i::Handle source, - i::EnumSet flags) { + i::EnumSet flags, + ParserSyncTestResult result) { i::Isolate* isolate = CcTest::i_isolate(); i::Factory* factory = isolate->factory(); @@ -1154,6 +1162,17 @@ void TestParserSyncWithFlags(i::Handle source, i::Handle message_string = i::Handle::cast(i::GetProperty(exception_handle, "message")); + if (result == kSuccess) { + i::OS::Print( + "Parser failed on:\n" + "\t%s\n" + "with error:\n" + "\t%s\n" + "However, we expected no error.", + source->ToCString().get(), message_string->ToCString().get()); + CHECK(false); + } + if (!data.has_error()) { i::OS::Print( "Parser failed on:\n" @@ -1187,13 +1206,21 @@ void TestParserSyncWithFlags(i::Handle source, "However, the parser succeeded", source->ToCString().get(), FormatMessage(&data)->ToCString().get()); CHECK(false); + } else if (result == kError) { + i::OS::Print( + "Expected error on:\n" + "\t%s\n" + "However, parser and preparser succeeded", + source->ToCString().get()); + CHECK(false); } } void TestParserSync(const char* source, const ParserFlag* flag_list, - size_t flag_list_length) { + size_t flag_list_length, + ParserSyncTestResult result = kSuccessOrError) { i::Handle str = CcTest::i_isolate()->factory()->NewStringFromAscii(i::CStrVector(source)); for (int bits = 0; bits < (1 << flag_list_length); bits++) { @@ -1201,7 +1228,7 @@ void TestParserSync(const char* source, for (size_t flag_index = 0; flag_index < flag_list_length; flag_index++) { if ((bits & (1 << flag_index)) != 0) flags.Add(flag_list[flag_index]); } - TestParserSyncWithFlags(str, flags); + TestParserSyncWithFlags(str, flags, result); } } @@ -1347,15 +1374,52 @@ TEST(PreparserStrictOctal) { } +void RunParserSyncTest(const char* context_data[][2], + const char* statement_data[], + ParserSyncTestResult result) { + v8::HandleScope handles(CcTest::isolate()); + v8::Handle context = v8::Context::New(CcTest::isolate()); + v8::Context::Scope context_scope(context); + + int marker; + CcTest::i_isolate()->stack_guard()->SetStackLimit( + reinterpret_cast(&marker) - 128 * 1024); + + static const ParserFlag flags[] = { + kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators, + kAllowForOf + }; + for (int i = 0; context_data[i][0] != NULL; ++i) { + for (int j = 0; statement_data[j] != NULL; ++j) { + int kPrefixLen = i::StrLength(context_data[i][0]); + int kStatementLen = i::StrLength(statement_data[j]); + int kSuffixLen = i::StrLength(context_data[i][1]); + int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen; + + // Plug the source code pieces together. + i::ScopedVector program(kProgramSize + 1); + int length = i::OS::SNPrintF(program, + "%s%s%s", + context_data[i][0], + statement_data[j], + context_data[i][1]); + CHECK(length == kProgramSize); + TestParserSync(program.start(), + flags, + ARRAY_SIZE(flags), + result); + } + } +} + + TEST(ErrorsEvalAndArguments) { // Tests that both preparsing and parsing produce the right kind of errors for // using "eval" and "arguments" as identifiers. Without the strict mode, it's // ok to use "eval" or "arguments" as identifiers. With the strict mode, it // isn't. const char* context_data[][2] = { - { "", "" }, - { "\"use strict\";", "}" }, - { "var eval; function test_func() {", "}"}, + { "\"use strict\";", "" }, { "var eval; function test_func() {\"use strict\"; ", "}"}, { NULL, NULL } }; @@ -1384,36 +1448,67 @@ TEST(ErrorsEvalAndArguments) { NULL }; - v8::HandleScope handles(CcTest::isolate()); - v8::Handle context = v8::Context::New(CcTest::isolate()); - v8::Context::Scope context_scope(context); + RunParserSyncTest(context_data, statement_data, kError); +} - int marker; - CcTest::i_isolate()->stack_guard()->SetStackLimit( - reinterpret_cast(&marker) - 128 * 1024); - static const ParserFlag flags[] = { - kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators, - kAllowForOf +TEST(NoErrorsEvalAndArgumentsClassic) { + // Tests that both preparsing and parsing accept "eval" and "arguments" as + // identifiers when needed. + const char* context_data[][2] = { + { "", "" }, + { "function test_func() {", "}"}, + { NULL, NULL } }; - for (int i = 0; context_data[i][0] != NULL; ++i) { - for (int j = 0; statement_data[j] != NULL; ++j) { - int kPrefixLen = i::StrLength(context_data[i][0]); - int kStatementLen = i::StrLength(statement_data[j]); - int kSuffixLen = i::StrLength(context_data[i][1]); - int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen; - // Plug the source code pieces together. - i::ScopedVector program(kProgramSize + 1); - int length = i::OS::SNPrintF(program, - "%s%s%s", - context_data[i][0], - statement_data[j], - context_data[i][1]); - CHECK(length == kProgramSize); - TestParserSync(program.start(), flags, ARRAY_SIZE(flags)); - } - } + const char* statement_data[] = { + "var eval;", + "var arguments", + "var foo, eval;", + "var foo, arguments;", + "try { } catch (eval) { }", + "try { } catch (arguments) { }", + "function eval() { }", + "function arguments() { }", + "function foo(eval) { }", + "function foo(arguments) { }", + "function foo(bar, eval) { }", + "function foo(bar, arguments) { }", + "eval = 1;", + "arguments = 1;", + "var foo = eval = 1;", + "var foo = arguments = 1;", + "++eval;", + "++arguments;", + "eval++;", + "arguments++;", + NULL + }; + + RunParserSyncTest(context_data, statement_data, kSuccess); +} + + +TEST(NoErrorsEvalAndArgumentsStrict) { + const char* context_data[][2] = { + { "\"use strict\";", "" }, + { "function test_func() { \"use strict\";", "}" }, + { NULL, NULL } + }; + + const char* statement_data[] = { + "eval;", + "arguments;", + "var foo = eval;", + "var foo = arguments;", + "var foo = { eval: 1 };", + "var foo = { arguments: 1 };", + "var foo = { }; foo.eval = {};", + "var foo = { }; foo.arguments = {};", + NULL + }; + + RunParserSyncTest(context_data, statement_data, kSuccess); } @@ -1423,10 +1518,8 @@ TEST(ErrorsFutureStrictReservedWords) { // it's ok to use future strict reserved words as identifiers. With the strict // mode, it isn't. const char* context_data[][2] = { - { "", "" }, - { "\"use strict\";", "}" }, - { "var eval; function test_func() {", "}"}, - { "var eval; function test_func() {\"use strict\"; ", "}"}, + { "\"use strict\";", "" }, + { "function test_func() {\"use strict\"; ", "}"}, { NULL, NULL } }; @@ -1444,36 +1537,32 @@ TEST(ErrorsFutureStrictReservedWords) { NULL }; - v8::HandleScope handles(CcTest::isolate()); - v8::Handle context = v8::Context::New(CcTest::isolate()); - v8::Context::Scope context_scope(context); + RunParserSyncTest(context_data, statement_data, kError); +} - int marker; - CcTest::i_isolate()->stack_guard()->SetStackLimit( - reinterpret_cast(&marker) - 128 * 1024); - static const ParserFlag flags[] = { - kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators, - kAllowForOf +TEST(NoErrorsFutureStrictReservedWords) { + const char* context_data[][2] = { + { "", "" }, + { "function test_func() {", "}"}, + { NULL, NULL } }; - for (int i = 0; context_data[i][0] != NULL; ++i) { - for (int j = 0; statement_data[j] != NULL; ++j) { - int kPrefixLen = i::StrLength(context_data[i][0]); - int kStatementLen = i::StrLength(statement_data[j]); - int kSuffixLen = i::StrLength(context_data[i][1]); - int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen; - // Plug the source code pieces together. - i::ScopedVector program(kProgramSize + 1); - int length = i::OS::SNPrintF(program, - "%s%s%s", - context_data[i][0], - statement_data[j], - context_data[i][1]); - CHECK(length == kProgramSize); - TestParserSync(program.start(), flags, ARRAY_SIZE(flags)); - } - } + const char* statement_data[] = { + "var interface;", + "var foo, interface;", + "try { } catch (interface) { }", + "function interface() { }", + "function foo(interface) { }", + "function foo(bar, interface) { }", + "interface = 1;", + "var foo = interface = 1;", + "++interface;", + "interface++;", + NULL + }; + + RunParserSyncTest(context_data, statement_data, kSuccess); } @@ -1483,7 +1572,7 @@ TEST(ErrorsReservedWords) { // strict mode. const char* context_data[][2] = { { "", "" }, - { "\"use strict\";", "}" }, + { "\"use strict\";", "" }, { "var eval; function test_func() {", "}"}, { "var eval; function test_func() {\"use strict\"; ", "}"}, { NULL, NULL } @@ -1504,53 +1593,70 @@ TEST(ErrorsReservedWords) { NULL }; - v8::HandleScope handles(CcTest::isolate()); - v8::Handle context = v8::Context::New(CcTest::isolate()); - v8::Context::Scope context_scope(context); + RunParserSyncTest(context_data, statement_data, kError); +} - int marker; - CcTest::i_isolate()->stack_guard()->SetStackLimit( - reinterpret_cast(&marker) - 128 * 1024); - static const ParserFlag flags[] = { - kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators, - kAllowForOf +TEST(NoErrorsYieldClassic) { + // In classic mode, it's okay to use "yield" as identifier, *except* inside a + // generator (see next test). + const char* context_data[][2] = { + { "", "" }, + { "function is_not_gen() {", "}" }, + { NULL, NULL } }; - for (int i = 0; context_data[i][0] != NULL; ++i) { - for (int j = 0; statement_data[j] != NULL; ++j) { - int kPrefixLen = i::StrLength(context_data[i][0]); - int kStatementLen = i::StrLength(statement_data[j]); - int kSuffixLen = i::StrLength(context_data[i][1]); - int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen; - // Plug the source code pieces together. - i::ScopedVector program(kProgramSize + 1); - int length = i::OS::SNPrintF(program, - "%s%s%s", - context_data[i][0], - statement_data[j], - context_data[i][1]); - CHECK(length == kProgramSize); - TestParserSync(program.start(), flags, ARRAY_SIZE(flags)); - } - } + const char* statement_data[] = { + "var yield;", + "var foo, yield;", + "try { } catch (yield) { }", + "function yield() { }", + "function foo(yield) { }", + "function foo(bar, yield) { }", + "yield = 1;", + "var foo = yield = 1;", + "++yield;", + "yield++;", + NULL + }; + + RunParserSyncTest(context_data, statement_data, kSuccess); } -TEST(ErrorsYield) { - // Tests that both preparsing and parsing produce the right kind of errors for - // using yield as identifier. In non-strict mode, it's okay to use "yield" as - // an identifier, *except* inside a generator function. In strict mode, it's - // never okay. +TEST(ErrorsYieldClassicGenerator) { const char* context_data[][2] = { - { "", "" }, - { "\"use strict\";", "}" }, - { "var eval; function test_func() {", "}"}, - { "var eval; function test_func() {\"use strict\"; ", "}"}, - { "function is_not_gen() {", "}" }, { "function * is_gen() {", "}" }, + { NULL, NULL } + }; + + const char* statement_data[] = { + "var yield;", + "var foo, yield;", + "try { } catch (yield) { }", + "function yield() { }", + // BUG: These should not be allowed, but they are (if kAllowGenerators is + // set) + // "function foo(yield) { }", + // "function foo(bar, yield) { }", + "yield = 1;", + "var foo = yield = 1;", + "++yield;", + "yield++;", + NULL + }; + + // If generators are not allowed, the error will be produced at the '*' token, + // so this test works both with and without the kAllowGenerators flag. + RunParserSyncTest(context_data, statement_data, kError); +} + + +TEST(ErrorsYieldStrict) { + const char* context_data[][2] = { + { "\"use strict\";", "" }, { "\"use strict\"; function is_not_gen() {", "}" }, - { "\"use strict\"; function * is_gen() {", "}" }, + { "function test_func() {\"use strict\"; ", "}"}, { NULL, NULL } }; @@ -1565,126 +1671,127 @@ TEST(ErrorsYield) { "var foo = yield = 1;", "++yield;", "yield++;", - "yield 2;", // this is legal inside generator - "yield * 2;", // this is legal inside generator NULL }; - v8::HandleScope handles(CcTest::isolate()); - v8::Handle context = v8::Context::New(CcTest::isolate()); - v8::Context::Scope context_scope(context); + RunParserSyncTest(context_data, statement_data, kError); +} - int marker; - CcTest::i_isolate()->stack_guard()->SetStackLimit( - reinterpret_cast(&marker) - 128 * 1024); - static const ParserFlag flags[] = { - kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators, - kAllowForOf +TEST(ErrorsYield) { + const char* context_data[][2] = { + { "function * is_gen() {", "}" }, + { NULL, NULL } }; - for (int i = 0; context_data[i][0] != NULL; ++i) { - for (int j = 0; statement_data[j] != NULL; ++j) { - int kPrefixLen = i::StrLength(context_data[i][0]); - int kStatementLen = i::StrLength(statement_data[j]); - int kSuffixLen = i::StrLength(context_data[i][1]); - int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen; - // Plug the source code pieces together. - i::ScopedVector program(kProgramSize + 1); - int length = i::OS::SNPrintF(program, - "%s%s%s", - context_data[i][0], - statement_data[j], - context_data[i][1]); - CHECK(length == kProgramSize); - TestParserSync(program.start(), flags, ARRAY_SIZE(flags)); - } - } + const char* statement_data[] = { + "yield 2;", // this is legal inside generator + "yield * 2;", // this is legal inside generator + NULL + }; + + // Here we cannot assert that there is no error, since there will be without + // the kAllowGenerators flag. However, we test that Parser and PreParser + // produce the same errors. + RunParserSyncTest(context_data, statement_data, kSuccessOrError); } TEST(ErrorsNameOfStrictFunction) { // Tests that illegal tokens as names of a strict function produce the correct // errors. + const char* context_data[][2] = { + { "", ""}, + { "\"use strict\";", ""}, + { NULL, NULL } + }; + const char* statement_data[] = { - "function eval() { }", // legal - "function eval() {\"use strict\";}", // illegal - "function arguments() { }", // legal - "function arguments() {\"use strict\";}", // illegal + "function eval() {\"use strict\";}", + "function arguments() {\"use strict\";}", + "function interface() {\"use strict\";}", + "function yield() {\"use strict\";}", // Future reserved words are always illegal - "function super() { }", // illegal - "function super() {\"use strict\";}", // illegal - "function interface() { }", // legal - "function interface() {\"use strict\";}", // illegal - "function yield() { }", // legal - "function yield() {\"use strict\";}", // illegal + "function super() { }", + "function super() {\"use strict\";}", NULL }; - v8::HandleScope handles(CcTest::isolate()); - v8::Handle context = v8::Context::New(CcTest::isolate()); - v8::Context::Scope context_scope(context); + RunParserSyncTest(context_data, statement_data, kError); +} - int marker; - CcTest::i_isolate()->stack_guard()->SetStackLimit( - reinterpret_cast(&marker) - 128 * 1024); - static const ParserFlag flags[] = { - kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators, - kAllowForOf +TEST(NoErrorsNameOfStrictFunction) { + const char* context_data[][2] = { + { "", ""}, + { NULL, NULL } }; - for (int j = 0; statement_data[j] != NULL; ++j) { - TestParserSync(statement_data[j], flags, ARRAY_SIZE(flags)); - } + + const char* statement_data[] = { + "function eval() { }", + "function arguments() { }", + "function interface() { }", + "function yield() { }", + NULL + }; + + RunParserSyncTest(context_data, statement_data, kSuccess); +} + + + +TEST(ErrorsIllegalWordsAsLabelsClassic) { + // Using future reserved words as labels is always an error. + const char* context_data[][2] = { + { "", ""}, + { "function test_func() {", "}" }, + { NULL, NULL } + }; + + const char* statement_data[] = { + "super: while(true) { break super; }", + NULL + }; + + RunParserSyncTest(context_data, statement_data, kError); } -TEST(ErrorsIllegalWordsAsLabels) { +TEST(ErrorsIllegalWordsAsLabelsStrict) { // Tests that illegal tokens as labels produce the correct errors. const char* context_data[][2] = { - { "", "" }, - { "\"use strict\";", "}" }, + { "\"use strict\";", "" }, + { "function test_func() {\"use strict\"; ", "}"}, { NULL, NULL } }; const char* statement_data[] = { - "mylabel: while(true) { break mylabel; }", - "eval: while(true) { break eval; }", - "arguments: while(true) { break arguments; }", - "super: while(true) { break super; }", // always illegal + "super: while(true) { break super; }", "interface: while(true) { break interface; }", "yield: while(true) { break yield; }", NULL }; - v8::HandleScope handles(CcTest::isolate()); - v8::Handle context = v8::Context::New(CcTest::isolate()); - v8::Context::Scope context_scope(context); + RunParserSyncTest(context_data, statement_data, kError); +} - int marker; - CcTest::i_isolate()->stack_guard()->SetStackLimit( - reinterpret_cast(&marker) - 128 * 1024); - static const ParserFlag flags[] = { - kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators, - kAllowForOf +TEST(NoErrorsIllegalWordsAsLabels) { + // Using eval and arguments as labels is legal even in strict mode. + const char* context_data[][2] = { + { "", ""}, + { "function test_func() {", "}" }, + { "\"use strict\";", "" }, + { "\"use strict\"; function test_func() {", "}" }, + { NULL, NULL } }; - for (int i = 0; context_data[i][0] != NULL; ++i) { - for (int j = 0; statement_data[j] != NULL; ++j) { - int kPrefixLen = i::StrLength(context_data[i][0]); - int kStatementLen = i::StrLength(statement_data[j]); - int kSuffixLen = i::StrLength(context_data[i][1]); - int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen; - // Plug the source code pieces together. - i::ScopedVector program(kProgramSize + 1); - int length = i::OS::SNPrintF(program, - "%s%s%s", - context_data[i][0], - statement_data[j], - context_data[i][1]); - CHECK(length == kProgramSize); - TestParserSync(program.start(), flags, ARRAY_SIZE(flags)); - } - } + const char* statement_data[] = { + "mylabel: while(true) { break mylabel; }", + "eval: while(true) { break eval; }", + "arguments: while(true) { break arguments; }", + NULL + }; + + RunParserSyncTest(context_data, statement_data, kSuccess); }