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

View File

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

View File

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