Upgrade to 3.29
Update V8 to 3.29.88.17 and update makefiles to support building on
all the relevant platforms.
Bug: 17370214
Change-Id: Ia3407c157fd8d72a93e23d8318ccaf6ecf77fa4e
diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc
old mode 100755
new mode 100644
index 6bcae7c..72f2298
--- a/test/cctest/test-parsing.cc
+++ b/test/cctest/test-parsing.cc
@@ -25,21 +25,25 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include "v8.h"
+#include "src/v8.h"
-#include "cctest.h"
-#include "compiler.h"
-#include "execution.h"
-#include "isolate.h"
-#include "parser.h"
-#include "preparser.h"
-#include "scanner-character-streams.h"
-#include "token.h"
-#include "utils.h"
+#include "src/ast-value-factory.h"
+#include "src/compiler.h"
+#include "src/execution.h"
+#include "src/isolate.h"
+#include "src/objects.h"
+#include "src/parser.h"
+#include "src/preparser.h"
+#include "src/rewriter.h"
+#include "src/scanner-character-streams.h"
+#include "src/token.h"
+#include "src/utils.h"
+
+#include "test/cctest/cctest.h"
TEST(ScanKeywords) {
struct KeywordToken {
@@ -68,6 +72,7 @@
// The scanner should parse Harmony keywords for this test.
scanner.SetHarmonyScoping(true);
scanner.SetHarmonyModules(true);
+ scanner.SetHarmonyClasses(true);
scanner.Initialize(&stream);
CHECK_EQ(key_token.token, scanner.Next());
CHECK_EQ(i::Token::EOS, scanner.Next());
@@ -82,8 +87,8 @@
}
// Adding characters will make keyword matching fail.
static const char chars_to_append[] = { 'z', '0', '_' };
- for (int j = 0; j < static_cast<int>(ARRAY_SIZE(chars_to_append)); ++j) {
- memmove(buffer, keyword, length);
+ for (int j = 0; j < static_cast<int>(arraysize(chars_to_append)); ++j) {
+ i::MemMove(buffer, keyword, length);
buffer[length] = chars_to_append[j];
i::Utf8ToUtf16CharacterStream stream(buffer, length + 1);
i::Scanner scanner(&unicode_cache);
@@ -93,7 +98,7 @@
}
// Replacing characters will make keyword matching fail.
{
- memmove(buffer, keyword, length);
+ i::MemMove(buffer, keyword, length);
buffer[length - 1] = '_';
i::Utf8ToUtf16CharacterStream stream(buffer, length);
i::Scanner scanner(&unicode_cache);
@@ -107,6 +112,8 @@
TEST(ScanHTMLEndComments) {
v8::V8::Initialize();
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handles(isolate);
// Regression test. See:
// http://code.google.com/p/chromium/issues/detail?id=53548
@@ -138,27 +145,41 @@
};
// Parser/Scanner needs a stack limit.
- int marker;
- i::Isolate::Current()->stack_guard()->SetStackLimit(
- reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
-
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ i::GetCurrentStackPosition() - 128 * 1024);
+ uintptr_t stack_limit = CcTest::i_isolate()->stack_guard()->real_climit();
for (int i = 0; tests[i]; i++) {
- v8::ScriptData* data =
- v8::ScriptData::PreCompile(tests[i], i::StrLength(tests[i]));
- CHECK(data != NULL && !data->HasError());
- delete data;
+ const i::byte* source =
+ reinterpret_cast<const i::byte*>(tests[i]);
+ i::Utf8ToUtf16CharacterStream stream(source, i::StrLength(tests[i]));
+ 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);
+ i::PreParser::PreParseResult result = preparser.PreParseProgram();
+ CHECK_EQ(i::PreParser::kPreParseSuccess, result);
+ CHECK(!log.HasError());
}
for (int i = 0; fail_tests[i]; i++) {
- v8::ScriptData* data =
- v8::ScriptData::PreCompile(fail_tests[i], i::StrLength(fail_tests[i]));
- CHECK(data == NULL || data->HasError());
- delete data;
+ const i::byte* source =
+ reinterpret_cast<const i::byte*>(fail_tests[i]);
+ i::Utf8ToUtf16CharacterStream stream(source, i::StrLength(fail_tests[i]));
+ 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);
+ i::PreParser::PreParseResult result = preparser.PreParseProgram();
+ // Even in the case of a syntax error, kPreParseSuccess is returned.
+ CHECK_EQ(i::PreParser::kPreParseSuccess, result);
+ CHECK(log.HasError());
}
}
-class ScriptResource : public v8::String::ExternalAsciiStringResource {
+class ScriptResource : public v8::String::ExternalOneByteStringResource {
public:
ScriptResource(const char* data, size_t length)
: data_(data), length_(length) { }
@@ -172,13 +193,13 @@
};
-TEST(Preparsing) {
- v8::HandleScope handles;
- v8::Persistent<v8::Context> context = v8::Context::New();
+TEST(UsingCachedData) {
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handles(isolate);
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
- int marker;
- i::Isolate::Current()->stack_guard()->SetStackLimit(
- reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ i::GetCurrentStackPosition() - 128 * 1024);
// Source containing functions that might be lazily compiled and all types
// of symbols (string, propertyName, regexp).
@@ -192,56 +213,87 @@
"var v = /RegExp Literal/;"
"var w = /RegExp Literal\\u0020With Escape/gin;"
"var y = { get getter() { return 42; }, "
- " set setter(v) { this.value = v; }};";
+ " set setter(v) { this.value = v; }};"
+ "var f = a => function (b) { return a + b; };"
+ "var g = a => b => a + b;";
int source_length = i::StrLength(source);
- const char* error_source = "var x = y z;";
- int error_source_length = i::StrLength(error_source);
- v8::ScriptData* preparse =
- v8::ScriptData::PreCompile(source, source_length);
- CHECK(!preparse->HasError());
+ // ScriptResource will be deleted when the corresponding String is GCd.
+ v8::ScriptCompiler::Source script_source(v8::String::NewExternal(
+ isolate, new ScriptResource(source, source_length)));
+ i::FLAG_harmony_arrow_functions = true;
+ i::FLAG_min_preparse_length = 0;
+ v8::ScriptCompiler::Compile(isolate, &script_source,
+ v8::ScriptCompiler::kProduceParserCache);
+ CHECK(script_source.GetCachedData());
+
+ // Compile the script again, using the cached data.
bool lazy_flag = i::FLAG_lazy;
- {
- i::FLAG_lazy = true;
- ScriptResource* resource = new ScriptResource(source, source_length);
- v8::Local<v8::String> script_source = v8::String::NewExternal(resource);
- v8::Script::Compile(script_source, NULL, preparse);
- }
-
- {
- i::FLAG_lazy = false;
-
- ScriptResource* resource = new ScriptResource(source, source_length);
- v8::Local<v8::String> script_source = v8::String::NewExternal(resource);
- v8::Script::New(script_source, NULL, preparse, v8::Local<v8::String>());
- }
- delete preparse;
+ i::FLAG_lazy = true;
+ v8::ScriptCompiler::Compile(isolate, &script_source,
+ v8::ScriptCompiler::kConsumeParserCache);
+ i::FLAG_lazy = false;
+ v8::ScriptCompiler::CompileUnbound(isolate, &script_source,
+ v8::ScriptCompiler::kConsumeParserCache);
i::FLAG_lazy = lazy_flag;
+}
- // Syntax error.
- v8::ScriptData* error_preparse =
- v8::ScriptData::PreCompile(error_source, error_source_length);
- CHECK(error_preparse->HasError());
- i::ScriptDataImpl *pre_impl =
- reinterpret_cast<i::ScriptDataImpl*>(error_preparse);
- i::Scanner::Location error_location =
- pre_impl->MessageLocation();
- // Error is at "z" in source, location 10..11.
- CHECK_EQ(10, error_location.beg_pos);
- CHECK_EQ(11, error_location.end_pos);
- // Should not crash.
- const char* message = pre_impl->BuildMessage();
- pre_impl->BuildArgs();
- CHECK_GT(strlen(message), 0);
+
+TEST(PreparseFunctionDataIsUsed) {
+ // This tests that we actually do use the function data generated by the
+ // preparser.
+
+ // Make preparsing work for short scripts.
+ i::FLAG_min_preparse_length = 0;
+ i::FLAG_harmony_arrow_functions = true;
+
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handles(isolate);
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
+ v8::Context::Scope context_scope(context);
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ i::GetCurrentStackPosition() - 128 * 1024);
+
+ const char* good_code[] = {
+ "function this_is_lazy() { var a; } function foo() { return 25; } foo();",
+ "var this_is_lazy = () => { var a; }; var foo = () => 25; foo();",
+ };
+
+ // Insert a syntax error inside the lazy function.
+ const char* bad_code[] = {
+ "function this_is_lazy() { if ( } function foo() { return 25; } foo();",
+ "var this_is_lazy = () => { if ( }; var foo = () => 25; foo();",
+ };
+
+ for (unsigned i = 0; i < arraysize(good_code); i++) {
+ v8::ScriptCompiler::Source good_source(v8_str(good_code[i]));
+ v8::ScriptCompiler::Compile(isolate, &good_source,
+ v8::ScriptCompiler::kProduceDataToCache);
+
+ const v8::ScriptCompiler::CachedData* cached_data =
+ good_source.GetCachedData();
+ CHECK(cached_data->data != NULL);
+ CHECK_GT(cached_data->length, 0);
+
+ // Now compile the erroneous code with the good preparse data. If the
+ // preparse data is used, the lazy function is skipped and it should
+ // compile fine.
+ v8::ScriptCompiler::Source bad_source(
+ v8_str(bad_code[i]), new v8::ScriptCompiler::CachedData(
+ cached_data->data, cached_data->length));
+ v8::Local<v8::Value> result =
+ v8::ScriptCompiler::Compile(isolate, &bad_source)->Run();
+ CHECK(result->IsInt32());
+ CHECK_EQ(25, result->Int32Value());
+ }
}
TEST(StandAlonePreParser) {
v8::V8::Initialize();
- int marker;
- i::Isolate::Current()->stack_guard()->SetStackLimit(
- reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ i::GetCurrentStackPosition() - 128 * 1024);
const char* programs[] = {
"{label: 42}",
@@ -249,28 +301,27 @@
"function foo(x, y) { return x + y; }",
"%ArgleBargle(glop);",
"var x = new new Function('this.x = 42');",
+ "var f = (x, y) => x + y;",
NULL
};
- uintptr_t stack_limit = i::Isolate::Current()->stack_guard()->real_climit();
+ uintptr_t stack_limit = CcTest::i_isolate()->stack_guard()->real_climit();
for (int i = 0; programs[i]; i++) {
const char* program = programs[i];
i::Utf8ToUtf16CharacterStream stream(
reinterpret_cast<const i::byte*>(program),
static_cast<unsigned>(strlen(program)));
i::CompleteParserRecorder log;
- i::Scanner scanner(i::Isolate::Current()->unicode_cache());
+ i::Scanner scanner(CcTest::i_isolate()->unicode_cache());
scanner.Initialize(&stream);
- int flags = i::kAllowLazy | i::kAllowNativesSyntax;
- v8::preparser::PreParser::PreParseResult result =
- v8::preparser::PreParser::PreParseProgram(&scanner,
- &log,
- flags,
- stack_limit);
- CHECK_EQ(v8::preparser::PreParser::kPreParseSuccess, result);
- i::ScriptDataImpl data(log.ExtractData());
- CHECK(!data.has_error());
+ i::PreParser preparser(&scanner, &log, stack_limit);
+ preparser.set_allow_lazy(true);
+ preparser.set_allow_natives_syntax(true);
+ preparser.set_allow_arrow_functions(true);
+ i::PreParser::PreParseResult result = preparser.PreParseProgram();
+ CHECK_EQ(i::PreParser::kPreParseSuccess, result);
+ CHECK(!log.HasError());
}
}
@@ -278,9 +329,8 @@
TEST(StandAlonePreParserNoNatives) {
v8::V8::Initialize();
- int marker;
- i::Isolate::Current()->stack_guard()->SetStackLimit(
- reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ i::GetCurrentStackPosition() - 128 * 1024);
const char* programs[] = {
"%ArgleBargle(glop);",
@@ -288,36 +338,68 @@
NULL
};
- uintptr_t stack_limit = i::Isolate::Current()->stack_guard()->real_climit();
+ uintptr_t stack_limit = CcTest::i_isolate()->stack_guard()->real_climit();
for (int i = 0; programs[i]; i++) {
const char* program = programs[i];
i::Utf8ToUtf16CharacterStream stream(
reinterpret_cast<const i::byte*>(program),
static_cast<unsigned>(strlen(program)));
i::CompleteParserRecorder log;
- i::Scanner scanner(i::Isolate::Current()->unicode_cache());
+ i::Scanner scanner(CcTest::i_isolate()->unicode_cache());
scanner.Initialize(&stream);
- // Flags don't allow natives syntax.
- v8::preparser::PreParser::PreParseResult result =
- v8::preparser::PreParser::PreParseProgram(&scanner,
- &log,
- i::kAllowLazy,
- stack_limit);
- CHECK_EQ(v8::preparser::PreParser::kPreParseSuccess, result);
- i::ScriptDataImpl data(log.ExtractData());
- // Data contains syntax error.
- CHECK(data.has_error());
+ // Preparser defaults to disallowing natives syntax.
+ i::PreParser preparser(&scanner, &log, stack_limit);
+ preparser.set_allow_lazy(true);
+ i::PreParser::PreParseResult result = preparser.PreParseProgram();
+ CHECK_EQ(i::PreParser::kPreParseSuccess, result);
+ CHECK(log.HasError());
+ }
+}
+
+
+TEST(PreparsingObjectLiterals) {
+ // Regression test for a bug where the symbol stream produced by PreParser
+ // didn't match what Parser wanted to consume.
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handles(isolate);
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
+ v8::Context::Scope context_scope(context);
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ i::GetCurrentStackPosition() - 128 * 1024);
+
+ {
+ const char* source = "var myo = {if: \"foo\"}; myo.if;";
+ v8::Local<v8::Value> result = ParserCacheCompileRun(source);
+ CHECK(result->IsString());
+ v8::String::Utf8Value utf8(result);
+ CHECK_EQ("foo", *utf8);
+ }
+
+ {
+ const char* source = "var myo = {\"bar\": \"foo\"}; myo[\"bar\"];";
+ v8::Local<v8::Value> result = ParserCacheCompileRun(source);
+ CHECK(result->IsString());
+ v8::String::Utf8Value utf8(result);
+ CHECK_EQ("foo", *utf8);
+ }
+
+ {
+ const char* source = "var myo = {1: \"foo\"}; myo[1];";
+ v8::Local<v8::Value> result = ParserCacheCompileRun(source);
+ CHECK(result->IsString());
+ v8::String::Utf8Value utf8(result);
+ CHECK_EQ("foo", *utf8);
}
}
TEST(RegressChromium62639) {
v8::V8::Initialize();
+ i::Isolate* isolate = CcTest::i_isolate();
- int marker;
- i::Isolate::Current()->stack_guard()->SetStackLimit(
- reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
+ isolate->stack_guard()->SetStackLimit(i::GetCurrentStackPosition() -
+ 128 * 1024);
const char* program = "var x = 'something';\n"
"escape: function() {}";
@@ -329,41 +411,55 @@
i::Utf8ToUtf16CharacterStream stream(
reinterpret_cast<const i::byte*>(program),
static_cast<unsigned>(strlen(program)));
- i::ScriptDataImpl* data =
- i::ParserApi::PreParse(&stream, NULL, false);
- CHECK(data->HasError());
- delete data;
+ i::CompleteParserRecorder log;
+ i::Scanner scanner(CcTest::i_isolate()->unicode_cache());
+ scanner.Initialize(&stream);
+ i::PreParser preparser(&scanner, &log,
+ CcTest::i_isolate()->stack_guard()->real_climit());
+ preparser.set_allow_lazy(true);
+ i::PreParser::PreParseResult result = preparser.PreParseProgram();
+ // Even in the case of a syntax error, kPreParseSuccess is returned.
+ CHECK_EQ(i::PreParser::kPreParseSuccess, result);
+ CHECK(log.HasError());
}
TEST(Regress928) {
v8::V8::Initialize();
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
// Preparsing didn't consider the catch clause of a try statement
// as with-content, which made it assume that a function inside
// the block could be lazily compiled, and an extra, unexpected,
// entry was added to the data.
- int marker;
- i::Isolate::Current()->stack_guard()->SetStackLimit(
- reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
+ isolate->stack_guard()->SetStackLimit(i::GetCurrentStackPosition() -
+ 128 * 1024);
const char* program =
"try { } catch (e) { var foo = function () { /* first */ } }"
"var bar = function () { /* second */ }";
- v8::HandleScope handles;
- i::Handle<i::String> source(
- FACTORY->NewStringFromAscii(i::CStrVector(program)));
- i::ScriptDataImpl* data = i::ParserApi::PartialPreParse(source, NULL, false);
- CHECK(!data->HasError());
-
- data->Initialize();
+ v8::HandleScope handles(CcTest::isolate());
+ i::Handle<i::String> source = factory->NewStringFromAsciiChecked(program);
+ i::GenericStringUtf16CharacterStream stream(source, 0, source->length());
+ i::CompleteParserRecorder log;
+ i::Scanner scanner(CcTest::i_isolate()->unicode_cache());
+ scanner.Initialize(&stream);
+ i::PreParser preparser(&scanner, &log,
+ CcTest::i_isolate()->stack_guard()->real_climit());
+ preparser.set_allow_lazy(true);
+ i::PreParser::PreParseResult result = preparser.PreParseProgram();
+ CHECK_EQ(i::PreParser::kPreParseSuccess, result);
+ i::ScriptData* sd = log.GetScriptData();
+ i::ParseData pd(sd);
+ pd.Initialize();
int first_function =
static_cast<int>(strstr(program, "function") - program);
int first_lbrace = first_function + i::StrLength("function () ");
CHECK_EQ('{', program[first_lbrace]);
- i::FunctionEntry entry1 = data->GetFunctionEntry(first_lbrace);
+ i::FunctionEntry entry1 = pd.GetFunctionEntry(first_lbrace);
CHECK(!entry1.is_valid());
int second_function =
@@ -371,42 +467,38 @@
int second_lbrace =
second_function + i::StrLength("function () ");
CHECK_EQ('{', program[second_lbrace]);
- i::FunctionEntry entry2 = data->GetFunctionEntry(second_lbrace);
+ i::FunctionEntry entry2 = pd.GetFunctionEntry(second_lbrace);
CHECK(entry2.is_valid());
CHECK_EQ('}', program[entry2.end_pos() - 1]);
- delete data;
+ delete sd;
}
TEST(PreParseOverflow) {
v8::V8::Initialize();
- int marker;
- i::Isolate::Current()->stack_guard()->SetStackLimit(
- reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ i::GetCurrentStackPosition() - 128 * 1024);
size_t kProgramSize = 1024 * 1024;
- i::SmartArrayPointer<char> program(
- reinterpret_cast<char*>(malloc(kProgramSize + 1)));
- memset(*program, '(', kProgramSize);
+ i::SmartArrayPointer<char> program(i::NewArray<char>(kProgramSize + 1));
+ memset(program.get(), '(', kProgramSize);
program[kProgramSize] = '\0';
- uintptr_t stack_limit = i::Isolate::Current()->stack_guard()->real_climit();
+ uintptr_t stack_limit = CcTest::i_isolate()->stack_guard()->real_climit();
i::Utf8ToUtf16CharacterStream stream(
- reinterpret_cast<const i::byte*>(*program),
+ reinterpret_cast<const i::byte*>(program.get()),
static_cast<unsigned>(kProgramSize));
i::CompleteParserRecorder log;
- i::Scanner scanner(i::Isolate::Current()->unicode_cache());
+ i::Scanner scanner(CcTest::i_isolate()->unicode_cache());
scanner.Initialize(&stream);
-
- v8::preparser::PreParser::PreParseResult result =
- v8::preparser::PreParser::PreParseProgram(&scanner,
- &log,
- true,
- stack_limit);
- CHECK_EQ(v8::preparser::PreParser::kPreParseStackOverflow, result);
+ i::PreParser preparser(&scanner, &log, stack_limit);
+ preparser.set_allow_lazy(true);
+ preparser.set_allow_arrow_functions(true);
+ i::PreParser::PreParseResult result = preparser.PreParseProgram();
+ CHECK_EQ(i::PreParser::kPreParseStackOverflow, result);
}
@@ -432,29 +524,31 @@
#define CHECK_EQU(v1, v2) CHECK_EQ(static_cast<int>(v1), static_cast<int>(v2))
-void TestCharacterStream(const char* ascii_source,
- unsigned length,
- unsigned start = 0,
- unsigned end = 0) {
+void TestCharacterStream(const char* one_byte_source, unsigned length,
+ unsigned start = 0, unsigned end = 0) {
if (end == 0) end = length;
unsigned sub_length = end - start;
- i::HandleScope test_scope;
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+ i::HandleScope test_scope(isolate);
i::SmartArrayPointer<i::uc16> uc16_buffer(new i::uc16[length]);
for (unsigned i = 0; i < length; i++) {
- uc16_buffer[i] = static_cast<i::uc16>(ascii_source[i]);
+ uc16_buffer[i] = static_cast<i::uc16>(one_byte_source[i]);
}
- i::Vector<const char> ascii_vector(ascii_source, static_cast<int>(length));
- i::Handle<i::String> ascii_string(
- FACTORY->NewStringFromAscii(ascii_vector));
- TestExternalResource resource(*uc16_buffer, length);
+ i::Vector<const char> one_byte_vector(one_byte_source,
+ static_cast<int>(length));
+ i::Handle<i::String> one_byte_string =
+ factory->NewStringFromAscii(one_byte_vector).ToHandleChecked();
+ TestExternalResource resource(uc16_buffer.get(), length);
i::Handle<i::String> uc16_string(
- FACTORY->NewExternalStringFromTwoByte(&resource));
+ factory->NewExternalStringFromTwoByte(&resource).ToHandleChecked());
i::ExternalTwoByteStringUtf16CharacterStream uc16_stream(
i::Handle<i::ExternalTwoByteString>::cast(uc16_string), start, end);
- i::GenericStringUtf16CharacterStream string_stream(ascii_string, start, end);
+ i::GenericStringUtf16CharacterStream string_stream(one_byte_string, start,
+ end);
i::Utf8ToUtf16CharacterStream utf8_stream(
- reinterpret_cast<const i::byte*>(ascii_source), end);
+ reinterpret_cast<const i::byte*>(one_byte_source), end);
utf8_stream.SeekForward(start);
unsigned i = start;
@@ -463,7 +557,7 @@
CHECK_EQU(i, uc16_stream.pos());
CHECK_EQU(i, string_stream.pos());
CHECK_EQU(i, utf8_stream.pos());
- int32_t c0 = ascii_source[i];
+ int32_t c0 = one_byte_source[i];
int32_t c1 = uc16_stream.Advance();
int32_t c2 = string_stream.Advance();
int32_t c3 = utf8_stream.Advance();
@@ -477,7 +571,7 @@
}
while (i > start + sub_length / 4) {
// Pushback, re-read, pushback again.
- int32_t c0 = ascii_source[i - 1];
+ int32_t c0 = one_byte_source[i - 1];
CHECK_EQU(i, uc16_stream.pos());
CHECK_EQU(i, string_stream.pos());
CHECK_EQU(i, utf8_stream.pos());
@@ -520,7 +614,7 @@
CHECK_EQU(i, uc16_stream.pos());
CHECK_EQU(i, string_stream.pos());
CHECK_EQU(i, utf8_stream.pos());
- int32_t c0 = ascii_source[i];
+ int32_t c0 = one_byte_source[i];
int32_t c1 = uc16_stream.Advance();
int32_t c2 = string_stream.Advance();
int32_t c3 = utf8_stream.Advance();
@@ -543,8 +637,9 @@
TEST(CharacterStreams) {
- v8::HandleScope handles;
- v8::Persistent<v8::Context> context = v8::Context::New();
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handles(isolate);
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
TestCharacterStream("abc\0\n\r\x7f", 7);
@@ -580,7 +675,7 @@
i,
unibrow::Utf16::kNoPreviousCharacter);
}
- ASSERT(cursor == kAllUtf8CharsSizeU);
+ DCHECK(cursor == kAllUtf8CharsSizeU);
i::Utf8ToUtf16CharacterStream stream(reinterpret_cast<const i::byte*>(buffer),
kAllUtf8CharsSizeU);
@@ -617,7 +712,7 @@
i::Token::Value* expected_tokens,
int skip_pos = 0, // Zero means not skipping.
int skip_to = 0) {
- i::Scanner scanner(i::Isolate::Current()->unicode_cache());
+ i::Scanner scanner(CcTest::i_isolate()->unicode_cache());
scanner.Initialize(stream);
int i = 0;
@@ -632,6 +727,7 @@
} while (expected_tokens[i] != i::Token::ILLEGAL);
}
+
TEST(StreamScanner) {
v8::V8::Initialize();
@@ -668,8 +764,8 @@
i::Token::EOS,
i::Token::ILLEGAL
};
- ASSERT_EQ('{', str2[19]);
- ASSERT_EQ('}', str2[37]);
+ DCHECK_EQ('{', str2[19]);
+ DCHECK_EQ('}', str2[37]);
TestStreamScanner(&stream2, expectations2, 20, 37);
const char* str3 = "{}}}}";
@@ -698,15 +794,24 @@
i::Utf8ToUtf16CharacterStream stream(
reinterpret_cast<const i::byte*>(re_source),
static_cast<unsigned>(strlen(re_source)));
- i::Scanner scanner(i::Isolate::Current()->unicode_cache());
+ i::HandleScope scope(CcTest::i_isolate());
+ i::Scanner scanner(CcTest::i_isolate()->unicode_cache());
scanner.Initialize(&stream);
i::Token::Value start = scanner.peek();
CHECK(start == i::Token::DIV || start == i::Token::ASSIGN_DIV);
CHECK(scanner.ScanRegExpPattern(start == i::Token::ASSIGN_DIV));
scanner.Next(); // Current token is now the regexp literal.
- CHECK(scanner.is_literal_ascii());
- i::Vector<const char> actual = scanner.literal_ascii_string();
+ i::Zone zone(CcTest::i_isolate());
+ i::AstValueFactory ast_value_factory(&zone,
+ CcTest::i_isolate()->heap()->HashSeed());
+ ast_value_factory.Internalize(CcTest::i_isolate());
+ i::Handle<i::String> val =
+ scanner.CurrentSymbol(&ast_value_factory)->string();
+ i::DisallowHeapAllocation no_alloc;
+ i::String::FlatContent content = val->GetFlatContent();
+ CHECK(content.IsOneByte());
+ i::Vector<const uint8_t> actual = content.ToOneByteVector();
for (int i = 0; i < actual.length(); i++) {
CHECK_NE('\0', expected[i]);
CHECK_EQ(expected[i], actual[i]);
@@ -813,6 +918,8 @@
TEST(ScopePositions) {
+ v8::internal::FLAG_harmony_scoping = true;
+
// Test the parser for correctly setting the start and end positions
// of a scope. We check the scope positions of exactly one scope
// nested in the global scope of a program. 'inner source' is the
@@ -824,177 +931,184 @@
const char* inner_source;
const char* outer_suffix;
i::ScopeType scope_type;
- i::LanguageMode language_mode;
+ i::StrictMode strict_mode;
};
const SourceData source_data[] = {
- { " with ({}) ", "{ block; }", " more;", i::WITH_SCOPE, i::CLASSIC_MODE },
- { " with ({}) ", "{ block; }", "; more;", i::WITH_SCOPE, i::CLASSIC_MODE },
+ { " with ({}) ", "{ block; }", " more;", i::WITH_SCOPE, i::SLOPPY },
+ { " with ({}) ", "{ block; }", "; more;", i::WITH_SCOPE, i::SLOPPY },
{ " with ({}) ", "{\n"
" block;\n"
" }", "\n"
- " more;", i::WITH_SCOPE, i::CLASSIC_MODE },
- { " with ({}) ", "statement;", " more;", i::WITH_SCOPE, i::CLASSIC_MODE },
+ " more;", i::WITH_SCOPE, i::SLOPPY },
+ { " with ({}) ", "statement;", " more;", i::WITH_SCOPE, i::SLOPPY },
{ " with ({}) ", "statement", "\n"
- " more;", i::WITH_SCOPE, i::CLASSIC_MODE },
+ " more;", i::WITH_SCOPE, i::SLOPPY },
{ " with ({})\n"
" ", "statement;", "\n"
- " more;", i::WITH_SCOPE, i::CLASSIC_MODE },
+ " more;", i::WITH_SCOPE, i::SLOPPY },
{ " try {} catch ", "(e) { block; }", " more;",
- i::CATCH_SCOPE, i::CLASSIC_MODE },
+ i::CATCH_SCOPE, i::SLOPPY },
{ " try {} catch ", "(e) { block; }", "; more;",
- i::CATCH_SCOPE, i::CLASSIC_MODE },
+ i::CATCH_SCOPE, i::SLOPPY },
{ " try {} catch ", "(e) {\n"
" block;\n"
" }", "\n"
- " more;", i::CATCH_SCOPE, i::CLASSIC_MODE },
+ " more;", i::CATCH_SCOPE, i::SLOPPY },
{ " try {} catch ", "(e) { block; }", " finally { block; } more;",
- i::CATCH_SCOPE, i::CLASSIC_MODE },
+ i::CATCH_SCOPE, i::SLOPPY },
{ " start;\n"
- " ", "{ let block; }", " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ " ", "{ let block; }", " more;", i::BLOCK_SCOPE, i::STRICT },
{ " start;\n"
- " ", "{ let block; }", "; more;", i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ " ", "{ let block; }", "; more;", i::BLOCK_SCOPE, i::STRICT },
{ " start;\n"
" ", "{\n"
" let block;\n"
" }", "\n"
- " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ " more;", i::BLOCK_SCOPE, i::STRICT },
{ " start;\n"
" function fun", "(a,b) { infunction; }", " more;",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
{ " start;\n"
" function fun", "(a,b) {\n"
" infunction;\n"
" }", "\n"
- " more;", i::FUNCTION_SCOPE, i::CLASSIC_MODE },
- { " (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ " more;", i::FUNCTION_SCOPE, i::SLOPPY },
+ // TODO(aperez): Change to use i::ARROW_SCOPE when implemented
+ { " start;\n", "(a,b) => a + b", "; more;",
+ i::FUNCTION_SCOPE, i::SLOPPY },
+ { " start;\n", "(a,b) => { return a+b; }", "\nmore;",
+ i::FUNCTION_SCOPE, i::SLOPPY },
+ { " start;\n"
+ " (function fun", "(a,b) { infunction; }", ")();",
+ i::FUNCTION_SCOPE, i::SLOPPY },
{ " for ", "(let x = 1 ; x < 10; ++ x) { block; }", " more;",
- i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x = 1 ; x < 10; ++ x) { block; }", "; more;",
- i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x = 1 ; x < 10; ++ x) {\n"
" block;\n"
" }", "\n"
- " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ " more;", i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x = 1 ; x < 10; ++ x) statement;", " more;",
- i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x = 1 ; x < 10; ++ x) statement", "\n"
- " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ " more;", i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x = 1 ; x < 10; ++ x)\n"
" statement;", "\n"
- " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ " more;", i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x in {}) { block; }", " more;",
- i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x in {}) { block; }", "; more;",
- i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x in {}) {\n"
" block;\n"
" }", "\n"
- " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ " more;", i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x in {}) statement;", " more;",
- i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x in {}) statement", "\n"
- " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ " more;", i::BLOCK_SCOPE, i::STRICT },
{ " for ", "(let x in {})\n"
" statement;", "\n"
- " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE },
+ " more;", i::BLOCK_SCOPE, i::STRICT },
// Check that 6-byte and 4-byte encodings of UTF-8 strings do not throw
// the preparser off in terms of byte offsets.
// 6 byte encoding.
{ " 'foo\355\240\201\355\260\211';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// 4 byte encoding.
{ " 'foo\360\220\220\212';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// 3 byte encoding of \u0fff.
{ " 'foo\340\277\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Broken 6 byte encoding with missing last byte.
{ " 'foo\355\240\201\355\211';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Broken 3 byte encoding of \u0fff with missing last byte.
{ " 'foo\340\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Broken 3 byte encoding of \u0fff with missing 2 last bytes.
{ " 'foo\340';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Broken 3 byte encoding of \u00ff should be a 2 byte encoding.
{ " 'foo\340\203\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Broken 3 byte encoding of \u007f should be a 2 byte encoding.
{ " 'foo\340\201\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Unpaired lead surrogate.
{ " 'foo\355\240\201';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Unpaired lead surrogate where following code point is a 3 byte sequence.
{ " 'foo\355\240\201\340\277\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Unpaired lead surrogate where following code point is a 4 byte encoding
// of a trail surrogate.
{ " 'foo\355\240\201\360\215\260\211';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Unpaired trail surrogate.
{ " 'foo\355\260\211';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// 2 byte encoding of \u00ff.
{ " 'foo\303\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Broken 2 byte encoding of \u00ff with missing last byte.
{ " 'foo\303';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Broken 2 byte encoding of \u007f should be a 1 byte encoding.
{ " 'foo\301\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Illegal 5 byte encoding.
{ " 'foo\370\277\277\277\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Illegal 6 byte encoding.
{ " 'foo\374\277\277\277\277\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Illegal 0xfe byte
{ " 'foo\376\277\277\277\277\277\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
// Illegal 0xff byte
{ " 'foo\377\277\277\277\277\277\277\277';\n"
" (function fun", "(a,b) { infunction; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
{ " 'foo';\n"
" (function fun", "(a,b) { 'bar\355\240\201\355\260\213'; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
+ i::FUNCTION_SCOPE, i::SLOPPY },
{ " 'foo';\n"
" (function fun", "(a,b) { 'bar\360\220\220\214'; }", ")();",
- i::FUNCTION_SCOPE, i::CLASSIC_MODE },
- { NULL, NULL, NULL, i::EVAL_SCOPE, i::CLASSIC_MODE }
+ i::FUNCTION_SCOPE, i::SLOPPY },
+ { NULL, NULL, NULL, i::EVAL_SCOPE, i::SLOPPY }
};
- v8::HandleScope handles;
- v8::Persistent<v8::Context> context = v8::Context::New();
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+
+ v8::HandleScope handles(CcTest::isolate());
+ v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
v8::Context::Scope context_scope(context);
- int marker;
- i::Isolate::Current()->stack_guard()->SetStackLimit(
- reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
- i::FLAG_harmony_scoping = true;
+ isolate->stack_guard()->SetStackLimit(i::GetCurrentStackPosition() -
+ 128 * 1024);
for (int i = 0; source_data[i].outer_prefix; i++) {
int kPrefixLen = Utf8LengthHelper(source_data[i].outer_prefix);
@@ -1005,33 +1119,39 @@
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::OS::SNPrintF(program, "%s%s%s",
- source_data[i].outer_prefix,
- source_data[i].inner_source,
- source_data[i].outer_suffix);
+ i::ScopedVector<char> program(kProgramByteSize + 1);
+ i::SNPrintF(program, "%s%s%s",
+ source_data[i].outer_prefix,
+ source_data[i].inner_source,
+ source_data[i].outer_suffix);
// Parse program source.
- i::Handle<i::String> source(
- FACTORY->NewStringFromUtf8(i::CStrVector(program.start())));
+ i::Handle<i::String> source = factory->NewStringFromUtf8(
+ i::CStrVector(program.start())).ToHandleChecked();
CHECK_EQ(source->length(), kProgramSize);
- i::Handle<i::Script> script = FACTORY->NewScript(source);
- i::Parser parser(script, i::kAllowLazy | i::EXTENDED_MODE, NULL, NULL);
- i::CompilationInfo info(script);
+ i::Handle<i::Script> script = factory->NewScript(source);
+ i::CompilationInfoWithZone info(script);
+ i::Parser::ParseInfo parse_info = {isolate->stack_guard()->real_climit(),
+ isolate->heap()->HashSeed(),
+ isolate->unicode_cache()};
+ i::Parser parser(&info, &parse_info);
+ parser.set_allow_lazy(true);
+ parser.set_allow_harmony_scoping(true);
+ parser.set_allow_arrow_functions(true);
info.MarkAsGlobal();
- info.SetLanguageMode(source_data[i].language_mode);
- i::FunctionLiteral* function = parser.ParseProgram(&info);
- CHECK(function != NULL);
+ info.SetStrictMode(source_data[i].strict_mode);
+ parser.Parse();
+ CHECK(info.function() != NULL);
// Check scope types and positions.
- i::Scope* scope = function->scope();
+ i::Scope* scope = info.function()->scope();
CHECK(scope->is_global_scope());
CHECK_EQ(scope->start_position(), 0);
CHECK_EQ(scope->end_position(), kProgramSize);
CHECK_EQ(scope->inner_scopes()->length(), 1);
i::Scope* inner_scope = scope->inner_scopes()->at(0);
- CHECK_EQ(inner_scope->type(), source_data[i].scope_type);
+ CHECK_EQ(inner_scope->scope_type(), source_data[i].scope_type);
CHECK_EQ(inner_scope->start_position(), kPrefixLen);
// The end position of a token is one position after the last
// character belonging to that token.
@@ -1040,96 +1160,212 @@
}
-void TestParserSync(i::Handle<i::String> source, int flags) {
- uintptr_t stack_limit = i::Isolate::Current()->stack_guard()->real_climit();
- bool harmony_scoping = ((i::kLanguageModeMask & flags) == i::EXTENDED_MODE);
+const char* ReadString(unsigned* start) {
+ int length = start[0];
+ char* result = i::NewArray<char>(length + 1);
+ for (int i = 0; i < length; i++) {
+ result[i] = start[i + 1];
+ }
+ result[length] = '\0';
+ return result;
+}
+
+
+i::Handle<i::String> FormatMessage(i::Vector<unsigned> data) {
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+ const char* message =
+ ReadString(&data[i::PreparseDataConstants::kMessageTextPos]);
+ i::Handle<i::String> format = v8::Utils::OpenHandle(
+ *v8::String::NewFromUtf8(CcTest::isolate(), message));
+ int arg_count = data[i::PreparseDataConstants::kMessageArgCountPos];
+ const char* arg = NULL;
+ i::Handle<i::JSArray> args_array;
+ if (arg_count == 1) {
+ // Position after text found by skipping past length field and
+ // length field content words.
+ int pos = i::PreparseDataConstants::kMessageTextPos + 1 +
+ data[i::PreparseDataConstants::kMessageTextPos];
+ arg = ReadString(&data[pos]);
+ args_array = factory->NewJSArray(1);
+ i::JSArray::SetElement(args_array, 0, v8::Utils::OpenHandle(*v8_str(arg)),
+ NONE, i::SLOPPY).Check();
+ } else {
+ CHECK_EQ(0, arg_count);
+ args_array = factory->NewJSArray(0);
+ }
+
+ i::Handle<i::JSObject> builtins(isolate->js_builtins_object());
+ i::Handle<i::Object> format_fun = i::Object::GetProperty(
+ isolate, builtins, "FormatMessage").ToHandleChecked();
+ i::Handle<i::Object> arg_handles[] = { format, args_array };
+ i::Handle<i::Object> result = i::Execution::Call(
+ isolate, format_fun, builtins, 2, arg_handles).ToHandleChecked();
+ CHECK(result->IsString());
+ i::DeleteArray(message);
+ i::DeleteArray(arg);
+ data.Dispose();
+ return i::Handle<i::String>::cast(result);
+}
+
+
+enum ParserFlag {
+ kAllowLazy,
+ kAllowNativesSyntax,
+ kAllowHarmonyScoping,
+ kAllowModules,
+ kAllowHarmonyNumericLiterals,
+ kAllowArrowFunctions,
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+};
+
+
+enum ParserSyncTestResult {
+ kSuccessOrError,
+ kSuccess,
+ kError
+};
+
+template <typename Traits>
+void SetParserFlags(i::ParserBase<Traits>* parser,
+ i::EnumSet<ParserFlag> flags) {
+ parser->set_allow_lazy(flags.Contains(kAllowLazy));
+ parser->set_allow_natives_syntax(flags.Contains(kAllowNativesSyntax));
+ parser->set_allow_harmony_scoping(flags.Contains(kAllowHarmonyScoping));
+ parser->set_allow_modules(flags.Contains(kAllowModules));
+ parser->set_allow_harmony_numeric_literals(
+ flags.Contains(kAllowHarmonyNumericLiterals));
+ parser->set_allow_harmony_object_literals(
+ flags.Contains(kAllowHarmonyObjectLiterals));
+ parser->set_allow_arrow_functions(flags.Contains(kAllowArrowFunctions));
+ parser->set_allow_classes(flags.Contains(kAllowClasses));
+}
+
+
+void TestParserSyncWithFlags(i::Handle<i::String> source,
+ i::EnumSet<ParserFlag> flags,
+ ParserSyncTestResult result) {
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+
+ uintptr_t stack_limit = isolate->stack_guard()->real_climit();
// Preparse the data.
i::CompleteParserRecorder log;
- i::Scanner scanner(i::Isolate::Current()->unicode_cache());
- i::GenericStringUtf16CharacterStream stream(source, 0, source->length());
- scanner.SetHarmonyScoping(harmony_scoping);
- scanner.Initialize(&stream);
- v8::preparser::PreParser::PreParseResult result =
- v8::preparser::PreParser::PreParseProgram(
- &scanner, &log, flags, stack_limit);
- CHECK_EQ(v8::preparser::PreParser::kPreParseSuccess, result);
- i::ScriptDataImpl data(log.ExtractData());
+ {
+ i::Scanner scanner(isolate->unicode_cache());
+ i::GenericStringUtf16CharacterStream stream(source, 0, source->length());
+ i::PreParser preparser(&scanner, &log, stack_limit);
+ SetParserFlags(&preparser, flags);
+ scanner.Initialize(&stream);
+ i::PreParser::PreParseResult result = preparser.PreParseProgram();
+ CHECK_EQ(i::PreParser::kPreParseSuccess, result);
+ }
+
+ bool preparse_error = log.HasError();
// Parse the data
- i::Handle<i::Script> script = FACTORY->NewScript(source);
- bool save_harmony_scoping = i::FLAG_harmony_scoping;
- i::FLAG_harmony_scoping = harmony_scoping;
- i::Parser parser(script, flags, NULL, NULL);
- i::CompilationInfo info(script);
- info.MarkAsGlobal();
- i::FunctionLiteral* function = parser.ParseProgram(&info);
- i::FLAG_harmony_scoping = save_harmony_scoping;
-
- i::String* type_string = NULL;
- if (function == NULL) {
- // Extract exception from the parser.
- i::Handle<i::String> type_symbol = FACTORY->LookupAsciiSymbol("type");
- CHECK(i::Isolate::Current()->has_pending_exception());
- i::MaybeObject* maybe_object = i::Isolate::Current()->pending_exception();
- i::JSObject* exception = NULL;
- CHECK(maybe_object->To(&exception));
-
- // Get the type string.
- maybe_object = exception->GetProperty(*type_symbol);
- CHECK(maybe_object->To(&type_string));
+ i::FunctionLiteral* function;
+ {
+ i::Handle<i::Script> script = factory->NewScript(source);
+ i::CompilationInfoWithZone info(script);
+ i::Parser::ParseInfo parse_info = {isolate->stack_guard()->real_climit(),
+ isolate->heap()->HashSeed(),
+ isolate->unicode_cache()};
+ i::Parser parser(&info, &parse_info);
+ SetParserFlags(&parser, flags);
+ info.MarkAsGlobal();
+ parser.Parse();
+ function = info.function();
}
// Check that preparsing fails iff parsing fails.
- if (data.has_error() && function != NULL) {
- i::OS::Print(
- "Preparser failed on:\n"
- "\t%s\n"
- "with error:\n"
- "\t%s\n"
- "However, the parser succeeded",
- *source->ToCString(), data.BuildMessage());
- CHECK(false);
- } else if (!data.has_error() && function == NULL) {
- i::OS::Print(
- "Parser failed on:\n"
- "\t%s\n"
- "with error:\n"
- "\t%s\n"
- "However, the preparser succeeded",
- *source->ToCString(), *type_string->ToCString());
- CHECK(false);
- }
-
- // Check that preparser and parser produce the same error.
if (function == NULL) {
- if (!type_string->IsEqualTo(i::CStrVector(data.BuildMessage()))) {
- i::OS::Print(
+ // Extract exception from the parser.
+ CHECK(isolate->has_pending_exception());
+ i::Handle<i::JSObject> exception_handle(
+ i::JSObject::cast(isolate->pending_exception()));
+ i::Handle<i::String> message_string =
+ i::Handle<i::String>::cast(i::Object::GetProperty(
+ isolate, exception_handle, "message").ToHandleChecked());
+
+ if (result == kSuccess) {
+ v8::base::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 (!preparse_error) {
+ v8::base::OS::Print(
+ "Parser failed on:\n"
+ "\t%s\n"
+ "with error:\n"
+ "\t%s\n"
+ "However, the preparser succeeded",
+ source->ToCString().get(), message_string->ToCString().get());
+ CHECK(false);
+ }
+ // Check that preparser and parser produce the same error.
+ i::Handle<i::String> preparser_message =
+ FormatMessage(log.ErrorMessageData());
+ if (!i::String::Equals(message_string, preparser_message)) {
+ v8::base::OS::Print(
"Expected parser and preparser to produce the same error on:\n"
"\t%s\n"
"However, found the following error messages\n"
"\tparser: %s\n"
"\tpreparser: %s\n",
- *source->ToCString(), *type_string->ToCString(), data.BuildMessage());
+ source->ToCString().get(),
+ message_string->ToCString().get(),
+ preparser_message->ToCString().get());
CHECK(false);
}
+ } else if (preparse_error) {
+ v8::base::OS::Print(
+ "Preparser failed on:\n"
+ "\t%s\n"
+ "with error:\n"
+ "\t%s\n"
+ "However, the parser succeeded",
+ source->ToCString().get(),
+ FormatMessage(log.ErrorMessageData())->ToCString().get());
+ CHECK(false);
+ } else if (result == kError) {
+ v8::base::OS::Print(
+ "Expected error on:\n"
+ "\t%s\n"
+ "However, parser and preparser succeeded",
+ source->ToCString().get());
+ CHECK(false);
}
}
-void TestParserSyncWithFlags(i::Handle<i::String> source) {
- static const int kFlagsCount = 6;
- const int flags[kFlagsCount] = {
- i::kNoParsingFlags | i::CLASSIC_MODE,
- i::kNoParsingFlags | i::STRICT_MODE,
- i::kNoParsingFlags | i::EXTENDED_MODE,
- i::kAllowLazy | i::CLASSIC_MODE,
- i::kAllowLazy | i::STRICT_MODE,
- i::kAllowLazy | i::EXTENDED_MODE
- };
-
- for (int k = 0; k < kFlagsCount; ++k) {
- TestParserSync(source, flags[k]);
+void TestParserSync(const char* source,
+ const ParserFlag* varying_flags,
+ size_t varying_flags_length,
+ ParserSyncTestResult result = kSuccessOrError,
+ const ParserFlag* always_true_flags = NULL,
+ size_t always_true_flags_length = 0) {
+ i::Handle<i::String> str =
+ CcTest::i_isolate()->factory()->NewStringFromAsciiChecked(source);
+ for (int bits = 0; bits < (1 << varying_flags_length); bits++) {
+ i::EnumSet<ParserFlag> flags;
+ for (size_t flag_index = 0; flag_index < varying_flags_length;
+ ++flag_index) {
+ if ((bits & (1 << flag_index)) != 0) flags.Add(varying_flags[flag_index]);
+ }
+ for (size_t flag_index = 0; flag_index < always_true_flags_length;
+ ++flag_index) {
+ flags.Add(always_true_flags[flag_index]);
+ }
+ TestParserSyncWithFlags(str, flags, result);
}
}
@@ -1147,6 +1383,7 @@
{ "with ({})", "" },
{ "switch (12) { case 12: ", "}" },
{ "switch (12) { default: ", "}" },
+ { "switch (12) { ", "case 12: }" },
{ "label2: ", "" },
{ NULL, NULL }
};
@@ -1175,9 +1412,10 @@
"break",
"break label",
"break\nlabel",
- "return",
- "return 12",
- "return\n12",
+ // TODO(marja): activate once parsing 'return' is merged into ParserBase.
+ // "return",
+ // "return 12",
+ // "return\n12",
"with ({}) ;",
"with ({}) {}",
"with ({}) 12",
@@ -1202,13 +1440,22 @@
NULL
};
- v8::HandleScope handles;
- v8::Persistent<v8::Context> context = v8::Context::New();
+ v8::HandleScope handles(CcTest::isolate());
+ v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
v8::Context::Scope context_scope(context);
- int marker;
- i::Isolate::Current()->stack_guard()->SetStackLimit(
- reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ i::GetCurrentStackPosition() - 128 * 1024);
+
+ static const ParserFlag flags1[] = {
+ kAllowArrowFunctions,
+ kAllowClasses,
+ kAllowHarmonyNumericLiterals,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonyScoping,
+ kAllowLazy,
+ kAllowModules,
+ };
for (int i = 0; context_data[i][0] != NULL; ++i) {
for (int j = 0; statement_data[j] != NULL; ++j) {
@@ -1221,18 +1468,2504 @@
+ kSuffixLen + i::StrLength("label: for (;;) { }");
// Plug the source code pieces together.
- i::Vector<char> program = i::Vector<char>::New(kProgramSize + 1);
- int length = i::OS::SNPrintF(program,
+ i::ScopedVector<char> program(kProgramSize + 1);
+ int length = i::SNPrintF(program,
"label: for (;;) { %s%s%s%s }",
context_data[i][0],
statement_data[j],
termination_data[k],
context_data[i][1]);
CHECK(length == kProgramSize);
- i::Handle<i::String> source =
- FACTORY->NewStringFromAscii(i::CStrVector(program.start()));
- TestParserSyncWithFlags(source);
+ TestParserSync(program.start(), flags1, arraysize(flags1));
}
}
}
+
+ // Neither Harmony numeric literals nor our natives syntax have any
+ // interaction with the flags above, so test these separately to reduce
+ // the combinatorial explosion.
+ static const ParserFlag flags2[] = { kAllowHarmonyNumericLiterals };
+ TestParserSync("0o1234", flags2, arraysize(flags2));
+ TestParserSync("0b1011", flags2, arraysize(flags2));
+
+ static const ParserFlag flags3[] = { kAllowNativesSyntax };
+ TestParserSync("%DebugPrint(123)", flags3, arraysize(flags3));
+}
+
+
+TEST(StrictOctal) {
+ // Test that syntax error caused by octal literal is reported correctly as
+ // such (issue 2220).
+ v8::V8::Initialize();
+ v8::HandleScope scope(CcTest::isolate());
+ v8::Context::Scope context_scope(
+ v8::Context::New(CcTest::isolate()));
+ v8::TryCatch try_catch;
+ const char* script =
+ "\"use strict\"; \n"
+ "a = function() { \n"
+ " b = function() { \n"
+ " 01; \n"
+ " }; \n"
+ "}; \n";
+ v8::Script::Compile(v8::String::NewFromUtf8(CcTest::isolate(), script));
+ CHECK(try_catch.HasCaught());
+ v8::String::Utf8Value exception(try_catch.Exception());
+ 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,
+ const ParserFlag* flags = NULL,
+ int flags_len = 0,
+ const ParserFlag* always_true_flags = NULL,
+ int always_true_flags_len = 0) {
+ v8::HandleScope handles(CcTest::isolate());
+ v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
+ v8::Context::Scope context_scope(context);
+
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ i::GetCurrentStackPosition() - 128 * 1024);
+
+ static const ParserFlag default_flags[] = {
+ kAllowArrowFunctions,
+ kAllowClasses,
+ kAllowHarmonyNumericLiterals,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonyScoping,
+ kAllowLazy,
+ kAllowModules,
+ kAllowNativesSyntax,
+ };
+ ParserFlag* generated_flags = NULL;
+ if (flags == NULL) {
+ flags = default_flags;
+ flags_len = arraysize(default_flags);
+ if (always_true_flags != NULL) {
+ // Remove always_true_flags from default_flags.
+ CHECK(always_true_flags_len < flags_len);
+ generated_flags = new ParserFlag[flags_len - always_true_flags_len];
+ int flag_index = 0;
+ for (int i = 0; i < flags_len; ++i) {
+ bool use_flag = true;
+ for (int j = 0; j < always_true_flags_len; ++j) {
+ if (flags[i] == always_true_flags[j]) {
+ use_flag = false;
+ break;
+ }
+ }
+ if (use_flag) generated_flags[flag_index++] = flags[i];
+ }
+ CHECK(flag_index == flags_len - always_true_flags_len);
+ flags_len = flag_index;
+ flags = generated_flags;
+ }
+ }
+ 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::SNPrintF(program,
+ "%s%s%s",
+ context_data[i][0],
+ statement_data[j],
+ context_data[i][1]);
+ CHECK(length == kProgramSize);
+ TestParserSync(program.start(),
+ flags,
+ flags_len,
+ result,
+ always_true_flags,
+ always_true_flags_len);
+ }
+ }
+ delete[] generated_flags;
+}
+
+
+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) => { }",
+ "(arguments) => { }",
+ "(foo, eval) => { }",
+ "(foo, 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(NoErrorsEvalAndArgumentsSloppy) {
+ // 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\";", "}" },
+ { "() => { \"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
+ };
+
+ static const ParserFlag always_flags[] = {kAllowArrowFunctions};
+ RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+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\"; ", "}"},
+ { "() => { \"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++;",
+ "var yield = 13;",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowArrowFunctions};
+ RunParserSyncTest(context_data, statement_data, kError, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
+
+
+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++;",
+ "var yield = 13;",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowArrowFunctions};
+ RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+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\"; ", "}"},
+ { "var eval; () => {", "}"},
+ { "var eval; () => {\"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) => { }",
+ "(bar, super) => { }",
+ "super = 1;",
+ "var foo = super = 1;",
+ "++super;",
+ "super++;",
+ "function foo super",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(NoErrorsLetSloppyAllModes) {
+ // In sloppy mode, it's okay to use "let" as identifier.
+ const char* context_data[][2] = {
+ { "", "" },
+ { "function f() {", "}" },
+ { "(function f() {", "})" },
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "var let;",
+ "var foo, let;",
+ "try { } catch (let) { }",
+ "function let() { }",
+ "(function let() { })",
+ "function foo(let) { }",
+ "function foo(bar, let) { }",
+ "let = 1;",
+ "var foo = let = 1;",
+ "let * 2;",
+ "++let;",
+ "let++;",
+ "let: 34",
+ "function let(let) { let: let(let + let(0)); }",
+ "({ let: 1 })",
+ "({ get let() { 1 } })",
+ "let(100)",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(NoErrorsYieldSloppyAllModes) {
+ // In sloppy mode, it's okay to use "yield" as identifier, *except* inside a
+ // generator (see other test).
+ const char* context_data[][2] = {
+ { "", "" },
+ { "function not_gen() {", "}" },
+ { "(function not_gen() {", "})" },
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "var yield;",
+ "var foo, yield;",
+ "try { } catch (yield) { }",
+ "function yield() { }",
+ "(function yield() { })",
+ "function foo(yield) { }",
+ "function foo(bar, yield) { }",
+ "yield = 1;",
+ "var foo = yield = 1;",
+ "yield * 2;",
+ "++yield;",
+ "yield++;",
+ "yield: 34",
+ "function yield(yield) { yield: yield (yield + yield(0)); }",
+ "({ yield: 1 })",
+ "({ get yield() { 1 } })",
+ "yield(100)",
+ "yield[100]",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(NoErrorsYieldSloppyGeneratorsEnabled) {
+ // In sloppy mode, it's okay to use "yield" as identifier, *except* inside a
+ // generator (see next test).
+ const char* context_data[][2] = {
+ { "", "" },
+ { "function not_gen() {", "}" },
+ { "function * gen() { function not_gen() {", "} }" },
+ { "(function not_gen() {", "})" },
+ { "(function * gen() { (function not_gen() {", "}) })" },
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "var yield;",
+ "var foo, yield;",
+ "try { } catch (yield) { }",
+ "function yield() { }",
+ "(function yield() { })",
+ "function foo(yield) { }",
+ "function foo(bar, yield) { }",
+ "function * yield() { }",
+ "(function * yield() { })",
+ "yield = 1;",
+ "var foo = yield = 1;",
+ "yield * 2;",
+ "++yield;",
+ "yield++;",
+ "yield: 34",
+ "function yield(yield) { yield: yield (yield + yield(0)); }",
+ "({ yield: 1 })",
+ "({ get yield() { 1 } })",
+ "yield(100)",
+ "yield[100]",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(ErrorsYieldStrict) {
+ const char* context_data[][2] = {
+ { "\"use strict\";", "" },
+ { "\"use strict\"; function not_gen() {", "}" },
+ { "function test_func() {\"use strict\"; ", "}"},
+ { "\"use strict\"; function * gen() { function not_gen() {", "} }" },
+ { "\"use strict\"; (function not_gen() {", "})" },
+ { "\"use strict\"; (function * gen() { (function not_gen() {", "}) })" },
+ { "() => {\"use strict\"; ", "}" },
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "var yield;",
+ "var foo, yield;",
+ "try { } catch (yield) { }",
+ "function yield() { }",
+ "(function yield() { })",
+ "function foo(yield) { }",
+ "function foo(bar, yield) { }",
+ "function * yield() { }",
+ "(function * yield() { })",
+ "yield = 1;",
+ "var foo = yield = 1;",
+ "++yield;",
+ "yield++;",
+ "yield: 34;",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(NoErrorsGenerator) {
+ const char* context_data[][2] = {
+ { "function * gen() {", "}" },
+ { "(function * gen() {", "})" },
+ { "(function * () {", "})" },
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ // A generator without a body is valid.
+ ""
+ // Valid yield expressions inside generators.
+ "yield 2;",
+ "yield * 2;",
+ "yield * \n 2;",
+ "yield yield 1;",
+ "yield * yield * 1;",
+ "yield 3 + (yield 4);",
+ "yield * 3 + (yield * 4);",
+ "(yield * 3) + (yield * 4);",
+ "yield 3; yield 4;",
+ "yield * 3; yield * 4;",
+ "(function (yield) { })",
+ "yield { yield: 12 }",
+ "yield /* comment */ { yield: 12 }",
+ "yield * \n { yield: 12 }",
+ "yield /* comment */ * \n { yield: 12 }",
+ // You can return in a generator.
+ "yield 1; return",
+ "yield * 1; return",
+ "yield 1; return 37",
+ "yield * 1; return 37",
+ "yield 1; return 37; yield 'dead';",
+ "yield * 1; return 37; yield * 'dead';",
+ // Yield is still a valid key in object literals.
+ "({ yield: 1 })",
+ "({ get yield() { } })",
+ // Yield without RHS.
+ "yield;",
+ "yield",
+ "yield\n",
+ "yield /* comment */"
+ "yield // comment\n"
+ "(yield)",
+ "[yield]",
+ "{yield}",
+ "yield, yield",
+ "yield; yield",
+ "(yield) ? yield : yield",
+ "(yield) \n ? yield : yield",
+ // If there is a newline before the next token, we don't look for RHS.
+ "yield\nfor (;;) {}",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(ErrorsYieldGenerator) {
+ const char* context_data[][2] = {
+ { "function * gen() {", "}" },
+ { "\"use strict\"; function * gen() {", "}" },
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ // Invalid yield expressions inside generators.
+ "var yield;",
+ "var foo, yield;",
+ "try { } catch (yield) { }",
+ "function yield() { }",
+ // The name of the NFE is let-bound in the generator, which does not permit
+ // yield to be an identifier.
+ "(function yield() { })",
+ "(function * yield() { })",
+ // Yield isn't valid as a formal parameter for generators.
+ "function * foo(yield) { }",
+ "(function * foo(yield) { })",
+ "yield = 1;",
+ "var foo = yield = 1;",
+ "++yield;",
+ "yield++;",
+ "yield *",
+ "(yield *)",
+ // Yield binds very loosely, so this parses as "yield (3 + yield 4)", which
+ // is invalid.
+ "yield 3 + yield 4;",
+ "yield: 34",
+ "yield ? 1 : 2",
+ // Parses as yield (/ yield): invalid.
+ "yield / yield",
+ "+ yield",
+ "+ yield 3",
+ // Invalid (no newline allowed between yield and *).
+ "yield\n*3",
+ // Invalid (we see a newline, so we parse {yield:42} as a statement, not an
+ // object literal, and yield is not a valid label).
+ "yield\n{yield: 42}",
+ "yield /* comment */\n {yield: 42}",
+ "yield //comment\n {yield: 42}",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(ErrorsNameOfStrictFunction) {
+ // Tests that illegal tokens as names of a strict function produce the correct
+ // errors.
+ const char* context_data[][2] = {
+ { "function ", ""},
+ { "\"use strict\"; function", ""},
+ { "function * ", ""},
+ { "\"use strict\"; function * ", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "eval() {\"use strict\";}",
+ "arguments() {\"use strict\";}",
+ "interface() {\"use strict\";}",
+ "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] = {
+ { "function ", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "eval() { }",
+ "arguments() { }",
+ "interface() { }",
+ "yield() { }",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(NoErrorsNameOfStrictGenerator) {
+ const char* context_data[][2] = {
+ { "function * ", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "eval() { }",
+ "arguments() { }",
+ "interface() { }",
+ "yield() { }",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(ErrorsIllegalWordsAsLabelsSloppy) {
+ // 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\"; ", "}"},
+ { "() => {\"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() {", "}" },
+ { "\"use strict\"; () => {", "}" },
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "mylabel: while(true) { break mylabel; }",
+ "eval: while(true) { break eval; }",
+ "arguments: while(true) { break arguments; }",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowArrowFunctions};
+ RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+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 Parser doesn't start producing less "preparse
+ // data" (data which the embedder can cache).
+ v8::V8::Initialize();
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handles(isolate);
+
+ CcTest::i_isolate()->stack_guard()->SetStackLimit(
+ i::GetCurrentStackPosition() - 128 * 1024);
+
+ struct TestCase {
+ const char* program;
+ int functions;
+ } test_cases[] = {
+ // No functions.
+ {"var x = 42;", 0},
+ // Functions.
+ {"function foo() {}", 1}, {"function foo() {} function bar() {}", 2},
+ // Getter / setter functions are recorded as functions if they're on the top
+ // level.
+ {"var x = {get foo(){} };", 1},
+ // Functions insize lazy functions are not recorded.
+ {"function lazy() { function a() {} function b() {} function c() {} }", 1},
+ {"function lazy() { var x = {get foo(){} } }", 1},
+ {NULL, 0}
+ };
+
+ for (int i = 0; test_cases[i].program; i++) {
+ const char* program = test_cases[i].program;
+ i::Factory* factory = CcTest::i_isolate()->factory();
+ i::Handle<i::String> source =
+ factory->NewStringFromUtf8(i::CStrVector(program)).ToHandleChecked();
+ i::Handle<i::Script> script = factory->NewScript(source);
+ i::CompilationInfoWithZone info(script);
+ i::ScriptData* sd = NULL;
+ info.SetCachedData(&sd, v8::ScriptCompiler::kProduceParserCache);
+ i::Parser::Parse(&info, true);
+ i::ParseData pd(sd);
+
+ if (pd.FunctionCount() != test_cases[i].functions) {
+ v8::base::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, pd.FunctionCount());
+ CHECK(false);
+ }
+ delete sd;
+ }
+}
+
+
+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);
+}
+
+
+TEST(ErrorsRegexpLiteral) {
+ const char* context_data[][2] = {
+ {"var r = ", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "/unterminated",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(NoErrorsRegexpLiteral) {
+ const char* context_data[][2] = {
+ {"var r = ", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "/foo/",
+ "/foo/g",
+ "/foo/whatever", // This is an error but not detected by the parser.
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(Intrinsics) {
+ const char* context_data[][2] = {
+ {"", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "%someintrinsic(arg)",
+ NULL
+ };
+
+ // This test requires kAllowNativesSyntax to succeed.
+ static const ParserFlag always_true_flags[] = {
+ kAllowNativesSyntax
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
+ always_true_flags, 1);
+}
+
+
+TEST(NoErrorsNewExpression) {
+ const char* context_data[][2] = {
+ {"", ""},
+ {"var f =", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "new foo",
+ "new foo();",
+ "new foo(1);",
+ "new foo(1, 2);",
+ // The first () will be processed as a part of the NewExpression and the
+ // second () will be processed as part of LeftHandSideExpression.
+ "new foo()();",
+ // The first () will be processed as a part of the inner NewExpression and
+ // the second () will be processed as a part of the outer NewExpression.
+ "new new foo()();",
+ "new foo.bar;",
+ "new foo.bar();",
+ "new foo.bar.baz;",
+ "new foo.bar().baz;",
+ "new foo[bar];",
+ "new foo[bar]();",
+ "new foo[bar][baz];",
+ "new foo[bar]()[baz];",
+ "new foo[bar].baz(baz)()[bar].baz;",
+ "new \"foo\"", // Runtime error
+ "new 1", // Runtime error
+ // This even runs:
+ "(new new Function(\"this.x = 1\")).x;",
+ "new new Test_Two(String, 2).v(0123).length;",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(ErrorsNewExpression) {
+ const char* context_data[][2] = {
+ {"", ""},
+ {"var f =", ""},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "new foo bar",
+ "new ) foo",
+ "new ++foo",
+ "new foo ++",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(StrictObjectLiteralChecking) {
+ const char* strict_context_data[][2] = {
+ {"\"use strict\"; var myobject = {", "};"},
+ {"\"use strict\"; var myobject = {", ",};"},
+ { NULL, NULL }
+ };
+ const char* non_strict_context_data[][2] = {
+ {"var myobject = {", "};"},
+ {"var myobject = {", ",};"},
+ { NULL, NULL }
+ };
+
+ // These are only errors in strict mode.
+ const char* statement_data[] = {
+ "foo: 1, foo: 2",
+ "\"foo\": 1, \"foo\": 2",
+ "foo: 1, \"foo\": 2",
+ "1: 1, 1: 2",
+ "1: 1, \"1\": 2",
+ "get: 1, get: 2", // Not a getter for real, just a property called get.
+ "set: 1, set: 2", // Not a setter for real, just a property called set.
+ NULL
+ };
+
+ RunParserSyncTest(non_strict_context_data, statement_data, kSuccess);
+ RunParserSyncTest(strict_context_data, statement_data, kError);
+}
+
+
+TEST(ErrorsObjectLiteralChecking) {
+ const char* context_data[][2] = {
+ {"\"use strict\"; var myobject = {", "};"},
+ {"var myobject = {", "};"},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ ",",
+ "foo: 1, get foo() {}",
+ "foo: 1, set foo(v) {}",
+ "\"foo\": 1, get \"foo\"() {}",
+ "\"foo\": 1, set \"foo\"(v) {}",
+ "1: 1, get 1() {}",
+ "1: 1, set 1() {}",
+ "get foo() {}, get foo() {}",
+ "set foo(_) {}, set foo(_) {}",
+ // It's counter-intuitive, but these collide too (even in classic
+ // mode). Note that we can have "foo" and foo as properties in classic
+ // mode,
+ // but we cannot have "foo" and get foo, or foo and get "foo".
+ "foo: 1, get \"foo\"() {}",
+ "foo: 1, set \"foo\"(v) {}",
+ "\"foo\": 1, get foo() {}",
+ "\"foo\": 1, set foo(v) {}",
+ "1: 1, get \"1\"() {}",
+ "1: 1, set \"1\"() {}",
+ "\"1\": 1, get 1() {}"
+ "\"1\": 1, set 1(v) {}"
+ // Wrong number of parameters
+ "get bar(x) {}",
+ "get bar(x, y) {}",
+ "set bar() {}",
+ "set bar(x, y) {}",
+ // Parsing FunctionLiteral for getter or setter fails
+ "get foo( +",
+ "get foo() \"error\"",
+ NULL};
+
+ RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(NoErrorsObjectLiteralChecking) {
+ const char* context_data[][2] = {
+ {"var myobject = {", "};"},
+ {"var myobject = {", ",};"},
+ {"\"use strict\"; var myobject = {", "};"},
+ {"\"use strict\"; var myobject = {", ",};"},
+ { NULL, NULL }
+ };
+
+ const char* statement_data[] = {
+ "foo: 1, bar: 2",
+ "\"foo\": 1, \"bar\": 2",
+ "1: 1, 2: 2",
+ // Syntax: IdentifierName ':' AssignmentExpression
+ "foo: bar = 5 + baz",
+ // Syntax: 'get' PropertyName '(' ')' '{' FunctionBody '}'
+ "get foo() {}",
+ "get \"foo\"() {}",
+ "get 1() {}",
+ // Syntax: 'set' PropertyName '(' PropertySetParameterList ')'
+ // '{' FunctionBody '}'
+ "set foo(v) {}",
+ "set \"foo\"(v) {}",
+ "set 1(v) {}",
+ // Non-colliding getters and setters -> no errors
+ "foo: 1, get bar() {}",
+ "foo: 1, set bar(v) {}",
+ "\"foo\": 1, get \"bar\"() {}",
+ "\"foo\": 1, set \"bar\"(v) {}",
+ "1: 1, get 2() {}",
+ "1: 1, set 2(v) {}",
+ "get: 1, get foo() {}",
+ "set: 1, set foo(_) {}",
+ // Keywords, future reserved and strict future reserved are also allowed as
+ // property names.
+ "if: 4",
+ "interface: 5",
+ "super: 6",
+ "eval: 7",
+ "arguments: 8",
+ NULL
+ };
+
+ RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(TooManyArguments) {
+ const char* context_data[][2] = {
+ {"foo(", "0)"},
+ { NULL, NULL }
+ };
+
+ using v8::internal::Code;
+ char statement[Code::kMaxArguments * 2 + 1];
+ for (int i = 0; i < Code::kMaxArguments; ++i) {
+ statement[2 * i] = '0';
+ statement[2 * i + 1] = ',';
+ }
+ statement[Code::kMaxArguments * 2] = 0;
+
+ const char* statement_data[] = {
+ statement,
+ NULL
+ };
+
+ // The test is quite slow, so run it with a reduced set of flags.
+ static const ParserFlag empty_flags[] = {kAllowLazy};
+ RunParserSyncTest(context_data, statement_data, kError, empty_flags, 1);
+}
+
+
+TEST(StrictDelete) {
+ // "delete <Identifier>" is not allowed in strict mode.
+ const char* strict_context_data[][2] = {
+ {"\"use strict\"; ", ""},
+ { NULL, NULL }
+ };
+
+ const char* sloppy_context_data[][2] = {
+ {"", ""},
+ { NULL, NULL }
+ };
+
+ // These are errors in the strict mode.
+ const char* sloppy_statement_data[] = {
+ "delete foo;",
+ "delete foo + 1;",
+ "delete (foo);",
+ "delete eval;",
+ "delete interface;",
+ NULL
+ };
+
+ // These are always OK
+ const char* good_statement_data[] = {
+ "delete this;",
+ "delete 1;",
+ "delete 1 + 2;",
+ "delete foo();",
+ "delete foo.bar;",
+ "delete foo[bar];",
+ "delete foo--;",
+ "delete --foo;",
+ "delete new foo();",
+ "delete new foo(bar);",
+ NULL
+ };
+
+ // These are always errors
+ const char* bad_statement_data[] = {
+ "delete if;",
+ NULL
+ };
+
+ RunParserSyncTest(strict_context_data, sloppy_statement_data, kError);
+ RunParserSyncTest(sloppy_context_data, sloppy_statement_data, kSuccess);
+
+ RunParserSyncTest(strict_context_data, good_statement_data, kSuccess);
+ RunParserSyncTest(sloppy_context_data, good_statement_data, kSuccess);
+
+ RunParserSyncTest(strict_context_data, bad_statement_data, kError);
+ RunParserSyncTest(sloppy_context_data, bad_statement_data, kError);
+}
+
+
+TEST(InvalidLeftHandSide) {
+ const char* assignment_context_data[][2] = {
+ {"", " = 1;"},
+ {"\"use strict\"; ", " = 1;"},
+ { NULL, NULL }
+ };
+
+ const char* prefix_context_data[][2] = {
+ {"++", ";"},
+ {"\"use strict\"; ++", ";"},
+ {NULL, NULL},
+ };
+
+ const char* postfix_context_data[][2] = {
+ {"", "++;"},
+ {"\"use strict\"; ", "++;"},
+ { NULL, NULL }
+ };
+
+ // Good left hand sides for assigment or prefix / postfix operations.
+ const char* good_statement_data[] = {
+ "foo",
+ "foo.bar",
+ "foo[bar]",
+ "foo()[bar]",
+ "foo().bar",
+ "this.foo",
+ "this[foo]",
+ "new foo()[bar]",
+ "new foo().bar",
+ "foo()",
+ "foo(bar)",
+ "foo[bar]()",
+ "foo.bar()",
+ "this()",
+ "this.foo()",
+ "this[foo].bar()",
+ "this.foo[foo].bar(this)(bar)[foo]()",
+ NULL
+ };
+
+ // Bad left hand sides for assigment or prefix / postfix operations.
+ const char* bad_statement_data_common[] = {
+ "2",
+ "new foo",
+ "new foo()",
+ "null",
+ "if", // Unexpected token
+ "{x: 1}", // Unexpected token
+ "this",
+ "\"bar\"",
+ "(foo + bar)",
+ "new new foo()[bar]", // means: new (new foo()[bar])
+ "new new foo().bar", // means: new (new foo()[bar])
+ NULL
+ };
+
+ // These are not okay for assignment, but okay for prefix / postix.
+ const char* bad_statement_data_for_assignment[] = {
+ "++foo",
+ "foo++",
+ "foo + bar",
+ NULL
+ };
+
+ RunParserSyncTest(assignment_context_data, good_statement_data, kSuccess);
+ RunParserSyncTest(assignment_context_data, bad_statement_data_common, kError);
+ RunParserSyncTest(assignment_context_data, bad_statement_data_for_assignment,
+ kError);
+
+ RunParserSyncTest(prefix_context_data, good_statement_data, kSuccess);
+ RunParserSyncTest(prefix_context_data, bad_statement_data_common, kError);
+
+ RunParserSyncTest(postfix_context_data, good_statement_data, kSuccess);
+ RunParserSyncTest(postfix_context_data, bad_statement_data_common, kError);
+}
+
+
+TEST(FuncNameInferrerBasic) {
+ // Tests that function names are inferred properly.
+ i::FLAG_allow_natives_syntax = true;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ LocalContext env;
+ CompileRun("var foo1 = function() {}; "
+ "var foo2 = function foo3() {}; "
+ "function not_ctor() { "
+ " var foo4 = function() {}; "
+ " return %FunctionGetInferredName(foo4); "
+ "} "
+ "function Ctor() { "
+ " var foo5 = function() {}; "
+ " return %FunctionGetInferredName(foo5); "
+ "} "
+ "var obj1 = { foo6: function() {} }; "
+ "var obj2 = { 'foo7': function() {} }; "
+ "var obj3 = {}; "
+ "obj3[1] = function() {}; "
+ "var obj4 = {}; "
+ "obj4[1] = function foo8() {}; "
+ "var obj5 = {}; "
+ "obj5['foo9'] = function() {}; "
+ "var obj6 = { obj7 : { foo10: function() {} } };");
+ ExpectString("%FunctionGetInferredName(foo1)", "foo1");
+ // foo2 is not unnamed -> its name is not inferred.
+ ExpectString("%FunctionGetInferredName(foo2)", "");
+ ExpectString("not_ctor()", "foo4");
+ ExpectString("Ctor()", "Ctor.foo5");
+ ExpectString("%FunctionGetInferredName(obj1.foo6)", "obj1.foo6");
+ ExpectString("%FunctionGetInferredName(obj2.foo7)", "obj2.foo7");
+ ExpectString("%FunctionGetInferredName(obj3[1])",
+ "obj3.(anonymous function)");
+ ExpectString("%FunctionGetInferredName(obj4[1])", "");
+ ExpectString("%FunctionGetInferredName(obj5['foo9'])", "obj5.foo9");
+ ExpectString("%FunctionGetInferredName(obj6.obj7.foo10)", "obj6.obj7.foo10");
+}
+
+
+TEST(FuncNameInferrerTwoByte) {
+ // Tests function name inferring in cases where some parts of the inferred
+ // function name are two-byte strings.
+ i::FLAG_allow_natives_syntax = true;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ LocalContext env;
+ uint16_t* two_byte_source = AsciiToTwoByteString(
+ "var obj1 = { oXj2 : { foo1: function() {} } }; "
+ "%FunctionGetInferredName(obj1.oXj2.foo1)");
+ uint16_t* two_byte_name = AsciiToTwoByteString("obj1.oXj2.foo1");
+ // Make it really non-Latin1 (replace the Xs with a non-Latin1 character).
+ two_byte_source[14] = two_byte_source[78] = two_byte_name[6] = 0x010d;
+ v8::Local<v8::String> source =
+ v8::String::NewFromTwoByte(isolate, two_byte_source);
+ v8::Local<v8::Value> result = CompileRun(source);
+ CHECK(result->IsString());
+ v8::Local<v8::String> expected_name =
+ v8::String::NewFromTwoByte(isolate, two_byte_name);
+ CHECK(result->Equals(expected_name));
+ i::DeleteArray(two_byte_source);
+ i::DeleteArray(two_byte_name);
+}
+
+
+TEST(FuncNameInferrerEscaped) {
+ // The same as FuncNameInferrerTwoByte, except that we express the two-byte
+ // character as a unicode escape.
+ i::FLAG_allow_natives_syntax = true;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ LocalContext env;
+ uint16_t* two_byte_source = AsciiToTwoByteString(
+ "var obj1 = { o\\u010dj2 : { foo1: function() {} } }; "
+ "%FunctionGetInferredName(obj1.o\\u010dj2.foo1)");
+ uint16_t* two_byte_name = AsciiToTwoByteString("obj1.oXj2.foo1");
+ // Fix to correspond to the non-ASCII name in two_byte_source.
+ two_byte_name[6] = 0x010d;
+ v8::Local<v8::String> source =
+ v8::String::NewFromTwoByte(isolate, two_byte_source);
+ v8::Local<v8::Value> result = CompileRun(source);
+ CHECK(result->IsString());
+ v8::Local<v8::String> expected_name =
+ v8::String::NewFromTwoByte(isolate, two_byte_name);
+ CHECK(result->Equals(expected_name));
+ i::DeleteArray(two_byte_source);
+ i::DeleteArray(two_byte_name);
+}
+
+
+TEST(RegressionLazyFunctionWithErrorWithArg) {
+ // The bug occurred when a lazy function had an error which requires a
+ // parameter (such as "unknown label" here). The error message was processed
+ // before the AstValueFactory containing the error message string was
+ // internalized.
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ LocalContext env;
+ i::FLAG_lazy = true;
+ i::FLAG_min_preparse_length = 0;
+ CompileRun("function this_is_lazy() {\n"
+ " break p;\n"
+ "}\n"
+ "this_is_lazy();\n");
+}
+
+
+TEST(SerializationOfMaybeAssignmentFlag) {
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+ i::HandleScope scope(isolate);
+ LocalContext env;
+
+ const char* src =
+ "function h() {"
+ " var result = [];"
+ " function f() {"
+ " result.push(2);"
+ " }"
+ " function assertResult(r) {"
+ " f();"
+ " result = [];"
+ " }"
+ " assertResult([2]);"
+ " assertResult([2]);"
+ " return f;"
+ "};"
+ "h();";
+
+ i::ScopedVector<char> program(Utf8LengthHelper(src) + 1);
+ i::SNPrintF(program, "%s", src);
+ i::Handle<i::String> source = factory->InternalizeUtf8String(program.start());
+ source->PrintOn(stdout);
+ printf("\n");
+ i::Zone zone(isolate);
+ v8::Local<v8::Value> v = CompileRun(src);
+ i::Handle<i::Object> o = v8::Utils::OpenHandle(*v);
+ i::Handle<i::JSFunction> f = i::Handle<i::JSFunction>::cast(o);
+ i::Context* context = f->context();
+ i::AstValueFactory avf(&zone, isolate->heap()->HashSeed());
+ avf.Internalize(isolate);
+ const i::AstRawString* name = avf.GetOneByteString("result");
+ i::Handle<i::String> str = name->string();
+ CHECK(str->IsInternalizedString());
+ i::Scope* global_scope =
+ new (&zone) i::Scope(NULL, i::GLOBAL_SCOPE, &avf, &zone);
+ global_scope->Initialize();
+ i::Scope* s = i::Scope::DeserializeScopeChain(context, global_scope, &zone);
+ DCHECK(s != global_scope);
+ DCHECK(name != NULL);
+
+ // Get result from h's function context (that is f's context)
+ i::Variable* var = s->Lookup(name);
+
+ CHECK(var != NULL);
+ // Maybe assigned should survive deserialization
+ CHECK(var->maybe_assigned() == i::kMaybeAssigned);
+ // TODO(sigurds) Figure out if is_used should survive context serialization.
+}
+
+
+TEST(IfArgumentsArrayAccessedThenParametersMaybeAssigned) {
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+ i::HandleScope scope(isolate);
+ LocalContext env;
+
+
+ const char* src =
+ "function f(x) {"
+ " var a = arguments;"
+ " function g(i) {"
+ " ++a[0];"
+ " };"
+ " return g;"
+ " }"
+ "f(0);";
+
+ i::ScopedVector<char> program(Utf8LengthHelper(src) + 1);
+ i::SNPrintF(program, "%s", src);
+ i::Handle<i::String> source = factory->InternalizeUtf8String(program.start());
+ source->PrintOn(stdout);
+ printf("\n");
+ i::Zone zone(isolate);
+ v8::Local<v8::Value> v = CompileRun(src);
+ i::Handle<i::Object> o = v8::Utils::OpenHandle(*v);
+ i::Handle<i::JSFunction> f = i::Handle<i::JSFunction>::cast(o);
+ i::Context* context = f->context();
+ i::AstValueFactory avf(&zone, isolate->heap()->HashSeed());
+ avf.Internalize(isolate);
+
+ i::Scope* global_scope =
+ new (&zone) i::Scope(NULL, i::GLOBAL_SCOPE, &avf, &zone);
+ global_scope->Initialize();
+ i::Scope* s = i::Scope::DeserializeScopeChain(context, global_scope, &zone);
+ DCHECK(s != global_scope);
+ const i::AstRawString* name_x = avf.GetOneByteString("x");
+
+ // Get result from f's function context (that is g's outer context)
+ i::Variable* var_x = s->Lookup(name_x);
+ CHECK(var_x != NULL);
+ CHECK(var_x->maybe_assigned() == i::kMaybeAssigned);
+}
+
+
+TEST(ExportsMaybeAssigned) {
+ i::FLAG_use_strict = true;
+ i::FLAG_harmony_scoping = true;
+ i::FLAG_harmony_modules = true;
+
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+ i::HandleScope scope(isolate);
+ LocalContext env;
+
+ const char* src =
+ "module A {"
+ " export var x = 1;"
+ " export function f() { return x };"
+ " export const y = 2;"
+ " module B {}"
+ " export module C {}"
+ "};"
+ "A.f";
+
+ i::ScopedVector<char> program(Utf8LengthHelper(src) + 1);
+ i::SNPrintF(program, "%s", src);
+ i::Handle<i::String> source = factory->InternalizeUtf8String(program.start());
+ source->PrintOn(stdout);
+ printf("\n");
+ i::Zone zone(isolate);
+ v8::Local<v8::Value> v = CompileRun(src);
+ i::Handle<i::Object> o = v8::Utils::OpenHandle(*v);
+ i::Handle<i::JSFunction> f = i::Handle<i::JSFunction>::cast(o);
+ i::Context* context = f->context();
+ i::AstValueFactory avf(&zone, isolate->heap()->HashSeed());
+ avf.Internalize(isolate);
+
+ i::Scope* global_scope =
+ new (&zone) i::Scope(NULL, i::GLOBAL_SCOPE, &avf, &zone);
+ global_scope->Initialize();
+ i::Scope* s = i::Scope::DeserializeScopeChain(context, global_scope, &zone);
+ DCHECK(s != global_scope);
+ const i::AstRawString* name_x = avf.GetOneByteString("x");
+ const i::AstRawString* name_f = avf.GetOneByteString("f");
+ const i::AstRawString* name_y = avf.GetOneByteString("y");
+ const i::AstRawString* name_B = avf.GetOneByteString("B");
+ const i::AstRawString* name_C = avf.GetOneByteString("C");
+
+ // Get result from h's function context (that is f's context)
+ i::Variable* var_x = s->Lookup(name_x);
+ CHECK(var_x != NULL);
+ CHECK(var_x->maybe_assigned() == i::kMaybeAssigned);
+ i::Variable* var_f = s->Lookup(name_f);
+ CHECK(var_f != NULL);
+ CHECK(var_f->maybe_assigned() == i::kMaybeAssigned);
+ i::Variable* var_y = s->Lookup(name_y);
+ CHECK(var_y != NULL);
+ CHECK(var_y->maybe_assigned() == i::kNotAssigned);
+ i::Variable* var_B = s->Lookup(name_B);
+ CHECK(var_B != NULL);
+ CHECK(var_B->maybe_assigned() == i::kNotAssigned);
+ i::Variable* var_C = s->Lookup(name_C);
+ CHECK(var_C != NULL);
+ CHECK(var_C->maybe_assigned() == i::kNotAssigned);
+}
+
+
+TEST(InnerAssignment) {
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+ i::HandleScope scope(isolate);
+ LocalContext env;
+
+ const char* prefix = "function f() {";
+ const char* midfix = " function g() {";
+ const char* suffix = "}}";
+ struct { const char* source; bool assigned; bool strict; } outers[] = {
+ // Actual assignments.
+ { "var x; var x = 5;", true, false },
+ { "var x; { var x = 5; }", true, false },
+ { "'use strict'; let x; x = 6;", true, true },
+ { "var x = 5; function x() {}", true, false },
+ // Actual non-assignments.
+ { "var x;", false, false },
+ { "var x = 5;", false, false },
+ { "'use strict'; let x;", false, true },
+ { "'use strict'; let x = 6;", false, true },
+ { "'use strict'; var x = 0; { let x = 6; }", false, true },
+ { "'use strict'; var x = 0; { let x; x = 6; }", false, true },
+ { "'use strict'; let x = 0; { let x = 6; }", false, true },
+ { "'use strict'; let x = 0; { let x; x = 6; }", false, true },
+ { "var x; try {} catch (x) { x = 5; }", false, false },
+ { "function x() {}", false, false },
+ // Eval approximation.
+ { "var x; eval('');", true, false },
+ { "eval(''); var x;", true, false },
+ { "'use strict'; let x; eval('');", true, true },
+ { "'use strict'; eval(''); let x;", true, true },
+ // Non-assignments not recognized, because the analysis is approximative.
+ { "var x; var x;", true, false },
+ { "var x = 5; var x;", true, false },
+ { "var x; { var x; }", true, false },
+ { "var x; function x() {}", true, false },
+ { "function x() {}; var x;", true, false },
+ { "var x; try {} catch (x) { var x = 5; }", true, false },
+ };
+ struct { const char* source; bool assigned; bool with; } inners[] = {
+ // Actual assignments.
+ { "x = 1;", true, false },
+ { "x++;", true, false },
+ { "++x;", true, false },
+ { "x--;", true, false },
+ { "--x;", true, false },
+ { "{ x = 1; }", true, false },
+ { "'use strict'; { let x; }; x = 0;", true, false },
+ { "'use strict'; { const x = 1; }; x = 0;", true, false },
+ { "'use strict'; { function x() {} }; x = 0;", true, false },
+ { "with ({}) { x = 1; }", true, true },
+ { "eval('');", true, false },
+ { "'use strict'; { let y; eval('') }", true, false },
+ { "function h() { x = 0; }", true, false },
+ { "(function() { x = 0; })", true, false },
+ { "(function() { x = 0; })", true, false },
+ { "with ({}) (function() { x = 0; })", true, true },
+ // Actual non-assignments.
+ { "", false, false },
+ { "x;", false, false },
+ { "var x;", false, false },
+ { "var x = 8;", false, false },
+ { "var x; x = 8;", false, false },
+ { "'use strict'; let x;", false, false },
+ { "'use strict'; let x = 8;", false, false },
+ { "'use strict'; let x; x = 8;", false, false },
+ { "'use strict'; const x = 8;", false, false },
+ { "function x() {}", false, false },
+ { "function x() { x = 0; }", false, false },
+ { "function h(x) { x = 0; }", false, false },
+ { "'use strict'; { let x; x = 0; }", false, false },
+ { "{ var x; }; x = 0;", false, false },
+ { "with ({}) {}", false, true },
+ { "var x; { with ({}) { x = 1; } }", false, true },
+ { "try {} catch(x) { x = 0; }", false, false },
+ { "try {} catch(x) { with ({}) { x = 1; } }", false, true },
+ // Eval approximation.
+ { "eval('');", true, false },
+ { "function h() { eval(''); }", true, false },
+ { "(function() { eval(''); })", true, false },
+ // Shadowing not recognized because of eval approximation.
+ { "var x; eval('');", true, false },
+ { "'use strict'; let x; eval('');", true, false },
+ { "try {} catch(x) { eval(''); }", true, false },
+ { "function x() { eval(''); }", true, false },
+ { "(function(x) { eval(''); })", true, false },
+ };
+
+ // Used to trigger lazy compilation of function
+ int comment_len = 2048;
+ i::ScopedVector<char> comment(comment_len + 1);
+ i::SNPrintF(comment, "/*%0*d*/", comment_len - 4, 0);
+ int prefix_len = Utf8LengthHelper(prefix);
+ int midfix_len = Utf8LengthHelper(midfix);
+ int suffix_len = Utf8LengthHelper(suffix);
+ for (unsigned i = 0; i < arraysize(outers); ++i) {
+ const char* outer = outers[i].source;
+ int outer_len = Utf8LengthHelper(outer);
+ for (unsigned j = 0; j < arraysize(inners); ++j) {
+ for (unsigned outer_lazy = 0; outer_lazy < 2; ++outer_lazy) {
+ for (unsigned inner_lazy = 0; inner_lazy < 2; ++inner_lazy) {
+ if (outers[i].strict && inners[j].with) continue;
+ const char* inner = inners[j].source;
+ int inner_len = Utf8LengthHelper(inner);
+
+ int outer_comment_len = outer_lazy ? comment_len : 0;
+ int inner_comment_len = inner_lazy ? comment_len : 0;
+ const char* outer_comment = outer_lazy ? comment.start() : "";
+ const char* inner_comment = inner_lazy ? comment.start() : "";
+ int len = prefix_len + outer_comment_len + outer_len + midfix_len +
+ inner_comment_len + inner_len + suffix_len;
+ i::ScopedVector<char> program(len + 1);
+
+ i::SNPrintF(program, "%s%s%s%s%s%s%s", prefix, outer_comment, outer,
+ midfix, inner_comment, inner, suffix);
+ i::Handle<i::String> source =
+ factory->InternalizeUtf8String(program.start());
+ source->PrintOn(stdout);
+ printf("\n");
+
+ i::Handle<i::Script> script = factory->NewScript(source);
+ i::CompilationInfoWithZone info(script);
+ i::Parser::ParseInfo parse_info = {
+ isolate->stack_guard()->real_climit(),
+ isolate->heap()->HashSeed(), isolate->unicode_cache()};
+ i::Parser parser(&info, &parse_info);
+ parser.set_allow_harmony_scoping(true);
+ CHECK(parser.Parse());
+ CHECK(i::Rewriter::Rewrite(&info));
+ CHECK(i::Scope::Analyze(&info));
+ CHECK(info.function() != NULL);
+
+ i::Scope* scope = info.function()->scope();
+ CHECK_EQ(scope->inner_scopes()->length(), 1);
+ i::Scope* inner_scope = scope->inner_scopes()->at(0);
+ const i::AstRawString* var_name =
+ info.ast_value_factory()->GetOneByteString("x");
+ i::Variable* var = inner_scope->Lookup(var_name);
+ bool expected = outers[i].assigned || inners[j].assigned;
+ CHECK(var != NULL);
+ CHECK(var->is_used() || !expected);
+ CHECK((var->maybe_assigned() == i::kMaybeAssigned) == expected);
+ }
+ }
+ }
+ }
+}
+
+namespace {
+
+int* global_use_counts = NULL;
+
+void MockUseCounterCallback(v8::Isolate* isolate,
+ v8::Isolate::UseCounterFeature feature) {
+ ++global_use_counts[feature];
+}
+
+}
+
+
+TEST(UseAsmUseCount) {
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::HandleScope scope(isolate);
+ LocalContext env;
+ int use_counts[v8::Isolate::kUseCounterFeatureCount] = {};
+ global_use_counts = use_counts;
+ CcTest::isolate()->SetUseCounterCallback(MockUseCounterCallback);
+ CompileRun("\"use asm\";\n"
+ "var foo = 1;\n"
+ "\"use asm\";\n" // Only the first one counts.
+ "function bar() { \"use asm\"; var baz = 1; }");
+ CHECK_EQ(2, use_counts[v8::Isolate::kUseAsm]);
+}
+
+
+TEST(ErrorsArrowFunctions) {
+ // Tests that parser and preparser generate the same kind of errors
+ // on invalid arrow function syntax.
+ const char* context_data[][2] = {
+ {"", ";"},
+ {"v = ", ";"},
+ {"bar ? (", ") : baz;"},
+ {"bar ? baz : (", ");"},
+ {"bar[", "];"},
+ {"bar, ", ";"},
+ {"", ", bar;"},
+ {NULL, NULL}
+ };
+
+ const char* statement_data[] = {
+ "=> 0",
+ "=>",
+ "() =>",
+ "=> {}",
+ ") => {}",
+ ", => {}",
+ "(,) => {}",
+ "return => {}",
+ "() => {'value': 42}",
+
+ // Check that the early return introduced in ParsePrimaryExpression
+ // does not accept stray closing parentheses.
+ ")",
+ ") => 0",
+ "foo[()]",
+ "()",
+
+ // Parameter lists with extra parens should be recognized as errors.
+ "(()) => 0",
+ "((x)) => 0",
+ "((x, y)) => 0",
+ "(x, (y)) => 0",
+ "((x, y, z)) => 0",
+ "(x, (y, z)) => 0",
+ "((x, y), z) => 0",
+
+ // Parameter lists are always validated as strict, so those are errors.
+ "eval => {}",
+ "arguments => {}",
+ "yield => {}",
+ "interface => {}",
+ "(eval) => {}",
+ "(arguments) => {}",
+ "(yield) => {}",
+ "(interface) => {}",
+ "(eval, bar) => {}",
+ "(bar, eval) => {}",
+ "(bar, arguments) => {}",
+ "(bar, yield) => {}",
+ "(bar, interface) => {}",
+ // TODO(aperez): Detecting duplicates does not work in PreParser.
+ // "(bar, bar) => {}",
+
+ // The parameter list is parsed as an expression, but only
+ // a comma-separated list of identifier is valid.
+ "32 => {}",
+ "(32) => {}",
+ "(a, 32) => {}",
+ "if => {}",
+ "(if) => {}",
+ "(a, if) => {}",
+ "a + b => {}",
+ "(a + b) => {}",
+ "(a + b, c) => {}",
+ "(a, b - c) => {}",
+ "\"a\" => {}",
+ "(\"a\") => {}",
+ "(\"a\", b) => {}",
+ "(a, \"b\") => {}",
+ "-a => {}",
+ "(-a) => {}",
+ "(-a, b) => {}",
+ "(a, -b) => {}",
+ "{} => {}",
+ "({}) => {}",
+ "(a, {}) => {}",
+ "({}, a) => {}",
+ "a++ => {}",
+ "(a++) => {}",
+ "(a++, b) => {}",
+ "(a, b++) => {}",
+ "[] => {}",
+ "([]) => {}",
+ "(a, []) => {}",
+ "([], a) => {}",
+ "(a = b) => {}",
+ "(a = b, c) => {}",
+ "(a, b = c) => {}",
+ "(foo ? bar : baz) => {}",
+ "(a, foo ? bar : baz) => {}",
+ "(foo ? bar : baz, a) => {}",
+ NULL
+ };
+
+ // The test is quite slow, so run it with a reduced set of flags.
+ static const ParserFlag flags[] = {kAllowLazy, kAllowHarmonyScoping};
+ static const ParserFlag always_flags[] = { kAllowArrowFunctions };
+ RunParserSyncTest(context_data, statement_data, kError, flags,
+ arraysize(flags), always_flags, arraysize(always_flags));
+}
+
+
+TEST(NoErrorsArrowFunctions) {
+ // Tests that parser and preparser accept valid arrow functions syntax.
+ const char* context_data[][2] = {
+ {"", ";"},
+ {"bar ? (", ") : baz;"},
+ {"bar ? baz : (", ");"},
+ {"bar, ", ";"},
+ {"", ", bar;"},
+ {NULL, NULL}
+ };
+
+ const char* statement_data[] = {
+ "() => {}",
+ "() => { return 42 }",
+ "x => { return x; }",
+ "(x) => { return x; }",
+ "(x, y) => { return x + y; }",
+ "(x, y, z) => { return x + y + z; }",
+ "(x, y) => { x.a = y; }",
+ "() => 42",
+ "x => x",
+ "x => x * x",
+ "(x) => x",
+ "(x) => x * x",
+ "(x, y) => x + y",
+ "(x, y, z) => x, y, z",
+ "(x, y) => x.a = y",
+ "() => ({'value': 42})",
+ "x => y => x + y",
+ "(x, y) => (u, v) => x*u + y*v",
+ "(x, y) => z => z * (x + y)",
+ "x => (y, z) => z * (x + y)",
+
+ // Those are comma-separated expressions, with arrow functions as items.
+ // They stress the code for validating arrow function parameter lists.
+ "a, b => 0",
+ "a, b, (c, d) => 0",
+ "(a, b, (c, d) => 0)",
+ "(a, b) => 0, (c, d) => 1",
+ "(a, b => {}, a => a + 1)",
+ "((a, b) => {}, (a => a + 1))",
+ "(a, (a, (b, c) => 0))",
+
+ // Arrow has more precedence, this is the same as: foo ? bar : (baz = {})
+ "foo ? bar : baz => {}",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowArrowFunctions};
+ RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(NoErrorsSuper) {
+ // Tests that parser and preparser accept 'super' keyword in right places.
+ const char* context_data[][2] = {{"", ";"},
+ {"k = ", ";"},
+ {"foo(", ");"},
+ {NULL, NULL}};
+
+ const char* statement_data[] = {
+ "super.x",
+ "super[27]",
+ "new super",
+ "new super()",
+ "new super(12, 45)",
+ "new new super",
+ "new new super()",
+ "new new super()()",
+ "z.super", // Ok, property lookup.
+ NULL};
+
+ static const ParserFlag always_flags[] = {kAllowClasses};
+ RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ErrorsSuper) {
+ // Tests that parser and preparser generate same errors for 'super'.
+ const char* context_data[][2] = {{"", ";"},
+ {"k = ", ";"},
+ {"foo(", ");"},
+ {NULL, NULL}};
+
+ const char* statement_data[] = {
+ "super = x",
+ "y = super",
+ "f(super)",
+ NULL};
+
+ static const ParserFlag always_flags[] = {kAllowClasses};
+ RunParserSyncTest(context_data, statement_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(NoErrorsMethodDefinition) {
+ const char* context_data[][2] = {{"({", "});"},
+ {"'use strict'; ({", "});"},
+ {"({*", "});"},
+ {"'use strict'; ({*", "});"},
+ {NULL, NULL}};
+
+ const char* object_literal_body_data[] = {
+ "m() {}",
+ "m(x) { return x; }",
+ "m(x, y) {}, n() {}",
+ "set(x, y) {}",
+ "get(x, y) {}",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals};
+ RunParserSyncTest(context_data, object_literal_body_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(MethodDefinitionNames) {
+ const char* context_data[][2] = {{"({", "(x, y) {}});"},
+ {"'use strict'; ({", "(x, y) {}});"},
+ {"({*", "(x, y) {}});"},
+ {"'use strict'; ({*", "(x, y) {}});"},
+ {NULL, NULL}};
+
+ const char* name_data[] = {
+ "m",
+ "'m'",
+ "\"m\"",
+ "\"m n\"",
+ "true",
+ "false",
+ "null",
+ "0",
+ "1.2",
+ "1e1",
+ "1E1",
+ "1e+1",
+ "1e-1",
+
+ // Keywords
+ "async",
+ "await",
+ "break",
+ "case",
+ "catch",
+ "class",
+ "const",
+ "continue",
+ "debugger",
+ "default",
+ "delete",
+ "do",
+ "else",
+ "enum",
+ "export",
+ "extends",
+ "finally",
+ "for",
+ "function",
+ "if",
+ "implements",
+ "import",
+ "in",
+ "instanceof",
+ "interface",
+ "let",
+ "new",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "return",
+ "static",
+ "super",
+ "switch",
+ "this",
+ "throw",
+ "try",
+ "typeof",
+ "var",
+ "void",
+ "while",
+ "with",
+ "yield",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals};
+ RunParserSyncTest(context_data, name_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(MethodDefinitionStrictFormalParamereters) {
+ const char* context_data[][2] = {{"({method(", "){}});"},
+ {"'use strict'; ({method(", "){}});"},
+ {"({*method(", "){}});"},
+ {"'use strict'; ({*method(", "){}});"},
+ {NULL, NULL}};
+
+ const char* params_data[] = {
+ "x, x",
+ "x, y, x",
+ "eval",
+ "arguments",
+ "var",
+ "const",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals};
+ RunParserSyncTest(context_data, params_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(MethodDefinitionDuplicateProperty) {
+ // Duplicate properties are allowed in ES6 but we haven't removed that check
+ // yet.
+ const char* context_data[][2] = {{"'use strict'; ({", "});"},
+ {NULL, NULL}};
+
+ const char* params_data[] = {
+ "x: 1, x() {}",
+ "x() {}, x: 1",
+ "x() {}, get x() {}",
+ "x() {}, set x(_) {}",
+ "x() {}, x() {}",
+ "x() {}, y() {}, x() {}",
+ "x() {}, \"x\"() {}",
+ "x() {}, 'x'() {}",
+ "0() {}, '0'() {}",
+ "1.0() {}, 1: 1",
+
+ "x: 1, *x() {}",
+ "*x() {}, x: 1",
+ "*x() {}, get x() {}",
+ "*x() {}, set x(_) {}",
+ "*x() {}, *x() {}",
+ "*x() {}, y() {}, *x() {}",
+ "*x() {}, *\"x\"() {}",
+ "*x() {}, *'x'() {}",
+ "*0() {}, *'0'() {}",
+ "*1.0() {}, 1: 1",
+
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals};
+ RunParserSyncTest(context_data, params_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassExpressionNoErrors) {
+ const char* context_data[][2] = {{"(", ");"},
+ {"var C = ", ";"},
+ {"bar, ", ";"},
+ {NULL, NULL}};
+ const char* class_data[] = {
+ "class {}",
+ "class name {}",
+ "class extends F {}",
+ "class name extends F {}",
+ "class extends (F, G) {}",
+ "class name extends (F, G) {}",
+ "class extends class {} {}",
+ "class name extends class {} {}",
+ "class extends class base {} {}",
+ "class name extends class base {} {}",
+ NULL};
+
+ static const ParserFlag always_flags[] = {kAllowClasses};
+ RunParserSyncTest(context_data, class_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassDeclarationNoErrors) {
+ const char* context_data[][2] = {{"", ""},
+ {"{", "}"},
+ {"if (true) {", "}"},
+ {NULL, NULL}};
+ const char* statement_data[] = {
+ "class name {}",
+ "class name extends F {}",
+ "class name extends (F, G) {}",
+ "class name extends class {} {}",
+ "class name extends class base {} {}",
+ NULL};
+
+ static const ParserFlag always_flags[] = {kAllowClasses};
+ RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassBodyNoErrors) {
+ // Tests that parser and preparser accept valid class syntax.
+ const char* context_data[][2] = {{"(class {", "});"},
+ {"(class extends Base {", "});"},
+ {"class C {", "}"},
+ {"class C extends Base {", "}"},
+ {NULL, NULL}};
+ const char* class_body_data[] = {
+ ";",
+ ";;",
+ "m() {}",
+ "m() {};",
+ "; m() {}",
+ "m() {}; n(x) {}",
+ "get x() {}",
+ "set x(v) {}",
+ "get() {}",
+ "set() {}",
+ "*g() {}",
+ "*g() {};",
+ "; *g() {}",
+ "*g() {}; *h(x) {}",
+ "static() {}",
+ "static m() {}",
+ "static get x() {}",
+ "static set x(v) {}",
+ "static get() {}",
+ "static set() {}",
+ "static static() {}",
+ "static get static() {}",
+ "static set static(v) {}",
+ "*static() {}",
+ "*get() {}",
+ "*set() {}",
+ "static *g() {}",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, class_body_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassPropertyNameNoErrors) {
+ const char* context_data[][2] = {{"(class {", "() {}});"},
+ {"(class { get ", "() {}});"},
+ {"(class { set ", "(v) {}});"},
+ {"(class { static ", "() {}});"},
+ {"(class { static get ", "() {}});"},
+ {"(class { static set ", "(v) {}});"},
+ {"(class { *", "() {}});"},
+ {"(class { static *", "() {}});"},
+ {"class C {", "() {}}"},
+ {"class C { get ", "() {}}"},
+ {"class C { set ", "(v) {}}"},
+ {"class C { static ", "() {}}"},
+ {"class C { static get ", "() {}}"},
+ {"class C { static set ", "(v) {}}"},
+ {"class C { *", "() {}}"},
+ {"class C { static *", "() {}}"},
+ {NULL, NULL}};
+ const char* name_data[] = {
+ "42",
+ "42.5",
+ "42e2",
+ "42e+2",
+ "42e-2",
+ "null",
+ "false",
+ "true",
+ "'str'",
+ "\"str\"",
+ "static",
+ "get",
+ "set",
+ "var",
+ "const",
+ "let",
+ "this",
+ "class",
+ "function",
+ "yield",
+ "if",
+ "else",
+ "for",
+ "while",
+ "do",
+ "try",
+ "catch",
+ "finally",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, name_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassExpressionErrors) {
+ const char* context_data[][2] = {{"(", ");"},
+ {"var C = ", ";"},
+ {"bar, ", ";"},
+ {NULL, NULL}};
+ const char* class_data[] = {
+ "class",
+ "class name",
+ "class name extends",
+ "class extends",
+ "class {",
+ "class { m }",
+ "class { m; n }",
+ "class { m: 1 }",
+ "class { m(); n() }",
+ "class { get m }",
+ "class { get m() }",
+ "class { get m() { }",
+ "class { set m() {} }", // Missing required parameter.
+ "class { m() {}, n() {} }", // No commas allowed.
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, class_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassDeclarationErrors) {
+ const char* context_data[][2] = {{"", ""},
+ {"{", "}"},
+ {"if (true) {", "}"},
+ {NULL, NULL}};
+ const char* class_data[] = {
+ "class",
+ "class name",
+ "class name extends",
+ "class extends",
+ "class name {",
+ "class name { m }",
+ "class name { m; n }",
+ "class name { m: 1 }",
+ "class name { m(); n() }",
+ "class name { get x }",
+ "class name { get x() }",
+ "class name { set x() {) }", // missing required param
+ "class {}", // Name is required for declaration
+ "class extends base {}",
+ "class name { *",
+ "class name { * }",
+ "class name { *; }",
+ "class name { *get x() {} }",
+ "class name { *set x(_) {} }",
+ "class name { *static m() {} }",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyNumericLiterals
+ };
+ RunParserSyncTest(context_data, class_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassNameErrors) {
+ const char* context_data[][2] = {{"class ", "{}"},
+ {"(class ", "{});"},
+ {"'use strict'; class ", "{}"},
+ {"'use strict'; (class ", "{});"},
+ {NULL, NULL}};
+ const char* class_name[] = {
+ "arguments",
+ "eval",
+ "implements",
+ "interface",
+ "let",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "static",
+ "var",
+ "yield",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, class_name, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassGetterParamNameErrors) {
+ const char* context_data[][2] = {
+ {"class C { get name(", ") {} }"},
+ {"(class { get name(", ") {} });"},
+ {"'use strict'; class C { get name(", ") {} }"},
+ {"'use strict'; (class { get name(", ") {} })"},
+ {NULL, NULL}
+ };
+
+ const char* class_name[] = {
+ "arguments",
+ "eval",
+ "implements",
+ "interface",
+ "let",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "static",
+ "var",
+ "yield",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, class_name, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassStaticPrototypeErrors) {
+ const char* context_data[][2] = {{"class C {", "}"},
+ {"(class {", "});"},
+ {NULL, NULL}};
+
+ const char* class_body_data[] = {
+ "static prototype() {}",
+ "static get prototype() {}",
+ "static set prototype(_) {}",
+ "static *prototype() {}",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassSpecialConstructorErrors) {
+ const char* context_data[][2] = {{"class C {", "}"},
+ {"(class {", "});"},
+ {NULL, NULL}};
+
+ const char* class_body_data[] = {
+ "get constructor() {}",
+ "get constructor(_) {}",
+ "*constructor() {}",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassConstructorNoErrors) {
+ const char* context_data[][2] = {{"class C {", "}"},
+ {"(class {", "});"},
+ {NULL, NULL}};
+
+ const char* class_body_data[] = {
+ "constructor() {}",
+ "static constructor() {}",
+ "static get constructor() {}",
+ "static set constructor(_) {}",
+ "static *constructor() {}",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, class_body_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassMultipleConstructorErrors) {
+ // We currently do not allow any duplicate properties in class bodies. This
+ // test ensures that when we change that we still throw on duplicate
+ // constructors.
+ const char* context_data[][2] = {{"class C {", "}"},
+ {"(class {", "});"},
+ {NULL, NULL}};
+
+ const char* class_body_data[] = {
+ "constructor() {}; constructor() {}",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+// TODO(arv): We should allow duplicate property names.
+// https://code.google.com/p/v8/issues/detail?id=3570
+DISABLED_TEST(ClassMultiplePropertyNamesNoErrors) {
+ const char* context_data[][2] = {{"class C {", "}"},
+ {"(class {", "});"},
+ {NULL, NULL}};
+
+ const char* class_body_data[] = {
+ "constructor() {}; static constructor() {}",
+ "m() {}; static m() {}",
+ "m() {}; m() {}",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, class_body_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ClassesAreStrictErrors) {
+ const char* context_data[][2] = {{"", ""},
+ {"(", ");"},
+ {NULL, NULL}};
+
+ const char* class_body_data[] = {
+ "class C { method() { with ({}) {} } }",
+ "class C extends function() { with ({}) {} } {}",
+ "class C { *method() { with ({}) {} } }",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowClasses,
+ kAllowHarmonyObjectLiterals
+ };
+ RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
}