CHECK_EQ(11, error_location.end_pos);
// Should not crash.
const char* message = pre_impl->BuildMessage();
- pre_impl->BuildArgs();
+ i::Vector<const char*> args = pre_impl->BuildArgs();
CHECK_GT(strlen(message), 0);
+ args.Dispose();
+ i::DeleteArray(message);
+ delete error_preparse;
}
int kSuffixByteLen = i::StrLength(source_data[i].outer_suffix);
int kProgramSize = kPrefixLen + kInnerLen + kSuffixLen;
int kProgramByteSize = kPrefixByteLen + kInnerByteLen + kSuffixByteLen;
- i::Vector<char> program = i::Vector<char>::New(kProgramByteSize + 1);
+ i::ScopedVector<char> program(kProgramByteSize + 1);
i::OS::SNPrintF(program, "%s%s%s",
source_data[i].outer_prefix,
source_data[i].inner_source,
};
+enum ParserSyncTestResult {
+ kSuccessOrError,
+ kSuccess,
+ kError
+};
+
+
void SetParserFlags(i::ParserBase* parser, i::EnumSet<ParserFlag> flags) {
parser->set_allow_lazy(flags.Contains(kAllowLazy));
parser->set_allow_natives_syntax(flags.Contains(kAllowNativesSyntax));
void TestParserSyncWithFlags(i::Handle<i::String> source,
- i::EnumSet<ParserFlag> flags) {
+ i::EnumSet<ParserFlag> flags,
+ ParserSyncTestResult result) {
i::Isolate* isolate = CcTest::i_isolate();
i::Factory* factory = isolate->factory();
i::Handle<i::String> message_string =
i::Handle<i::String>::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"
"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<i::String> str =
CcTest::i_isolate()->factory()->NewStringFromAscii(i::CStrVector(source));
for (int bits = 0; bits < (1 << flag_list_length); bits++) {
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);
}
}
CHECK_EQ("SyntaxError: Octal literals are not allowed in strict mode.",
*exception);
}
+
+
+void RunParserSyncTest(const char* context_data[][2],
+ const char* statement_data[],
+ ParserSyncTestResult result) {
+ v8::HandleScope handles(CcTest::isolate());
+ v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
+ v8::Context::Scope context_scope(context);
+
+ int marker;
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ reinterpret_cast<uintptr_t>(&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<char> 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\"; ", "}"},
+ { NULL, NULL }
+ };
+
+ 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, kError);
+}
+
+
+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 }
+ };
+
+ 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);
+}
+
+
+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.
+ const char* context_data[][2] = {
+ { "\"use strict\";", "" },
+ { "function test_func() {\"use strict\"; ", "}"},
+ { NULL, NULL }
+ };
+
+ 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, kError);
+}
+
+
+TEST(NoErrorsFutureStrictReservedWords) {
+ const char* context_data[][2] = {
+ { "", "" },
+ { "function test_func() {", "}"},
+ { NULL, NULL }
+ };
+
+ 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);
+}
+
+
+TEST(ErrorsReservedWords) {
+ // Tests that both preparsing and parsing produce the right kind of errors for
+ // using future reserved words as identifiers. These tests don't depend on the
+ // strict mode.
+ const char* context_data[][2] = {
+ { "", "" },
+ { "\"use strict\";", "" },
+ { "var eval; function test_func() {", "}"},
+ { "var eval; function test_func() {\"use strict\"; ", "}"},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "var super;",
+ "var foo, super;",
+ "try { } catch (super) { }",
+ "function super() { }",
+ "function foo(super) { }",
+ "function foo(bar, super) { }",
+ "super = 1;",
+ "var foo = super = 1;",
+ "++super;",
+ "super++;",
+ "function foo super",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+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 }
+ };
+
+ 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(ErrorsYieldClassicGenerator) {
+ const char* context_data[][2] = {
+ { "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() {", "}" },
+ { "function test_func() {\"use strict\"; ", "}"},
+ { NULL, NULL }
+ };
+
+ 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, kError);
+}
+
+
+TEST(ErrorsYield) {
+ const char* context_data[][2] = {
+ { "function * is_gen() {", "}" },
+ { NULL, NULL }
+ };
+
+ 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() {\"use strict\";}",
+ "function arguments() {\"use strict\";}",
+ "function interface() {\"use strict\";}",
+ "function yield() {\"use strict\";}",
+ // Future reserved words are always illegal
+ "function super() { }",
+ "function super() {\"use strict\";}",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(NoErrorsNameOfStrictFunction) {
+ const char* context_data[][2] = {
+ { "", ""},
+ { NULL, NULL }
+ };
+
+ 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(ErrorsIllegalWordsAsLabelsStrict) {
+ // Tests that illegal tokens as labels produce the correct errors.
+ const char* context_data[][2] = {
+ { "\"use strict\";", "" },
+ { "function test_func() {\"use strict\"; ", "}"},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "super: while(true) { break super; }",
+ "interface: while(true) { break interface; }",
+ "yield: while(true) { break yield; }",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+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 }
+ };
+
+ 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);
+}
+
+
+TEST(ErrorsParenthesizedLabels) {
+ // Parenthesized identifiers shouldn't be recognized as labels.
+ const char* context_data[][2] = {
+ { "", ""},
+ { "function test_func() {", "}" },
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "(mylabel): while(true) { break mylabel; }",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(NoErrorsParenthesizedDirectivePrologue) {
+ // Parenthesized directive prologue shouldn't be recognized.
+ const char* context_data[][2] = {
+ { "", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "(\"use strict\"); var eval;",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(ErrorsNotAnIdentifierName) {
+ const char* context_data[][2] = {
+ { "", ""},
+ { "\"use strict\";", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "var foo = {}; foo.{;",
+ "var foo = {}; foo.};",
+ "var foo = {}; foo.=;",
+ "var foo = {}; foo.888;",
+ "var foo = {}; foo.-;",
+ "var foo = {}; foo.--;",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(NoErrorsIdentifierNames) {
+ // Keywords etc. are valid as property names.
+ const char* context_data[][2] = {
+ { "", ""},
+ { "\"use strict\";", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "var foo = {}; foo.if;",
+ "var foo = {}; foo.yield;",
+ "var foo = {}; foo.super;",
+ "var foo = {}; foo.interface;",
+ "var foo = {}; foo.eval;",
+ "var foo = {}; foo.arguments;",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(DontRegressPreParserDataSizes) {
+ // These tests make sure that PreParser doesn't start producing less data.
+
+ v8::V8::Initialize();
+
+ int marker;
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
+
+ struct TestCase {
+ const char* program;
+ int symbols;
+ int functions;
+ } test_cases[] = {
+ // Labels, variables and functions are recorded as symbols.
+ {"{label: 42}", 1, 0}, {"{label: 42; label2: 43}", 2, 0},
+ {"var x = 42;", 1, 0}, {"var x = 42, y = 43;", 2, 0},
+ {"function foo() {}", 1, 1}, {"function foo() {} function bar() {}", 2, 2},
+ // Labels, variables and functions insize lazy functions are not recorded.
+ {"function lazy() { var a, b, c; }", 1, 1},
+ {"function lazy() { a: 1; b: 2; c: 3; }", 1, 1},
+ {"function lazy() { function a() {} function b() {} function c() {} }", 1,
+ 1},
+ {NULL, 0, 0}
+ };
+ // Each function adds 5 elements to the preparse function data.
+ const int kDataPerFunction = 5;
+
+ uintptr_t stack_limit = CcTest::i_isolate()->stack_guard()->real_climit();
+ for (int i = 0; test_cases[i].program; i++) {
+ const char* program = test_cases[i].program;
+ i::Utf8ToUtf16CharacterStream stream(
+ reinterpret_cast<const i::byte*>(program),
+ static_cast<unsigned>(strlen(program)));
+ i::CompleteParserRecorder log;
+ i::Scanner scanner(CcTest::i_isolate()->unicode_cache());
+ scanner.Initialize(&stream);
+
+ i::PreParser preparser(&scanner, &log, stack_limit);
+ preparser.set_allow_lazy(true);
+ preparser.set_allow_natives_syntax(true);
+ i::PreParser::PreParseResult result = preparser.PreParseProgram();
+ CHECK_EQ(i::PreParser::kPreParseSuccess, result);
+ if (log.symbol_ids() != test_cases[i].symbols) {
+ i::OS::Print(
+ "Expected preparse data for program:\n"
+ "\t%s\n"
+ "to contain %d symbols, however, received %d symbols.\n",
+ program, test_cases[i].symbols, log.symbol_ids());
+ CHECK(false);
+ }
+ if (log.function_position() != test_cases[i].functions * kDataPerFunction) {
+ i::OS::Print(
+ "Expected preparse data for program:\n"
+ "\t%s\n"
+ "to contain %d functions, however, received %d functions.\n",
+ program, test_cases[i].functions,
+ log.function_position() / kDataPerFunction);
+ CHECK(false);
+ }
+ i::ScriptDataImpl data(log.ExtractData());
+ CHECK(!data.has_error());
+ }
+}
+
+
+TEST(FunctionDeclaresItselfStrict) {
+ // Tests that we produce the right kinds of errors when a function declares
+ // itself strict (we cannot produce there errors as soon as we see the
+ // offending identifiers, because we don't know at that point whether the
+ // function is strict or not).
+ const char* context_data[][2] = {
+ {"function eval() {", "}"},
+ {"function arguments() {", "}"},
+ {"function yield() {", "}"},
+ {"function interface() {", "}"},
+ {"function foo(eval) {", "}"},
+ {"function foo(arguments) {", "}"},
+ {"function foo(yield) {", "}"},
+ {"function foo(interface) {", "}"},
+ {"function foo(bar, eval) {", "}"},
+ {"function foo(bar, arguments) {", "}"},
+ {"function foo(bar, yield) {", "}"},
+ {"function foo(bar, interface) {", "}"},
+ {"function foo(bar, bar) {", "}"},
+ { NULL, NULL }
+ };
+
+ const char* strict_statement_data[] = {
+ "\"use strict\";",
+ NULL
+ };
+
+ const char* non_strict_statement_data[] = {
+ ";",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, strict_statement_data, kError);
+ RunParserSyncTest(context_data, non_strict_statement_data, kSuccess);
+}
+
+
+TEST(ErrorsTryWithoutCatchOrFinally) {
+ const char* context_data[][2] = {
+ {"", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "try { }",
+ "try { } foo();",
+ "try { } catch (e) foo();",
+ "try { } catch { }",
+ "try { } finally foo();",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(NoErrorsTryCatchFinally) {
+ const char* context_data[][2] = {
+ {"", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "try { } catch (e) { }",
+ "try { } catch (e) { } finally { }",
+ "try { } finally { }",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}