[modules] Support parsing anonymous default exports

This includes anonymous Function, Generator, and Class declarations when
preceded by 'export default'. Parsing only at the moment, nothing useful is
done with the parsed Function/ClassLiteral.

BUG=v8:1569
LOG=n

Review URL: https://codereview.chromium.org/1589173002

Cr-Commit-Position: refs/heads/master@{#33344}
This commit is contained in:
adamk 2016-01-15 12:38:16 -08:00 committed by Commit bot
parent b099e861ef
commit 25532be593
3 changed files with 70 additions and 19 deletions

View File

@ -1275,6 +1275,7 @@ Statement* Parser::ParseStatementListItem(bool* ok) {
scope_->set_class_declaration_group_start( scope_->set_class_declaration_group_start(
scanner()->peek_location().beg_pos); scanner()->peek_location().beg_pos);
} }
Consume(Token::CLASS);
return ParseClassDeclaration(NULL, ok); return ParseClassDeclaration(NULL, ok);
case Token::CONST: case Token::CONST:
if (allow_const()) { if (allow_const()) {
@ -1559,17 +1560,47 @@ Statement* Parser::ParseExportDefault(bool* ok) {
Expect(Token::DEFAULT, CHECK_OK); Expect(Token::DEFAULT, CHECK_OK);
Scanner::Location default_loc = scanner()->location(); Scanner::Location default_loc = scanner()->location();
const AstRawString* default_string = ast_value_factory()->default_string();
ZoneList<const AstRawString*> names(1, zone()); ZoneList<const AstRawString*> names(1, zone());
Statement* result = NULL; Statement* result = nullptr;
Expression* default_export = nullptr;
switch (peek()) { switch (peek()) {
case Token::FUNCTION: case Token::FUNCTION: {
// TODO(ES6): Support parsing anonymous function declarations here. Consume(Token::FUNCTION);
result = ParseFunctionDeclaration(&names, CHECK_OK); int pos = position();
bool is_generator = Check(Token::MUL);
if (peek() == Token::LPAREN) {
// FunctionDeclaration[+Default] ::
// 'function' '(' FormalParameters ')' '{' FunctionBody '}'
//
// GeneratorDeclaration[+Default] ::
// 'function' '*' '(' FormalParameters ')' '{' FunctionBody '}'
default_export = ParseFunctionLiteral(
default_string, Scanner::Location::invalid(),
kSkipFunctionNameCheck,
is_generator ? FunctionKind::kGeneratorFunction
: FunctionKind::kNormalFunction,
pos, FunctionLiteral::kDeclaration, FunctionLiteral::kNormalArity,
language_mode(), CHECK_OK);
result = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
} else {
result = ParseFunctionDeclaration(pos, is_generator, &names, CHECK_OK);
}
break; break;
}
case Token::CLASS: case Token::CLASS:
// TODO(ES6): Support parsing anonymous class declarations here. Consume(Token::CLASS);
if (peek() == Token::EXTENDS || peek() == Token::LBRACE) {
// ClassDeclaration[+Default] ::
// 'class' ('extends' LeftHandExpression)? '{' ClassBody '}'
default_export =
ParseClassLiteral(default_string, Scanner::Location::invalid(),
false, position(), CHECK_OK);
result = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
} else {
result = ParseClassDeclaration(&names, CHECK_OK); result = ParseClassDeclaration(&names, CHECK_OK);
}
break; break;
default: { default: {
@ -1584,19 +1615,18 @@ Statement* Parser::ParseExportDefault(bool* ok) {
} }
} }
const AstRawString* default_string = ast_value_factory()->default_string();
DCHECK_LE(names.length(), 1); DCHECK_LE(names.length(), 1);
if (names.length() == 1) { if (names.length() == 1) {
scope_->module()->AddLocalExport(default_string, names.first(), zone(), ok); scope_->module()->AddLocalExport(default_string, names.first(), zone(), ok);
if (!*ok) { if (!*ok) {
ParserTraits::ReportMessageAt( ParserTraits::ReportMessageAt(
default_loc, MessageTemplate::kDuplicateExport, default_string); default_loc, MessageTemplate::kDuplicateExport, default_string);
return NULL; return nullptr;
} }
} else { } else {
// TODO(ES6): Assign result to a const binding with the name "*default*" // TODO(ES6): Assign result to a const binding with the name "*default*"
// and add an export entry with "*default*" as the local name. // and add an export entry with "*default*" as the local name.
USE(default_export);
} }
return result; return result;
@ -1687,6 +1717,7 @@ Statement* Parser::ParseExportDeclaration(bool* ok) {
break; break;
case Token::CLASS: case Token::CLASS:
Consume(Token::CLASS);
result = ParseClassDeclaration(&names, CHECK_OK); result = ParseClassDeclaration(&names, CHECK_OK);
break; break;
@ -2090,14 +2121,22 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) {
Statement* Parser::ParseFunctionDeclaration( Statement* Parser::ParseFunctionDeclaration(
ZoneList<const AstRawString*>* names, bool* ok) { ZoneList<const AstRawString*>* names, bool* ok) {
// FunctionDeclaration ::
// 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
// GeneratorDeclaration ::
// 'function' '*' Identifier '(' FormalParameterListopt ')'
// '{' FunctionBody '}'
Expect(Token::FUNCTION, CHECK_OK); Expect(Token::FUNCTION, CHECK_OK);
int pos = position(); int pos = position();
bool is_generator = Check(Token::MUL); bool is_generator = Check(Token::MUL);
return ParseFunctionDeclaration(pos, is_generator, names, ok);
}
Statement* Parser::ParseFunctionDeclaration(
int pos, bool is_generator, ZoneList<const AstRawString*>* names,
bool* ok) {
// FunctionDeclaration ::
// 'function' Identifier '(' FormalParameters ')' '{' FunctionBody '}'
// GeneratorDeclaration ::
// 'function' '*' Identifier '(' FormalParameters ')' '{' FunctionBody '}'
//
// 'function' and '*' (if present) have been consumed by the caller.
bool is_strict_reserved = false; bool is_strict_reserved = false;
const AstRawString* name = ParseIdentifierOrStrictReservedWord( const AstRawString* name = ParseIdentifierOrStrictReservedWord(
&is_strict_reserved, CHECK_OK); &is_strict_reserved, CHECK_OK);
@ -2148,6 +2187,8 @@ Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names,
// ClassDeclaration :: // ClassDeclaration ::
// 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}' // 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}'
// //
// 'class' is expected to be consumed by the caller.
//
// A ClassDeclaration // A ClassDeclaration
// //
// class C { ... } // class C { ... }
@ -2158,7 +2199,6 @@ Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names,
// //
// so rewrite it as such. // so rewrite it as such.
Expect(Token::CLASS, CHECK_OK);
if (!allow_harmony_sloppy() && is_sloppy(language_mode())) { if (!allow_harmony_sloppy() && is_sloppy(language_mode())) {
ReportMessage(MessageTemplate::kSloppyLexical); ReportMessage(MessageTemplate::kSloppyLexical);
*ok = false; *ok = false;

View File

@ -744,6 +744,9 @@ class Parser : public ParserBase<ParserTraits> {
bool* ok); bool* ok);
Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names, Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names,
bool* ok); bool* ok);
Statement* ParseFunctionDeclaration(int pos, bool is_generator,
ZoneList<const AstRawString*>* names,
bool* ok);
Statement* ParseClassDeclaration(ZoneList<const AstRawString*>* names, Statement* ParseClassDeclaration(ZoneList<const AstRawString*>* names,
bool* ok); bool* ok);
Statement* ParseNativeDeclaration(bool* ok); Statement* ParseNativeDeclaration(bool* ok);

View File

@ -5539,6 +5539,7 @@ TEST(ComputedPropertyNameShorthandError) {
TEST(BasicImportExportParsing) { TEST(BasicImportExportParsing) {
i::FLAG_harmony_modules = true; i::FLAG_harmony_modules = true;
// clang-format off
const char* kSources[] = { const char* kSources[] = {
"export let x = 0;", "export let x = 0;",
"export var y = 0;", "export var y = 0;",
@ -5550,7 +5551,11 @@ TEST(BasicImportExportParsing) {
"var a, b, c; export { a, b as baz, c };", "var a, b, c; export { a, b as baz, c };",
"var d, e; export { d as dreary, e, };", "var d, e; export { d as dreary, e, };",
"export default function f() {}", "export default function f() {}",
"export default function() {}",
"export default function*() {}",
"export default class C {}", "export default class C {}",
"export default class {}"
"export default class extends C {}"
"export default 42", "export default 42",
"var x; export default x = 7", "var x; export default x = 7",
"export { Q } from 'somemodule.js';", "export { Q } from 'somemodule.js';",
@ -5577,6 +5582,7 @@ TEST(BasicImportExportParsing) {
"import { static as s } from 'm.js';", "import { static as s } from 'm.js';",
"import { let as l } from 'm.js';", "import { let as l } from 'm.js';",
}; };
// clang-format on
i::Isolate* isolate = CcTest::i_isolate(); i::Isolate* isolate = CcTest::i_isolate();
i::Factory* factory = isolate->factory(); i::Factory* factory = isolate->factory();
@ -5633,6 +5639,7 @@ TEST(BasicImportExportParsing) {
TEST(ImportExportParsingErrors) { TEST(ImportExportParsingErrors) {
i::FLAG_harmony_modules = true; i::FLAG_harmony_modules = true;
// clang-format off
const char* kErrorSources[] = { const char* kErrorSources[] = {
"export {", "export {",
"var a; export { a", "var a; export { a",
@ -5661,6 +5668,10 @@ TEST(ImportExportParsingErrors) {
"var a, b; export { a as c, b as c };", "var a, b; export { a as c, b as c };",
"export default function f(){}; export default class C {};", "export default function f(){}; export default class C {};",
"export default function f(){}; var a; export { a as default };", "export default function f(){}; var a; export { a as default };",
"export function() {}",
"export function*() {}",
"export class {}",
"export class extends C {}",
"import from;", "import from;",
"import from 'm.js';", "import from 'm.js';",
@ -5689,11 +5700,8 @@ TEST(ImportExportParsingErrors) {
"import * as x, * as y from 'm.js';", "import * as x, * as y from 'm.js';",
"import {x}, {y} from 'm.js';", "import {x}, {y} from 'm.js';",
"import * as x, {y} from 'm.js';", "import * as x, {y} from 'm.js';",
// TODO(ES6): These two forms should be supported
"export default function() {};",
"export default class {};"
}; };
// clang-format on
i::Isolate* isolate = CcTest::i_isolate(); i::Isolate* isolate = CcTest::i_isolate();
i::Factory* factory = isolate->factory(); i::Factory* factory = isolate->factory();