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:
adamk 2015-01-27 13:06:36 -08:00 committed by Commit bot
parent 701c6e7475
commit aeb3a71740
10 changed files with 169 additions and 463 deletions

View File

@ -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__") \

View File

@ -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;

View File

@ -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(

View File

@ -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);

View File

@ -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) {

View File

@ -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],

View File

@ -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
};

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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],