}
-Module* Parser::ParseModuleUrl(bool* ok) {
+Module* Parser::ParseModuleSpecifier(bool* ok) {
// Module:
// String
}
+void* Parser::ParseModuleDeclarationClause(ZoneList<const AstRawString*>* names,
+ bool* ok) {
+ // Handles both imports and exports:
+ //
+ // ImportOrExportClause :
+ // '{' '}'
+ // '{' ImportOrExportsList '}'
+ // '{' ImportOrExportsList ',' '}'
+ //
+ // ImportOrExportsList :
+ // ImportOrExportSpecifier
+ // ImportOrExportsList ',' ImportOrExportSpecifier
+ //
+ // ImportOrExportSpecifier :
+ // IdentifierName
+ // IdentifierName 'as' IdentifierName
+
+ Expect(Token::LBRACE, CHECK_OK);
+
+ while (peek() != Token::RBRACE) {
+ const AstRawString* name = ParseIdentifierName(CHECK_OK);
+ names->Add(name, zone());
+ const AstRawString* export_name = NULL;
+ if (CheckContextualKeyword(CStrVector("as"))) {
+ export_name = ParseIdentifierName(CHECK_OK);
+ }
+ // TODO(ES6): Return the export_name as well as the name.
+ USE(export_name);
+ if (peek() == Token::RBRACE) break;
+ Expect(Token::COMMA, CHECK_OK);
+ }
+
+ Expect(Token::RBRACE, CHECK_OK);
+
+ return 0;
+}
+
+
Statement* Parser::ParseImportDeclaration(bool* ok) {
// ImportDeclaration:
// 'import' IdentifierName (',' IdentifierName)* 'from' ModuleUrl ';'
}
ExpectContextualKeyword(CStrVector("from"), CHECK_OK);
- Module* module = ParseModuleUrl(CHECK_OK);
+ Module* module = ParseModuleSpecifier(CHECK_OK);
ExpectSemicolon(CHECK_OK);
- // TODO(ES6): Do something with ParseModuleUrl's return value.
+ // TODO(ES6): Do something with ParseModuleSpecifier's return value.
USE(module);
for (int i = 0; i < names.length(); ++i) {
}
+Statement* Parser::ParseExportDefault(bool* ok) {
+ // Supports the following productions, starting after the 'default' token:
+ // 'export' 'default' FunctionDeclaration
+ // 'export' 'default' ClassDeclaration
+ // 'export' 'default' AssignmentExpression[In] ';'
+
+ Statement* result = NULL;
+ switch (peek()) {
+ case Token::FUNCTION:
+ // TODO(ES6): Support parsing anonymous function declarations here.
+ result = ParseFunctionDeclaration(NULL, CHECK_OK);
+ break;
+
+ case Token::CLASS:
+ // TODO(ES6): Support parsing anonymous class declarations here.
+ result = ParseClassDeclaration(NULL, CHECK_OK);
+ break;
+
+ default: {
+ int pos = peek_position();
+ Expression* expr = ParseAssignmentExpression(true, CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+ result = factory()->NewExpressionStatement(expr, pos);
+ break;
+ }
+ }
+
+ // TODO(ES6): Add default export to scope_->interface()
+
+ return result;
+}
+
+
Statement* Parser::ParseExportDeclaration(bool* ok) {
// ExportDeclaration:
- // 'export' Identifier (',' Identifier)* ';'
- // 'export' VariableDeclaration
- // 'export' FunctionDeclaration
- // 'export' GeneratorDeclaration
- // 'export' ModuleDeclaration
- //
- // TODO(ES6): implement current syntax
+ // 'export' '*' 'from' ModuleSpecifier ';'
+ // 'export' ExportClause ('from' ModuleSpecifier)? ';'
+ // 'export' VariableStatement
+ // 'export' Declaration
+ // 'export' 'default' ... (handled in ParseExportDefault)
+ int pos = peek_position();
Expect(Token::EXPORT, CHECK_OK);
Statement* result = NULL;
ZoneList<const AstRawString*> names(1, zone());
+ bool is_export_from = false;
switch (peek()) {
- case Token::IDENTIFIER: {
- int pos = position();
- const AstRawString* name =
- ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
- names.Add(name, zone());
- while (peek() == Token::COMMA) {
- Consume(Token::COMMA);
- name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
- names.Add(name, zone());
- }
+ case Token::DEFAULT:
+ Consume(Token::DEFAULT);
+ return ParseExportDefault(ok);
+
+ case Token::MUL: {
+ Consume(Token::MUL);
+ ExpectContextualKeyword(CStrVector("from"), CHECK_OK);
+ Module* module = ParseModuleSpecifier(CHECK_OK);
ExpectSemicolon(CHECK_OK);
+ // TODO(ES6): Do something with the return value
+ // of ParseModuleSpecifier.
+ USE(module);
+ is_export_from = true;
result = factory()->NewEmptyStatement(pos);
break;
}
+ case Token::LBRACE:
+ ParseModuleDeclarationClause(&names, CHECK_OK);
+ if (CheckContextualKeyword(CStrVector("from"))) {
+ Module* module = ParseModuleSpecifier(CHECK_OK);
+ // TODO(ES6): Do something with the return value
+ // of ParseModuleSpecifier.
+ USE(module);
+ is_export_from = true;
+ }
+ ExpectSemicolon(CHECK_OK);
+ result = factory()->NewEmptyStatement(pos);
+ break;
+
case Token::FUNCTION:
result = ParseFunctionDeclaration(&names, CHECK_OK);
break;
}
}
- // Extract declared names into export declarations and interface.
- Interface* interface = scope_->interface();
- for (int i = 0; i < names.length(); ++i) {
+ // TODO(ES6): Handle 'export from' once imports are properly implemented.
+ // For now we just drop such exports on the floor.
+ if (!is_export_from) {
+ // Extract declared names into export declarations and interface.
+ Interface* interface = scope_->interface();
+ for (int i = 0; i < names.length(); ++i) {
#ifdef DEBUG
- if (FLAG_print_interface_details)
- PrintF("# Export %.*s ", names[i]->length(), names[i]->raw_data());
+ if (FLAG_print_interface_details)
+ PrintF("# Export %.*s ", names[i]->length(), names[i]->raw_data());
#endif
- Interface* inner = Interface::NewUnknown(zone());
- interface->Add(names[i], inner, zone(), CHECK_OK);
- if (!*ok)
- return NULL;
- VariableProxy* proxy = NewUnresolved(names[i], LET, inner);
- USE(proxy);
- // TODO(rossberg): Rethink whether we actually need to store export
- // declarations (for compilation?).
- // ExportDeclaration* declaration =
- // factory()->NewExportDeclaration(proxy, scope_, position);
- // scope_->AddDeclaration(declaration);
+ Interface* inner = Interface::NewUnknown(zone());
+ interface->Add(names[i], inner, zone(), CHECK_OK);
+ if (!*ok) return NULL;
+ VariableProxy* proxy = NewUnresolved(names[i], LET, inner);
+ USE(proxy);
+ // TODO(rossberg): Rethink whether we actually need to store export
+ // declarations (for compilation?).
+ // ExportDeclaration* declaration =
+ // factory()->NewExportDeclaration(proxy, scope_, position);
+ // scope_->AddDeclaration(declaration);
+ }
}
DCHECK(result != NULL);
Statement* ParseStatementListItem(bool* ok);
Module* ParseModule(bool* ok);
Statement* ParseModuleItem(bool* ok);
- Module* ParseModuleUrl(bool* ok);
+ Module* ParseModuleSpecifier(bool* ok);
Statement* ParseImportDeclaration(bool* ok);
Statement* ParseExportDeclaration(bool* ok);
+ Statement* ParseExportDefault(bool* ok);
+ void* ParseModuleDeclarationClause(ZoneList<const AstRawString*>* names,
+ bool* ok);
Statement* ParseStatement(ZoneList<const AstRawString*>* labels, bool* ok);
Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names,
bool* ok);
TEST(BasicImportExportParsing) {
- const char kSource[] =
- "export let x = 0;"
- "import y from 'http://module.com/foo.js';"
- "function f() {};"
- "f();";
+ const char* kSources[] = {
+ "export let x = 0;",
+ "export var y = 0;",
+ "export const z = 0;",
+ "export function func() { };",
+ "export class C { };",
+ "export { };",
+ "function f() {}; f(); export { f };",
+ "var a, b, c; export { a, b as baz, c };",
+ "var d, e; export { d as dreary, e, };",
+ "import y from 'http://module.com/foo.js';",
+ "export default function f() {}",
+ "export default class C {}",
+ "export default 42",
+ "var x; export default x = 7",
+ "export { Q } from 'somemodule.js';",
+ "export * from 'somemodule.js';"
+ };
i::Isolate* isolate = CcTest::i_isolate();
i::Factory* factory = isolate->factory();
isolate->stack_guard()->SetStackLimit(i::GetCurrentStackPosition() -
128 * 1024);
- int kProgramByteSize = i::StrLength(kSource);
- i::ScopedVector<char> program(kProgramByteSize + 1);
- i::SNPrintF(program, "%s", kSource);
- i::Handle<i::String> source =
- factory->NewStringFromUtf8(i::CStrVector(program.start()))
- .ToHandleChecked();
+ for (unsigned i = 0; i < arraysize(kSources); ++i) {
+ int kProgramByteSize = i::StrLength(kSources[i]);
+ i::ScopedVector<char> program(kProgramByteSize + 1);
+ i::SNPrintF(program, "%s", kSources[i]);
+ i::Handle<i::String> source =
+ factory->NewStringFromUtf8(i::CStrVector(program.start()))
+ .ToHandleChecked();
- // Show that parsing as a module works
- {
- 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_modules(true);
- parser.set_allow_harmony_scoping(true);
- info.MarkAsModule();
- CHECK(parser.Parse());
+ // Show that parsing as a module works
+ {
+ 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_classes(true);
+ parser.set_allow_harmony_modules(true);
+ parser.set_allow_harmony_scoping(true);
+ info.MarkAsModule();
+ CHECK(parser.Parse());
+ }
+
+ // And that parsing a script does not.
+ {
+ 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_classes(true);
+ parser.set_allow_harmony_modules(true);
+ parser.set_allow_harmony_scoping(true);
+ info.MarkAsGlobal();
+ CHECK(!parser.Parse());
+ }
}
+}
+
+
+TEST(ImportExportParsingErrors) {
+ const char* kErrorSources[] = {
+ "export {",
+ "var a; export { a",
+ "var a; export { a,",
+ "var a; export { a, ;",
+ "var a; export { a as };",
+ "var a, b; export { a as , b};",
+ "export }",
+ "var foo, bar; export { foo bar };",
+ "export { foo };",
+ "export { , };",
+ "export default;",
+ "export default var x = 7;",
+ "export default let x = 7;",
+ "export default const x = 7;",
+ "export *;",
+ "export * from;",
+ "export { Q } from;",
+ "export default from 'module.js';",
+
+ // TODO(ES6): These two forms should be supported
+ "export default function() {};",
+ "export default class {};"
+ };
+
+ 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);
+
+ isolate->stack_guard()->SetStackLimit(i::GetCurrentStackPosition() -
+ 128 * 1024);
+
+ for (unsigned i = 0; i < arraysize(kErrorSources); ++i) {
+ int kProgramByteSize = i::StrLength(kErrorSources[i]);
+ i::ScopedVector<char> program(kProgramByteSize + 1);
+ i::SNPrintF(program, "%s", kErrorSources[i]);
+ i::Handle<i::String> source =
+ factory->NewStringFromUtf8(i::CStrVector(program.start()))
+ .ToHandleChecked();
- // And that parsing a script does not.
- {
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_classes(true);
parser.set_allow_harmony_modules(true);
parser.set_allow_harmony_scoping(true);
- info.MarkAsGlobal();
+ info.MarkAsModule();
CHECK(!parser.Parse());
}
}
// Flags: --harmony-modules
// Check that import/export declarations are rejected in eval or local scope.
-assertThrows("export x;", SyntaxError);
assertThrows("export let x;", SyntaxError);
assertThrows("import x from 'http://url';", SyntaxError);
-assertThrows("{ export x; }", SyntaxError);
assertThrows("{ export let x; }", SyntaxError);
assertThrows("{ import x from 'http://url'; }", SyntaxError);
-assertThrows("function f() { export x; }", SyntaxError);
assertThrows("function f() { export let x; }", SyntaxError);
assertThrows("function f() { import x from 'http://url'; }", SyntaxError);
-assertThrows("function f() { { export x; } }", SyntaxError);
assertThrows("function f() { { export let x; } }", SyntaxError);
assertThrows("function f() { { import x from 'http://url'; } }", SyntaxError);