Begin modernization of --harmony-modules
The approach taken in this CL is to incrementally move toward the currently-specced version of modules in ES6. The biggest change in this patch is separating the parsing of modules from the parsing of scripts, getting rid of the 'module' keyword and thus disallowing modules-in-scripts as well as modules-in-modules. The syntax supported by import/export declarations has not yet been significantly changed, with the major exception being that import declarations require a string as the 'from' part. Most of the existing tests have been disabled, with a first new test added in cctest/test-parsing. BUG=v8:1569 LOG=n Review URL: https://codereview.chromium.org/881623002 Cr-Commit-Position: refs/heads/master@{#26299}
This commit is contained in:
parent
701c6e7475
commit
aeb3a71740
@ -253,7 +253,6 @@ class AstValue : public ZoneObject {
|
||||
F(make_reference_error, "MakeReferenceErrorEmbedded") \
|
||||
F(make_syntax_error, "MakeSyntaxErrorEmbedded") \
|
||||
F(make_type_error, "MakeTypeErrorEmbedded") \
|
||||
F(module, "module") \
|
||||
F(native, "native") \
|
||||
F(next, "next") \
|
||||
F(proto, "__proto__") \
|
||||
|
@ -89,7 +89,8 @@ class CompilationInfo {
|
||||
kInliningEnabled = 1 << 17,
|
||||
kTypingEnabled = 1 << 18,
|
||||
kDisableFutureOptimization = 1 << 19,
|
||||
kToplevel = 1 << 20
|
||||
kModule = 1 << 20,
|
||||
kToplevel = 1 << 21
|
||||
};
|
||||
|
||||
CompilationInfo(Handle<JSFunction> closure, Zone* zone);
|
||||
@ -105,6 +106,7 @@ class CompilationInfo {
|
||||
bool is_lazy() const { return GetFlag(kLazy); }
|
||||
bool is_eval() const { return GetFlag(kEval); }
|
||||
bool is_global() const { return GetFlag(kGlobal); }
|
||||
bool is_module() const { return GetFlag(kModule); }
|
||||
StrictMode strict_mode() const {
|
||||
return GetFlag(kStrictMode) ? STRICT : SLOPPY;
|
||||
}
|
||||
@ -146,6 +148,11 @@ class CompilationInfo {
|
||||
SetFlag(kGlobal);
|
||||
}
|
||||
|
||||
void MarkAsModule() {
|
||||
DCHECK(!is_lazy());
|
||||
SetFlag(kModule);
|
||||
}
|
||||
|
||||
void set_parameter_count(int parameter_count) {
|
||||
DCHECK(IsStub());
|
||||
parameter_count_ = parameter_count;
|
||||
|
370
src/parser.cc
370
src/parser.cc
@ -265,6 +265,7 @@ void Parser::SetCachedData() {
|
||||
|
||||
Scope* Parser::NewScope(Scope* parent, ScopeType scope_type) {
|
||||
DCHECK(ast_value_factory());
|
||||
DCHECK(scope_type != MODULE_SCOPE || allow_harmony_modules());
|
||||
Scope* result = new (zone())
|
||||
Scope(isolate(), zone(), parent, scope_type, ast_value_factory());
|
||||
result->Initialize();
|
||||
@ -934,8 +935,17 @@ FunctionLiteral* Parser::DoParseProgram(CompilationInfo* info, Scope** scope,
|
||||
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16, zone());
|
||||
bool ok = true;
|
||||
int beg_pos = scanner()->location().beg_pos;
|
||||
ParseSourceElements(body, Token::EOS, info->is_eval(), true, eval_scope,
|
||||
&ok);
|
||||
if (info->is_module()) {
|
||||
DCHECK(allow_harmony_modules());
|
||||
Module* module = ParseModule(&ok);
|
||||
if (ok) {
|
||||
// TODO(adamk): Do something with returned Module
|
||||
CHECK(module);
|
||||
body->Add(factory()->NewEmptyStatement(RelocInfo::kNoPosition), zone());
|
||||
}
|
||||
} else {
|
||||
ParseStatementList(body, Token::EOS, info->is_eval(), eval_scope, &ok);
|
||||
}
|
||||
|
||||
if (ok && strict_mode() == STRICT) {
|
||||
CheckStrictOctalLiteral(beg_pos, scanner()->location().end_pos, &ok);
|
||||
@ -1080,11 +1090,10 @@ FunctionLiteral* Parser::ParseLazy(Utf16CharacterStream* source) {
|
||||
}
|
||||
|
||||
|
||||
void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
|
||||
int end_token, bool is_eval, bool is_global,
|
||||
Scope** eval_scope, bool* ok) {
|
||||
// SourceElements ::
|
||||
// (ModuleElement)* <end_token>
|
||||
void* Parser::ParseStatementList(ZoneList<Statement*>* body, int end_token,
|
||||
bool is_eval, Scope** eval_scope, bool* ok) {
|
||||
// StatementList ::
|
||||
// (StatementListItem)* <end_token>
|
||||
|
||||
// Allocate a target stack to use for this set of source
|
||||
// elements. This way, all scripts and functions get their own
|
||||
@ -1092,7 +1101,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
|
||||
// functions.
|
||||
TargetScope scope(&this->target_stack_);
|
||||
|
||||
DCHECK(processor != NULL);
|
||||
DCHECK(body != NULL);
|
||||
bool directive_prologue = true; // Parsing directive prologue.
|
||||
|
||||
while (peek() != end_token) {
|
||||
@ -1101,12 +1110,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
|
||||
}
|
||||
|
||||
Scanner::Location token_loc = scanner()->peek_location();
|
||||
Statement* stat;
|
||||
if (is_global && !is_eval) {
|
||||
stat = ParseModuleElement(NULL, CHECK_OK);
|
||||
} else {
|
||||
stat = ParseBlockElement(NULL, CHECK_OK);
|
||||
}
|
||||
Statement* stat = ParseStatementListItem(CHECK_OK);
|
||||
if (stat == NULL || stat->IsEmpty()) {
|
||||
directive_prologue = false; // End of directive prologue.
|
||||
continue;
|
||||
@ -1162,134 +1166,64 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
|
||||
}
|
||||
}
|
||||
|
||||
processor->Add(stat, zone());
|
||||
body->Add(stat, zone());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Statement* Parser::ParseModuleElement(ZoneList<const AstRawString*>* labels,
|
||||
bool* ok) {
|
||||
// (Ecma 262 5th Edition, clause 14):
|
||||
// SourceElement:
|
||||
Statement* Parser::ParseStatementListItem(bool* ok) {
|
||||
// (Ecma 262 6th Edition, 13.1):
|
||||
// StatementListItem:
|
||||
// Statement
|
||||
// FunctionDeclaration
|
||||
//
|
||||
// In harmony mode we allow additionally the following productions
|
||||
// ModuleElement:
|
||||
// LetDeclaration
|
||||
// ConstDeclaration
|
||||
// ModuleDeclaration
|
||||
// ImportDeclaration
|
||||
// ExportDeclaration
|
||||
// GeneratorDeclaration
|
||||
// Declaration
|
||||
|
||||
switch (peek()) {
|
||||
case Token::FUNCTION:
|
||||
return ParseFunctionDeclaration(NULL, ok);
|
||||
case Token::CLASS:
|
||||
return ParseClassDeclaration(NULL, ok);
|
||||
case Token::CONST:
|
||||
case Token::VAR:
|
||||
return ParseVariableStatement(kStatementListItem, NULL, ok);
|
||||
case Token::LET:
|
||||
DCHECK(allow_harmony_scoping());
|
||||
if (strict_mode() == STRICT) {
|
||||
return ParseVariableStatement(kStatementListItem, NULL, ok);
|
||||
}
|
||||
// Fall through.
|
||||
default:
|
||||
return ParseStatement(NULL, ok);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Statement* Parser::ParseModuleItem(bool* ok) {
|
||||
// (Ecma 262 6th Edition, 15.2):
|
||||
// ModuleItem :
|
||||
// ImportDeclaration
|
||||
// ExportDeclaration
|
||||
// StatementListItem
|
||||
|
||||
switch (peek()) {
|
||||
case Token::IMPORT:
|
||||
return ParseImportDeclaration(ok);
|
||||
case Token::EXPORT:
|
||||
return ParseExportDeclaration(ok);
|
||||
case Token::CONST:
|
||||
return ParseVariableStatement(kModuleElement, NULL, ok);
|
||||
case Token::LET:
|
||||
DCHECK(allow_harmony_scoping());
|
||||
if (strict_mode() == STRICT) {
|
||||
return ParseVariableStatement(kModuleElement, NULL, ok);
|
||||
}
|
||||
// Fall through.
|
||||
default: {
|
||||
Statement* stmt = ParseStatement(labels, CHECK_OK);
|
||||
// Handle 'module' as a context-sensitive keyword.
|
||||
if (FLAG_harmony_modules &&
|
||||
peek() == Token::IDENTIFIER &&
|
||||
!scanner()->HasAnyLineTerminatorBeforeNext() &&
|
||||
stmt != NULL) {
|
||||
ExpressionStatement* estmt = stmt->AsExpressionStatement();
|
||||
if (estmt != NULL && estmt->expression()->AsVariableProxy() != NULL &&
|
||||
estmt->expression()->AsVariableProxy()->raw_name() ==
|
||||
ast_value_factory()->module_string() &&
|
||||
!scanner()->literal_contains_escapes()) {
|
||||
return ParseModuleDeclaration(NULL, ok);
|
||||
}
|
||||
}
|
||||
return stmt;
|
||||
}
|
||||
default:
|
||||
return ParseStatementListItem(ok);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Statement* Parser::ParseModuleDeclaration(ZoneList<const AstRawString*>* names,
|
||||
bool* ok) {
|
||||
// ModuleDeclaration:
|
||||
// 'module' Identifier Module
|
||||
|
||||
int pos = peek_position();
|
||||
const AstRawString* name =
|
||||
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (FLAG_print_interface_details)
|
||||
PrintF("# Module %.*s ", name->length(), name->raw_data());
|
||||
#endif
|
||||
|
||||
Module* module = ParseModule(CHECK_OK);
|
||||
VariableProxy* proxy = NewUnresolved(name, MODULE, module->interface());
|
||||
Declaration* declaration =
|
||||
factory()->NewModuleDeclaration(proxy, module, scope_, pos);
|
||||
Declare(declaration, true, CHECK_OK);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (FLAG_print_interface_details)
|
||||
PrintF("# Module %.*s ", name->length(), name->raw_data());
|
||||
if (FLAG_print_interfaces) {
|
||||
PrintF("module %.*s: ", name->length(), name->raw_data());
|
||||
module->interface()->Print();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (names) names->Add(name, zone());
|
||||
if (module->body() == NULL)
|
||||
return factory()->NewEmptyStatement(pos);
|
||||
else
|
||||
return factory()->NewModuleStatement(proxy, module->body(), pos);
|
||||
}
|
||||
|
||||
|
||||
Module* Parser::ParseModule(bool* ok) {
|
||||
// Module:
|
||||
// '{' ModuleElement '}'
|
||||
// '=' ModulePath ';'
|
||||
// 'at' String ';'
|
||||
|
||||
switch (peek()) {
|
||||
case Token::LBRACE:
|
||||
return ParseModuleLiteral(ok);
|
||||
|
||||
case Token::ASSIGN: {
|
||||
Expect(Token::ASSIGN, CHECK_OK);
|
||||
Module* result = ParseModulePath(CHECK_OK);
|
||||
ExpectSemicolon(CHECK_OK);
|
||||
return result;
|
||||
}
|
||||
|
||||
default: {
|
||||
ExpectContextualKeyword(CStrVector("at"), CHECK_OK);
|
||||
Module* result = ParseModuleUrl(CHECK_OK);
|
||||
ExpectSemicolon(CHECK_OK);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Module* Parser::ParseModuleLiteral(bool* ok) {
|
||||
// Module:
|
||||
// '{' ModuleElement '}'
|
||||
// (Ecma 262 6th Edition, 15.2):
|
||||
// Module :
|
||||
// ModuleBody?
|
||||
//
|
||||
// ModuleBody :
|
||||
// ModuleItem*
|
||||
|
||||
int pos = peek_position();
|
||||
// Construct block expecting 16 statements.
|
||||
@ -1299,7 +1233,6 @@ Module* Parser::ParseModuleLiteral(bool* ok) {
|
||||
#endif
|
||||
Scope* scope = NewScope(scope_, MODULE_SCOPE);
|
||||
|
||||
Expect(Token::LBRACE, CHECK_OK);
|
||||
scope->set_start_position(scanner()->location().beg_pos);
|
||||
scope->SetStrictMode(STRICT);
|
||||
|
||||
@ -1307,15 +1240,14 @@ Module* Parser::ParseModuleLiteral(bool* ok) {
|
||||
BlockState block_state(&scope_, scope);
|
||||
Target target(&this->target_stack_, body);
|
||||
|
||||
while (peek() != Token::RBRACE) {
|
||||
Statement* stat = ParseModuleElement(NULL, CHECK_OK);
|
||||
while (peek() != Token::EOS) {
|
||||
Statement* stat = ParseModuleItem(CHECK_OK);
|
||||
if (stat && !stat->IsEmpty()) {
|
||||
body->AddStatement(stat, zone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expect(Token::RBRACE, CHECK_OK);
|
||||
scope->set_end_position(scanner()->location().end_pos);
|
||||
body->set_scope(scope);
|
||||
|
||||
@ -1338,60 +1270,6 @@ Module* Parser::ParseModuleLiteral(bool* ok) {
|
||||
}
|
||||
|
||||
|
||||
Module* Parser::ParseModulePath(bool* ok) {
|
||||
// ModulePath:
|
||||
// Identifier
|
||||
// ModulePath '.' Identifier
|
||||
|
||||
int pos = peek_position();
|
||||
Module* result = ParseModuleVariable(CHECK_OK);
|
||||
while (Check(Token::PERIOD)) {
|
||||
const AstRawString* name = ParseIdentifierName(CHECK_OK);
|
||||
#ifdef DEBUG
|
||||
if (FLAG_print_interface_details)
|
||||
PrintF("# Path .%.*s ", name->length(), name->raw_data());
|
||||
#endif
|
||||
Module* member = factory()->NewModulePath(result, name, pos);
|
||||
result->interface()->Add(name, member->interface(), zone(), ok);
|
||||
if (!*ok) {
|
||||
#ifdef DEBUG
|
||||
if (FLAG_print_interfaces) {
|
||||
PrintF("PATH TYPE ERROR at '%.*s'\n", name->length(), name->raw_data());
|
||||
PrintF("result: ");
|
||||
result->interface()->Print();
|
||||
PrintF("member: ");
|
||||
member->interface()->Print();
|
||||
}
|
||||
#endif
|
||||
ParserTraits::ReportMessage("invalid_module_path", name);
|
||||
return NULL;
|
||||
}
|
||||
result = member;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Module* Parser::ParseModuleVariable(bool* ok) {
|
||||
// ModulePath:
|
||||
// Identifier
|
||||
|
||||
int pos = peek_position();
|
||||
const AstRawString* name =
|
||||
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
|
||||
#ifdef DEBUG
|
||||
if (FLAG_print_interface_details)
|
||||
PrintF("# Module variable %.*s ", name->length(), name->raw_data());
|
||||
#endif
|
||||
VariableProxy* proxy = scope_->NewUnresolved(
|
||||
factory(), name, Interface::NewModule(zone()),
|
||||
scanner()->location().beg_pos);
|
||||
|
||||
return factory()->NewModuleVariable(proxy, pos);
|
||||
}
|
||||
|
||||
|
||||
Module* Parser::ParseModuleUrl(bool* ok) {
|
||||
// Module:
|
||||
// String
|
||||
@ -1421,24 +1299,11 @@ Module* Parser::ParseModuleUrl(bool* ok) {
|
||||
}
|
||||
|
||||
|
||||
Module* Parser::ParseModuleSpecifier(bool* ok) {
|
||||
// ModuleSpecifier:
|
||||
// String
|
||||
// ModulePath
|
||||
|
||||
if (peek() == Token::STRING) {
|
||||
return ParseModuleUrl(ok);
|
||||
} else {
|
||||
return ParseModulePath(ok);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Block* Parser::ParseImportDeclaration(bool* ok) {
|
||||
Statement* Parser::ParseImportDeclaration(bool* ok) {
|
||||
// ImportDeclaration:
|
||||
// 'import' IdentifierName (',' IdentifierName)* 'from' ModuleSpecifier ';'
|
||||
// 'import' IdentifierName (',' IdentifierName)* 'from' ModuleUrl ';'
|
||||
//
|
||||
// TODO(ES6): implement destructuring ImportSpecifiers
|
||||
// TODO(ES6): implement current syntax
|
||||
|
||||
int pos = peek_position();
|
||||
Expect(Token::IMPORT, CHECK_OK);
|
||||
@ -1453,38 +1318,17 @@ Block* Parser::ParseImportDeclaration(bool* ok) {
|
||||
}
|
||||
|
||||
ExpectContextualKeyword(CStrVector("from"), CHECK_OK);
|
||||
Module* module = ParseModuleSpecifier(CHECK_OK);
|
||||
Module* module = ParseModuleUrl(CHECK_OK);
|
||||
ExpectSemicolon(CHECK_OK);
|
||||
|
||||
// Generate a separate declaration for each identifier.
|
||||
// TODO(ES6): once we implement destructuring, make that one declaration.
|
||||
Block* block = factory()->NewBlock(NULL, 1, true, RelocInfo::kNoPosition);
|
||||
// TODO(ES6): Do something with ParseModuleUrl's return value.
|
||||
USE(module);
|
||||
|
||||
for (int i = 0; i < names.length(); ++i) {
|
||||
#ifdef DEBUG
|
||||
if (FLAG_print_interface_details)
|
||||
PrintF("# Import %.*s ", name->length(), name->raw_data());
|
||||
#endif
|
||||
Interface* interface = Interface::NewUnknown(zone());
|
||||
module->interface()->Add(names[i], interface, zone(), ok);
|
||||
if (!*ok) {
|
||||
#ifdef DEBUG
|
||||
if (FLAG_print_interfaces) {
|
||||
PrintF("IMPORT TYPE ERROR at '%.*s'\n", name->length(),
|
||||
name->raw_data());
|
||||
PrintF("module: ");
|
||||
module->interface()->Print();
|
||||
}
|
||||
#endif
|
||||
ParserTraits::ReportMessage("invalid_module_path", name);
|
||||
return NULL;
|
||||
}
|
||||
VariableProxy* proxy = NewUnresolved(names[i], LET, interface);
|
||||
Declaration* declaration =
|
||||
factory()->NewImportDeclaration(proxy, module, scope_, pos);
|
||||
Declare(declaration, true, CHECK_OK);
|
||||
// TODO(ES6): Add an appropriate declaration for each name
|
||||
}
|
||||
|
||||
return block;
|
||||
return factory()->NewEmptyStatement(pos);
|
||||
}
|
||||
|
||||
|
||||
@ -1496,7 +1340,7 @@ Statement* Parser::ParseExportDeclaration(bool* ok) {
|
||||
// 'export' GeneratorDeclaration
|
||||
// 'export' ModuleDeclaration
|
||||
//
|
||||
// TODO(ES6): implement structuring ExportSpecifiers
|
||||
// TODO(ES6): implement current syntax
|
||||
|
||||
Expect(Token::EXPORT, CHECK_OK);
|
||||
|
||||
@ -1507,19 +1351,14 @@ Statement* Parser::ParseExportDeclaration(bool* ok) {
|
||||
int pos = position();
|
||||
const AstRawString* name =
|
||||
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
|
||||
// Handle 'module' as a context-sensitive keyword.
|
||||
if (name != ast_value_factory()->module_string()) {
|
||||
names.Add(name, zone());
|
||||
while (peek() == Token::COMMA) {
|
||||
Consume(Token::COMMA);
|
||||
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());
|
||||
}
|
||||
ExpectSemicolon(CHECK_OK);
|
||||
result = factory()->NewEmptyStatement(pos);
|
||||
} else {
|
||||
result = ParseModuleDeclaration(&names, CHECK_OK);
|
||||
}
|
||||
ExpectSemicolon(CHECK_OK);
|
||||
result = factory()->NewEmptyStatement(pos);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1534,7 +1373,7 @@ Statement* Parser::ParseExportDeclaration(bool* ok) {
|
||||
case Token::VAR:
|
||||
case Token::LET:
|
||||
case Token::CONST:
|
||||
result = ParseVariableStatement(kModuleElement, &names, CHECK_OK);
|
||||
result = ParseVariableStatement(kStatementListItem, &names, CHECK_OK);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1581,39 +1420,6 @@ Statement* Parser::ParseExportDeclaration(bool* ok) {
|
||||
}
|
||||
|
||||
|
||||
Statement* Parser::ParseBlockElement(ZoneList<const AstRawString*>* labels,
|
||||
bool* ok) {
|
||||
// (Ecma 262 5th Edition, clause 14):
|
||||
// SourceElement:
|
||||
// Statement
|
||||
// FunctionDeclaration
|
||||
//
|
||||
// In harmony mode we allow additionally the following productions
|
||||
// BlockElement (aka SourceElement):
|
||||
// LetDeclaration
|
||||
// ConstDeclaration
|
||||
// GeneratorDeclaration
|
||||
// ClassDeclaration
|
||||
|
||||
switch (peek()) {
|
||||
case Token::FUNCTION:
|
||||
return ParseFunctionDeclaration(NULL, ok);
|
||||
case Token::CLASS:
|
||||
return ParseClassDeclaration(NULL, ok);
|
||||
case Token::CONST:
|
||||
return ParseVariableStatement(kModuleElement, NULL, ok);
|
||||
case Token::LET:
|
||||
DCHECK(allow_harmony_scoping());
|
||||
if (strict_mode() == STRICT) {
|
||||
return ParseVariableStatement(kModuleElement, NULL, ok);
|
||||
}
|
||||
// Fall through.
|
||||
default:
|
||||
return ParseStatement(labels, ok);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels,
|
||||
bool* ok) {
|
||||
// Statement ::
|
||||
@ -2067,7 +1873,7 @@ Block* Parser::ParseScopedBlock(ZoneList<const AstRawString*>* labels,
|
||||
Target target(&this->target_stack_, body);
|
||||
|
||||
while (peek() != Token::RBRACE) {
|
||||
Statement* stat = ParseBlockElement(NULL, CHECK_OK);
|
||||
Statement* stat = ParseStatementListItem(CHECK_OK);
|
||||
if (stat && !stat->IsEmpty()) {
|
||||
body->AddStatement(stat, zone());
|
||||
}
|
||||
@ -2482,24 +2288,16 @@ Statement* Parser::ParseExpressionOrLabelledStatement(
|
||||
return ParseNativeDeclaration(ok);
|
||||
}
|
||||
|
||||
// Parsed expression statement, or the context-sensitive 'module' keyword.
|
||||
// Only expect semicolon in the former case.
|
||||
// Also detect attempts at 'let' declarations in sloppy mode.
|
||||
if (!FLAG_harmony_modules || peek() != Token::IDENTIFIER ||
|
||||
scanner()->HasAnyLineTerminatorBeforeNext() ||
|
||||
expr->AsVariableProxy() == NULL ||
|
||||
expr->AsVariableProxy()->raw_name() !=
|
||||
ast_value_factory()->module_string() ||
|
||||
scanner()->literal_contains_escapes()) {
|
||||
if (peek() == Token::IDENTIFIER && expr->AsVariableProxy() != NULL &&
|
||||
expr->AsVariableProxy()->raw_name() ==
|
||||
ast_value_factory()->let_string()) {
|
||||
ReportMessage("sloppy_lexical", NULL);
|
||||
*ok = false;
|
||||
return NULL;
|
||||
}
|
||||
ExpectSemicolon(CHECK_OK);
|
||||
// Parsed expression statement, followed by semicolon.
|
||||
// Detect attempts at 'let' declarations in sloppy mode.
|
||||
if (peek() == Token::IDENTIFIER && expr->AsVariableProxy() != NULL &&
|
||||
expr->AsVariableProxy()->raw_name() ==
|
||||
ast_value_factory()->let_string()) {
|
||||
ReportMessage("sloppy_lexical", NULL);
|
||||
*ok = false;
|
||||
return NULL;
|
||||
}
|
||||
ExpectSemicolon(CHECK_OK);
|
||||
return factory()->NewExpressionStatement(expr, pos);
|
||||
}
|
||||
|
||||
@ -3913,7 +3711,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
|
||||
yield, RelocInfo::kNoPosition), zone());
|
||||
}
|
||||
|
||||
ParseSourceElements(body, Token::RBRACE, false, false, NULL, CHECK_OK);
|
||||
ParseStatementList(body, Token::RBRACE, false, NULL, CHECK_OK);
|
||||
|
||||
if (is_generator) {
|
||||
VariableProxy* get_proxy = factory()->NewVariableProxy(
|
||||
|
20
src/parser.h
20
src/parser.h
@ -696,7 +696,7 @@ class Parser : public ParserBase<ParserTraits> {
|
||||
static const int kMaxNumFunctionLocals = 4194303; // 2^22-1
|
||||
|
||||
enum VariableDeclarationContext {
|
||||
kModuleElement,
|
||||
kStatementListItem,
|
||||
kStatement,
|
||||
kForStatement
|
||||
};
|
||||
@ -746,22 +746,14 @@ class Parser : public ParserBase<ParserTraits> {
|
||||
// which is set to false if parsing failed; it is unchanged otherwise.
|
||||
// By making the 'exception handling' explicit, we are forced to check
|
||||
// for failure at the call sites.
|
||||
void* ParseSourceElements(ZoneList<Statement*>* processor, int end_token,
|
||||
bool is_eval, bool is_global,
|
||||
Scope** ad_hoc_eval_scope, bool* ok);
|
||||
Statement* ParseModuleElement(ZoneList<const AstRawString*>* labels,
|
||||
bool* ok);
|
||||
Statement* ParseModuleDeclaration(ZoneList<const AstRawString*>* names,
|
||||
bool* ok);
|
||||
void* ParseStatementList(ZoneList<Statement*>* processor, int end_token,
|
||||
bool is_eval, Scope** ad_hoc_eval_scope, bool* ok);
|
||||
Statement* ParseStatementListItem(bool* ok);
|
||||
Module* ParseModule(bool* ok);
|
||||
Module* ParseModuleLiteral(bool* ok);
|
||||
Module* ParseModulePath(bool* ok);
|
||||
Module* ParseModuleVariable(bool* ok);
|
||||
Statement* ParseModuleItem(bool* ok);
|
||||
Module* ParseModuleUrl(bool* ok);
|
||||
Module* ParseModuleSpecifier(bool* ok);
|
||||
Block* ParseImportDeclaration(bool* ok);
|
||||
Statement* ParseImportDeclaration(bool* ok);
|
||||
Statement* ParseExportDeclaration(bool* ok);
|
||||
Statement* ParseBlockElement(ZoneList<const AstRawString*>* labels, bool* ok);
|
||||
Statement* ParseStatement(ZoneList<const AstRawString*>* labels, bool* ok);
|
||||
Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names,
|
||||
bool* ok);
|
||||
|
@ -77,10 +77,8 @@ Scope::Scope(Isolate* isolate, Zone* zone, Scope* outer_scope,
|
||||
params_(4, zone),
|
||||
unresolved_(16, zone),
|
||||
decls_(4, zone),
|
||||
interface_(FLAG_harmony_modules && (scope_type == MODULE_SCOPE ||
|
||||
scope_type == SCRIPT_SCOPE)
|
||||
? Interface::NewModule(zone)
|
||||
: NULL),
|
||||
interface_(scope_type == MODULE_SCOPE ? Interface::NewModule(zone)
|
||||
: NULL),
|
||||
already_resolved_(false),
|
||||
ast_value_factory_(ast_value_factory),
|
||||
zone_(zone) {
|
||||
|
@ -63,6 +63,9 @@
|
||||
# are actually 13 * 38 * 5 * 128 = 316160 individual tests hidden here.
|
||||
'test-parsing/ParserSync': [PASS, NO_VARIANTS],
|
||||
|
||||
# Modules are busted
|
||||
'test-parsing/ExportsMaybeAssigned': [SKIP],
|
||||
|
||||
# This tests only the type system, so there is no point in running several
|
||||
# variants.
|
||||
'test-hydrogen-types/*': [PASS, NO_VARIANTS],
|
||||
|
@ -676,7 +676,6 @@ TEST(CrossScriptReferences_Simple2) {
|
||||
|
||||
TEST(CrossScriptReferencesHarmony) {
|
||||
i::FLAG_harmony_scoping = true;
|
||||
i::FLAG_harmony_modules = true;
|
||||
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
@ -687,7 +686,6 @@ TEST(CrossScriptReferencesHarmony) {
|
||||
"'use strict'; function x() { return 1 }; x()", "x()",
|
||||
"'use strict'; let x = 1; x", "x",
|
||||
"'use strict'; const x = 1; x", "x",
|
||||
"'use strict'; module x { export let a = 1 }; x.a", "x.a",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -823,7 +821,6 @@ TEST(CrossScriptReferencesHarmony) {
|
||||
TEST(GlobalLexicalOSR) {
|
||||
i::FLAG_use_strict = true;
|
||||
i::FLAG_harmony_scoping = true;
|
||||
i::FLAG_harmony_modules = true;
|
||||
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
@ -848,7 +845,6 @@ TEST(GlobalLexicalOSR) {
|
||||
TEST(CrossScriptConflicts) {
|
||||
i::FLAG_use_strict = true;
|
||||
i::FLAG_harmony_scoping = true;
|
||||
i::FLAG_harmony_modules = true;
|
||||
|
||||
HandleScope scope(CcTest::isolate());
|
||||
|
||||
@ -857,7 +853,6 @@ TEST(CrossScriptConflicts) {
|
||||
"function x() { return 1 }; x()",
|
||||
"let x = 1; x",
|
||||
"const x = 1; x",
|
||||
"module x { export let a = 1 }; x.a",
|
||||
NULL
|
||||
};
|
||||
const char* seconds[] = {
|
||||
@ -865,7 +860,6 @@ TEST(CrossScriptConflicts) {
|
||||
"function x() { return 2 }; x()",
|
||||
"let x = 2; x",
|
||||
"const x = 2; x",
|
||||
"module x { export let a = 2 }; x.a",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -4668,3 +4668,57 @@ TEST(ComputedPropertyNameShorthandError) {
|
||||
RunParserSyncTest(context_data, error_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
}
|
||||
|
||||
|
||||
TEST(BasicImportExportParsing) {
|
||||
const char kSource[] =
|
||||
"export let x = 0;"
|
||||
"import y from 'http://module.com/foo.js';"
|
||||
"function f() {};"
|
||||
"f();";
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
// 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_modules(true);
|
||||
parser.set_allow_harmony_scoping(true);
|
||||
info.MarkAsGlobal();
|
||||
CHECK(!parser.Parse());
|
||||
}
|
||||
}
|
||||
|
@ -27,164 +27,19 @@
|
||||
|
||||
// Flags: --harmony-modules
|
||||
|
||||
// Test basic module syntax, with and without automatic semicolon insertion.
|
||||
|
||||
module A {}
|
||||
|
||||
module A1 = A
|
||||
module A2 = A;
|
||||
module A3 = A2
|
||||
|
||||
module B {
|
||||
export vx
|
||||
export vy, lz, c, f
|
||||
|
||||
var vx
|
||||
var vx, vy;
|
||||
var vx = 0, vy
|
||||
let lx, ly
|
||||
let lz = 1
|
||||
const c = 9
|
||||
function f() {}
|
||||
|
||||
module C0 {}
|
||||
|
||||
export module C {
|
||||
let x
|
||||
export module D { export let x }
|
||||
let y
|
||||
}
|
||||
|
||||
let zz = ""
|
||||
|
||||
export var x0
|
||||
export var x1, x2 = 6, x3
|
||||
export let y0
|
||||
export let y1 = 0, y2
|
||||
export const z0 = 0
|
||||
export const z1 = 2, z2 = 3
|
||||
export function f0() {}
|
||||
export module M1 {}
|
||||
export module M2 = C.D
|
||||
export module M3 at "http://where"
|
||||
|
||||
import i0 from I
|
||||
import i1, i2, i3, M from I
|
||||
//import i4, i5 from "http://where"
|
||||
}
|
||||
|
||||
module I {
|
||||
export let i0, i1, i2, i3;
|
||||
export module M {}
|
||||
}
|
||||
|
||||
module C1 = B.C;
|
||||
module D1 = B.C.D
|
||||
module D2 = C1.D
|
||||
module D3 = D2
|
||||
|
||||
module E1 at "http://where"
|
||||
module E2 at "http://where";
|
||||
module E3 = E1
|
||||
|
||||
// Check that ASI does not interfere.
|
||||
|
||||
module X
|
||||
{
|
||||
let x
|
||||
}
|
||||
|
||||
module Y
|
||||
=
|
||||
X
|
||||
|
||||
module Z
|
||||
at
|
||||
"file://local"
|
||||
|
||||
import
|
||||
vx
|
||||
,
|
||||
vy
|
||||
from
|
||||
B
|
||||
|
||||
|
||||
module Wrap {
|
||||
export
|
||||
x
|
||||
,
|
||||
y
|
||||
|
||||
var
|
||||
x
|
||||
,
|
||||
y
|
||||
|
||||
export
|
||||
var
|
||||
v1 = 1
|
||||
|
||||
export
|
||||
let
|
||||
v2 = 2
|
||||
|
||||
export
|
||||
const
|
||||
v3 = 3
|
||||
|
||||
export
|
||||
function
|
||||
f
|
||||
(
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
export
|
||||
module V
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
export A, A1, A2, A3, B, I, C1, D1, D2, D3, E1, E2, E3, X, Y, Z, Wrap, x, y, UU
|
||||
|
||||
|
||||
|
||||
// Check that 'module' still works as an identifier.
|
||||
|
||||
var module
|
||||
module = {}
|
||||
module["a"] = 6
|
||||
function module() {}
|
||||
function f(module) { return module }
|
||||
try {} catch (module) {}
|
||||
|
||||
module
|
||||
v = 20
|
||||
|
||||
|
||||
|
||||
// Check that module declarations are rejected in eval or local scope.
|
||||
|
||||
module M { export let x; }
|
||||
|
||||
assertThrows("export x;", SyntaxError); // It's using eval, so should throw.
|
||||
// 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 M;", SyntaxError);
|
||||
assertThrows("module M {};", SyntaxError);
|
||||
assertThrows("import x from 'http://url';", SyntaxError);
|
||||
|
||||
assertThrows("{ export x; }", SyntaxError);
|
||||
assertThrows("{ export let x; }", SyntaxError);
|
||||
assertThrows("{ import x from M; }", SyntaxError);
|
||||
assertThrows("{ module M {}; }", 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 M; }", SyntaxError);
|
||||
assertThrows("function f() { module M {}; }", 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 M; } }", SyntaxError);
|
||||
assertThrows("function f() { { module M {}; } }", SyntaxError);
|
||||
assertThrows("function f() { { import x from 'http://url'; } }", SyntaxError);
|
||||
|
@ -80,6 +80,12 @@
|
||||
'regress/regress-2185-2': [PASS, NO_VARIANTS],
|
||||
'regress/regress-2612': [PASS, NO_VARIANTS],
|
||||
|
||||
# Modules are busted
|
||||
'harmony/module-linking': [SKIP],
|
||||
'harmony/module-recompile': [SKIP],
|
||||
'harmony/module-resolution': [SKIP],
|
||||
'harmony/regress/regress-343928': [SKIP],
|
||||
|
||||
# Issue 3660: Replacing activated TurboFan frames by unoptimized code does
|
||||
# not work, but we expect it to not crash.
|
||||
'debug-step-turbofan': [PASS, FAIL],
|
||||
|
Loading…
Reference in New Issue
Block a user