From b89c0a962c017082a0c8c662050d68223b1617ce Mon Sep 17 00:00:00 2001 From: "rossberg@chromium.org" Date: Wed, 29 Feb 2012 12:12:52 +0000 Subject: [PATCH] AST extensions and parsing for import & export declarations. R=jkummerow@chromium.org BUG= TEST= Review URL: https://chromiumcodereview.appspot.com/9496003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10866 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ast.cc | 2 + src/ast.h | 59 +++++++++++ src/full-codegen.cc | 18 ++++ src/hydrogen.cc | 12 ++- src/parser.cc | 180 +++++++++++++++++++++++++++------ src/parser.h | 10 +- src/prettyprinter.cc | 29 ++++++ src/rewriter.cc | 2 + test/mjsunit/harmony/module-parsing.js | 69 ++++++++++++- 9 files changed, 340 insertions(+), 41 deletions(-) diff --git a/src/ast.cc b/src/ast.cc index 9810c34..b19fc35 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -1008,6 +1008,8 @@ CaseClause::CaseClause(Isolate* isolate, INCREASE_NODE_COUNT(VariableDeclaration) INCREASE_NODE_COUNT(FunctionDeclaration) INCREASE_NODE_COUNT(ModuleDeclaration) +INCREASE_NODE_COUNT(ImportDeclaration) +INCREASE_NODE_COUNT(ExportDeclaration) INCREASE_NODE_COUNT(ModuleLiteral) INCREASE_NODE_COUNT(ModuleVariable) INCREASE_NODE_COUNT(ModulePath) diff --git a/src/ast.h b/src/ast.h index d7819c3..5398775 100644 --- a/src/ast.h +++ b/src/ast.h @@ -63,6 +63,8 @@ namespace internal { V(VariableDeclaration) \ V(FunctionDeclaration) \ V(ModuleDeclaration) \ + V(ImportDeclaration) \ + V(ExportDeclaration) \ #define MODULE_NODE_LIST(V) \ V(ModuleLiteral) \ @@ -544,6 +546,48 @@ class ModuleDeclaration: public Declaration { }; +class ImportDeclaration: public Declaration { + public: + DECLARE_NODE_TYPE(ImportDeclaration) + + Module* module() const { return module_; } + virtual InitializationFlag initialization() const { + return kCreatedInitialized; + } + + protected: + template friend class AstNodeFactory; + + ImportDeclaration(VariableProxy* proxy, + Module* module, + Scope* scope) + : Declaration(proxy, LET, scope), + module_(module) { + } + + private: + Module* module_; +}; + + +class ExportDeclaration: public Declaration { + public: + DECLARE_NODE_TYPE(ExportDeclaration) + + virtual InitializationFlag initialization() const { + return kCreatedInitialized; + } + + protected: + template friend class AstNodeFactory; + + ExportDeclaration(VariableProxy* proxy, + Scope* scope) + : Declaration(proxy, LET, scope) { + } +}; + + class Module: public AstNode { // TODO(rossberg): stuff to come... protected: @@ -2580,6 +2624,21 @@ class AstNodeFactory BASE_EMBEDDED { VISIT_AND_RETURN(ModuleDeclaration, decl) } + ImportDeclaration* NewImportDeclaration(VariableProxy* proxy, + Module* module, + Scope* scope) { + ImportDeclaration* decl = + new(zone_) ImportDeclaration(proxy, module, scope); + VISIT_AND_RETURN(ImportDeclaration, decl) + } + + ExportDeclaration* NewExportDeclaration(VariableProxy* proxy, + Scope* scope) { + ExportDeclaration* decl = + new(zone_) ExportDeclaration(proxy, scope); + VISIT_AND_RETURN(ExportDeclaration, decl) + } + ModuleLiteral* NewModuleLiteral(Block* body) { ModuleLiteral* module = new(zone_) ModuleLiteral(body); VISIT_AND_RETURN(ModuleLiteral, module) diff --git a/src/full-codegen.cc b/src/full-codegen.cc index a37b3e4..5f3c1d2 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -63,6 +63,14 @@ void BreakableStatementChecker::VisitModuleDeclaration( ModuleDeclaration* decl) { } +void BreakableStatementChecker::VisitImportDeclaration( + ImportDeclaration* decl) { +} + +void BreakableStatementChecker::VisitExportDeclaration( + ExportDeclaration* decl) { +} + void BreakableStatementChecker::VisitModuleLiteral(ModuleLiteral* module) { } @@ -622,6 +630,16 @@ void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* decl) { } +void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* decl) { + EmitDeclaration(decl->proxy(), decl->mode(), NULL); +} + + +void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* decl) { + // TODO(rossberg) +} + + void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) { // TODO(rossberg) } diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 6413fad..bc048b3 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -6932,7 +6932,17 @@ void HGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* decl) { void HGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* decl) { - // TODO(rossberg) + UNREACHABLE(); +} + + +void HGraphBuilder::VisitImportDeclaration(ImportDeclaration* decl) { + UNREACHABLE(); +} + + +void HGraphBuilder::VisitExportDeclaration(ExportDeclaration* decl) { + UNREACHABLE(); } diff --git a/src/parser.cc b/src/parser.cc index 4c342f8..5f04cab 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1184,10 +1184,10 @@ Statement* Parser::ParseModuleElement(ZoneStringList* labels, switch (peek()) { case Token::FUNCTION: - return ParseFunctionDeclaration(ok); + return ParseFunctionDeclaration(NULL, ok); case Token::LET: case Token::CONST: - return ParseVariableStatement(kModuleElement, ok); + return ParseVariableStatement(kModuleElement, NULL, ok); case Token::IMPORT: return ParseImportDeclaration(ok); case Token::EXPORT: @@ -1205,7 +1205,7 @@ Statement* Parser::ParseModuleElement(ZoneStringList* labels, estmt->expression()->AsVariableProxy()->name()->Equals( isolate()->heap()->module_symbol()) && !scanner().literal_contains_escapes()) { - return ParseModuleDeclaration(ok); + return ParseModuleDeclaration(NULL, ok); } } return stmt; @@ -1214,7 +1214,7 @@ Statement* Parser::ParseModuleElement(ZoneStringList* labels, } -Block* Parser::ParseModuleDeclaration(bool* ok) { +Block* Parser::ParseModuleDeclaration(ZoneStringList* names, bool* ok) { // ModuleDeclaration: // 'module' Identifier Module @@ -1229,6 +1229,7 @@ Block* Parser::ParseModuleDeclaration(bool* ok) { // TODO(rossberg): Add initialization statement to block. + if (names) names->Add(name); return block; } @@ -1236,19 +1237,26 @@ Block* Parser::ParseModuleDeclaration(bool* ok) { Module* Parser::ParseModule(bool* ok) { // Module: // '{' ModuleElement '}' - // '=' ModulePath - // 'at' String + // '=' ModulePath ';' + // 'at' String ';' switch (peek()) { case Token::LBRACE: return ParseModuleLiteral(ok); - case Token::ASSIGN: + case Token::ASSIGN: { Expect(Token::ASSIGN, CHECK_OK); - return ParseModulePath(ok); + Module* result = ParseModulePath(CHECK_OK); + ExpectSemicolon(CHECK_OK); + return result; + } - default: - return ParseModuleUrl(ok); + default: { + ExpectContextualKeyword("at", CHECK_OK); + Module* result = ParseModuleUrl(CHECK_OK); + ExpectSemicolon(CHECK_OK); + return result; + } } } @@ -1317,31 +1325,122 @@ Module* Parser::ParseModuleVariable(bool* ok) { Module* Parser::ParseModuleUrl(bool* ok) { // Module: - // 'at' String + // String - Expect(Token::IDENTIFIER, CHECK_OK); - Handle symbol = GetSymbol(CHECK_OK); - if (!symbol->IsEqualTo(CStrVector("at"))) { - *ok = false; - ReportUnexpectedToken(scanner().current_token()); - return NULL; - } Expect(Token::STRING, CHECK_OK); - symbol = GetSymbol(CHECK_OK); + Handle symbol = GetSymbol(CHECK_OK); return factory()->NewModuleUrl(symbol); } +Module* Parser::ParseModuleSpecifier(bool* ok) { + // ModuleSpecifier: + // String + // ModulePath + + if (peek() == Token::STRING) { + return ParseModuleUrl(ok); + } else { + return ParseModulePath(ok); + } +} + + Block* Parser::ParseImportDeclaration(bool* ok) { - // TODO(rossberg) - return NULL; + // ImportDeclaration: + // 'import' IdentifierName (',' IdentifierName)* 'from' ModuleSpecifier ';' + // + // TODO(ES6): implement destructuring ImportSpecifiers + + Expect(Token::IMPORT, CHECK_OK); + ZoneStringList names(1); + + Handle name = ParseIdentifierName(CHECK_OK); + while (peek() == Token::COMMA) { + Consume(Token::COMMA); + name = ParseIdentifierName(CHECK_OK); + names.Add(name); + } + + ExpectContextualKeyword("from", CHECK_OK); + Module* module = ParseModuleSpecifier(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); + for (int i = 0; i < names.length(); ++i) { + VariableProxy* proxy = NewUnresolved(names[i], LET); + Declaration* declaration = + factory()->NewImportDeclaration(proxy, module, top_scope_); + Declare(declaration, true, CHECK_OK); + // TODO(rossberg): Add initialization statement to block. + } + + + return block; } -Block* Parser::ParseExportDeclaration(bool* ok) { - // TODO(rossberg) - return NULL; +Statement* Parser::ParseExportDeclaration(bool* ok) { + // ExportDeclaration: + // 'export' Identifier (',' Identifier)* ';' + // 'export' VariableDeclaration + // 'export' FunctionDeclaration + // 'export' ModuleDeclaration + // + // TODO(ES6): implement structuring ExportSpecifiers + + Expect(Token::EXPORT, CHECK_OK); + + Statement* result = NULL; + ZoneStringList names(1); + switch (peek()) { + case Token::IDENTIFIER: { + Handle name = ParseIdentifier(CHECK_OK); + // Handle 'module' as a context-sensitive keyword. + if (!name->IsEqualTo(CStrVector("module"))) { + names.Add(name); + while (peek() == Token::COMMA) { + Consume(Token::COMMA); + name = ParseIdentifier(CHECK_OK); + names.Add(name); + } + ExpectSemicolon(CHECK_OK); + result = factory()->NewEmptyStatement(); + } else { + result = ParseModuleDeclaration(&names, CHECK_OK); + } + break; + } + + case Token::FUNCTION: + result = ParseFunctionDeclaration(&names, CHECK_OK); + break; + + case Token::VAR: + case Token::LET: + case Token::CONST: + result = ParseVariableStatement(kModuleElement, &names, CHECK_OK); + break; + + default: + *ok = false; + ReportUnexpectedToken(scanner().current_token()); + return NULL; + } + + // Extract declared names into export declarations. + for (int i = 0; i < names.length(); ++i) { + VariableProxy* proxy = NewUnresolved(names[i], LET); + Declaration* declaration = + factory()->NewExportDeclaration(proxy, top_scope_); + top_scope_->AddDeclaration(declaration); + } + + ASSERT(result != NULL); + return result; } @@ -1359,10 +1458,10 @@ Statement* Parser::ParseBlockElement(ZoneStringList* labels, switch (peek()) { case Token::FUNCTION: - return ParseFunctionDeclaration(ok); + return ParseFunctionDeclaration(NULL, ok); case Token::LET: case Token::CONST: - return ParseVariableStatement(kModuleElement, ok); + return ParseVariableStatement(kModuleElement, NULL, ok); default: return ParseStatement(labels, ok); } @@ -1404,7 +1503,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { case Token::CONST: // fall through case Token::LET: case Token::VAR: - stmt = ParseVariableStatement(kStatement, ok); + stmt = ParseVariableStatement(kStatement, NULL, ok); break; case Token::SEMICOLON: @@ -1481,7 +1580,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { *ok = false; return NULL; } - return ParseFunctionDeclaration(ok); + return ParseFunctionDeclaration(NULL, ok); } case Token::DEBUGGER: @@ -1708,7 +1807,7 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) { } -Statement* Parser::ParseFunctionDeclaration(bool* ok) { +Statement* Parser::ParseFunctionDeclaration(ZoneStringList* names, bool* ok) { // FunctionDeclaration :: // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}' Expect(Token::FUNCTION, CHECK_OK); @@ -1729,6 +1828,7 @@ Statement* Parser::ParseFunctionDeclaration(bool* ok) { Declaration* declaration = factory()->NewFunctionDeclaration(proxy, mode, fun, top_scope_); Declare(declaration, true, CHECK_OK); + if (names) names->Add(name); return factory()->NewEmptyStatement(); } @@ -1795,13 +1895,14 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) { Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context, + ZoneStringList* names, bool* ok) { // VariableStatement :: // VariableDeclarations ';' Handle ignore; Block* result = - ParseVariableDeclarations(var_context, NULL, &ignore, CHECK_OK); + ParseVariableDeclarations(var_context, NULL, names, &ignore, CHECK_OK); ExpectSemicolon(CHECK_OK); return result; } @@ -1821,6 +1922,7 @@ bool Parser::IsEvalOrArguments(Handle string) { Block* Parser::ParseVariableDeclarations( VariableDeclarationContext var_context, VariableDeclarationProperties* decl_props, + ZoneStringList* names, Handle* out, bool* ok) { // VariableDeclarations :: @@ -1965,6 +2067,7 @@ Block* Parser::ParseVariableDeclarations( *ok = false; return NULL; } + if (names) names->Add(name); // Parse initialization expression if present and/or needed. A // declaration of the form: @@ -2617,7 +2720,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { if (peek() == Token::VAR || peek() == Token::CONST) { Handle name; Block* variable_statement = - ParseVariableDeclarations(kForStatement, NULL, &name, CHECK_OK); + ParseVariableDeclarations(kForStatement, NULL, NULL, &name, CHECK_OK); if (peek() == Token::IN && !name.is_null()) { VariableProxy* each = top_scope_->NewUnresolved(factory(), name); @@ -2646,7 +2749,8 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Handle name; VariableDeclarationProperties decl_props = kHasNoInitializers; Block* variable_statement = - ParseVariableDeclarations(kForStatement, &decl_props, &name, CHECK_OK); + ParseVariableDeclarations(kForStatement, &decl_props, NULL, &name, + CHECK_OK); bool accept_IN = !name.is_null() && decl_props != kHasInitializers; if (peek() == Token::IN && accept_IN) { // Rewrite a for-in statement of the form @@ -4576,6 +4680,18 @@ void Parser::ExpectSemicolon(bool* ok) { } +void Parser::ExpectContextualKeyword(const char* keyword, bool* ok) { + Expect(Token::IDENTIFIER, ok); + if (!*ok) return; + Handle symbol = GetSymbol(ok); + if (!*ok) return; + if (!symbol->IsEqualTo(CStrVector(keyword))) { + *ok = false; + ReportUnexpectedToken(scanner().current_token()); + } +} + + Literal* Parser::GetLiteralUndefined() { return factory()->NewLiteral(isolate()->factory()->undefined_value()); } diff --git a/src/parser.h b/src/parser.h index 4c18fe9..dea73a1 100644 --- a/src/parser.h +++ b/src/parser.h @@ -581,23 +581,26 @@ class Parser { void* ParseSourceElements(ZoneList* processor, int end_token, bool* ok); Statement* ParseModuleElement(ZoneStringList* labels, bool* ok); - Block* ParseModuleDeclaration(bool* ok); + Block* ParseModuleDeclaration(ZoneStringList* names, bool* ok); Module* ParseModule(bool* ok); Module* ParseModuleLiteral(bool* ok); Module* ParseModulePath(bool* ok); Module* ParseModuleVariable(bool* ok); Module* ParseModuleUrl(bool* ok); + Module* ParseModuleSpecifier(bool* ok); Block* ParseImportDeclaration(bool* ok); - Block* ParseExportDeclaration(bool* ok); + Statement* ParseExportDeclaration(bool* ok); Statement* ParseBlockElement(ZoneStringList* labels, bool* ok); Statement* ParseStatement(ZoneStringList* labels, bool* ok); - Statement* ParseFunctionDeclaration(bool* ok); + Statement* ParseFunctionDeclaration(ZoneStringList* names, bool* ok); Statement* ParseNativeDeclaration(bool* ok); Block* ParseBlock(ZoneStringList* labels, bool* ok); Block* ParseVariableStatement(VariableDeclarationContext var_context, + ZoneStringList* names, bool* ok); Block* ParseVariableDeclarations(VariableDeclarationContext var_context, VariableDeclarationProperties* decl_props, + ZoneStringList* names, Handle* out, bool* ok); Statement* ParseExpressionOrLabelledStatement(ZoneStringList* labels, @@ -700,6 +703,7 @@ class Parser { void Expect(Token::Value token, bool* ok); bool Check(Token::Value token); void ExpectSemicolon(bool* ok); + void ExpectContextualKeyword(const char* keyword, bool* ok); Handle LiteralString(PretenureFlag tenured) { if (scanner().is_literal_ascii()) { diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc index d879da1..0d8dadc 100644 --- a/src/prettyprinter.cc +++ b/src/prettyprinter.cc @@ -83,6 +83,22 @@ void PrettyPrinter::VisitModuleDeclaration(ModuleDeclaration* node) { } +void PrettyPrinter::VisitImportDeclaration(ImportDeclaration* node) { + Print("import "); + PrintLiteral(node->proxy()->name(), false); + Print(" from "); + Visit(node->module()); + Print(";"); +} + + +void PrettyPrinter::VisitExportDeclaration(ExportDeclaration* node) { + Print("export "); + PrintLiteral(node->proxy()->name(), false); + Print(";"); +} + + void PrettyPrinter::VisitModuleLiteral(ModuleLiteral* node) { VisitBlock(node->body()); } @@ -771,6 +787,19 @@ void AstPrinter::VisitModuleDeclaration(ModuleDeclaration* node) { } +void AstPrinter::VisitImportDeclaration(ImportDeclaration* node) { + IndentedScope indent(this, "IMPORT"); + PrintLiteralIndented("NAME", node->proxy()->name(), true); + Visit(node->module()); +} + + +void AstPrinter::VisitExportDeclaration(ExportDeclaration* node) { + IndentedScope indent(this, "EXPORT "); + PrintLiteral(node->proxy()->name(), true); +} + + void AstPrinter::VisitModuleLiteral(ModuleLiteral* node) { VisitBlock(node->body()); } diff --git a/src/rewriter.cc b/src/rewriter.cc index a92c3c3..55f93ee 100644 --- a/src/rewriter.cc +++ b/src/rewriter.cc @@ -212,6 +212,8 @@ void Processor::VisitWithStatement(WithStatement* node) { void Processor::VisitVariableDeclaration(VariableDeclaration* node) {} void Processor::VisitFunctionDeclaration(FunctionDeclaration* node) {} void Processor::VisitModuleDeclaration(ModuleDeclaration* node) {} +void Processor::VisitImportDeclaration(ImportDeclaration* node) {} +void Processor::VisitExportDeclaration(ExportDeclaration* node) {} void Processor::VisitModuleLiteral(ModuleLiteral* node) {} void Processor::VisitModuleVariable(ModuleVariable* node) {} void Processor::VisitModulePath(ModulePath* node) {} diff --git a/test/mjsunit/harmony/module-parsing.js b/test/mjsunit/harmony/module-parsing.js index 5a5e82f..cf56502 100644 --- a/test/mjsunit/harmony/module-parsing.js +++ b/test/mjsunit/harmony/module-parsing.js @@ -36,19 +36,39 @@ module A2 = A; module A3 = A2 module B { - var x - var x, y; - var x = 0, y - let x, y - let z = 1 + export x + export y, z, c, f + + var vx + var vx, vy; + var vx = 0, vy + let lx, ly + let lz = 1 const c = 9 function f() {} + module C { let x module D {} 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 from I + import i4, i5 from "http://where" } module C1 = B.C; @@ -76,6 +96,45 @@ module Z at "file://local" +import +x +, +y +from +"file://local" + +module Wrap { +export +x +, +y + +export +var +v1 = 1 + +export +let +v2 = 2 + +export +const +v3 = 3 + +export +function +f +( +) +{ +} + +export +module V +{ +} +} + // Check that 'module' still works as an identifier. -- 2.7.4