From ab26fb6b21a10ff5f94588213b00f2ed2ef981b4 Mon Sep 17 00:00:00 2001 From: "rossberg@chromium.org" Date: Mon, 16 Apr 2012 14:43:27 +0000 Subject: [PATCH] Implement rudimentary module linking. Constructs the (generally cyclic) graph of module instance objects and populates their exports. Any exports other than nested modules are currently set to 'undefined' (but already present as properties). Details: - Added new type JSModule for instance objects: a JSObject carrying a context. - Statically allocate instance objects for all module literals (in parser 8-}). - Extend interfaces to record and unify concrete instance objects, and to support iteration over members. - Introduce new runtime function for pushing module contexts. - Generate code for allocating, initializing, and setting module contexts, and for populating instance objects from module literals. Currently, all non-module exports are still initialized with 'undefined'. - Module aliases are resolved statically, so no special code is required. - Make sure that code containing module constructs is never optimized (macrofy AST node construction flag setting while we're at it). - Add test case checking linkage. Baseline: http://codereview.chromium.org/9722043/ R=svenpanne@chromium.org,mstarzinger@chromium.org BUG= TEST= Review URL: https://chromiumcodereview.appspot.com/9844002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11336 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/full-codegen-arm.cc | 30 +++-- src/ast.cc | 201 +++++++++++------------------- src/ast.h | 10 +- src/factory.cc | 23 +++- src/factory.h | 12 +- src/full-codegen.cc | 81 ++++++++++-- src/full-codegen.h | 6 +- src/heap.cc | 26 ++++ src/heap.h | 6 + src/hydrogen.cc | 46 ++----- src/ia32/full-codegen-ia32.cc | 29 +++-- src/interface.cc | 13 +- src/interface.h | 46 ++++++- src/objects-debug.cc | 12 ++ src/objects-inl.h | 23 +++- src/objects-printer.cc | 19 ++- src/objects-visiting.cc | 1 + src/objects.cc | 1 + src/objects.h | 34 +++++ src/parser.cc | 29 +++-- src/runtime.cc | 21 +++- src/runtime.h | 1 + src/scopeinfo.cc | 4 +- src/scopes.cc | 5 +- src/x64/full-codegen-x64.cc | 29 +++-- test/mjsunit/harmony/module-linking.js | 121 ++++++++++++++++++ test/mjsunit/harmony/module-parsing.js | 10 +- test/mjsunit/harmony/module-resolution.js | 2 +- 28 files changed, 596 insertions(+), 245 deletions(-) create mode 100644 test/mjsunit/harmony/module-linking.js diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 54a6e25..e7555cb 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -806,10 +806,10 @@ void FullCodeGenerator::VisitVariableDeclaration( bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET; switch (variable->location()) { case Variable::UNALLOCATED: - globals_.Add(variable->name()); - globals_.Add(variable->binding_needs_init() - ? isolate()->factory()->the_hole_value() - : isolate()->factory()->undefined_value()); + globals_->Add(variable->name()); + globals_->Add(variable->binding_needs_init() + ? isolate()->factory()->the_hole_value() + : isolate()->factory()->undefined_value()); break; case Variable::PARAMETER: @@ -865,12 +865,12 @@ void FullCodeGenerator::VisitFunctionDeclaration( Variable* variable = proxy->var(); switch (variable->location()) { case Variable::UNALLOCATED: { - globals_.Add(variable->name()); + globals_->Add(variable->name()); Handle function = Compiler::BuildFunctionInfo(declaration->fun(), script()); // Check for stack-overflow exception. if (function.is_null()) return SetStackOverflow(); - globals_.Add(function); + globals_->Add(function); break; } @@ -918,15 +918,24 @@ void FullCodeGenerator::VisitFunctionDeclaration( void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) { VariableProxy* proxy = declaration->proxy(); Variable* variable = proxy->var(); + Handle instance = declaration->module()->interface()->Instance(); + ASSERT(!instance.is_null()); + switch (variable->location()) { - case Variable::UNALLOCATED: - // TODO(rossberg): initialize module instance object + case Variable::UNALLOCATED: { + Comment cmnt(masm_, "[ ModuleDeclaration"); + globals_->Add(variable->name()); + globals_->Add(instance); + Visit(declaration->module()); break; + } case Variable::CONTEXT: { Comment cmnt(masm_, "[ ModuleDeclaration"); EmitDebugCheckDeclarationContext(variable); - // TODO(rossberg): initialize module instance object + __ mov(r1, Operand(instance)); + __ str(r1, ContextOperand(cp, variable->index())); + Visit(declaration->module()); break; } @@ -4536,7 +4545,8 @@ void FullCodeGenerator::LoadContextField(Register dst, int context_index) { void FullCodeGenerator::PushFunctionArgumentForContextAllocation() { Scope* declaration_scope = scope()->DeclarationScope(); - if (declaration_scope->is_global_scope()) { + if (declaration_scope->is_global_scope() || + declaration_scope->is_module_scope()) { // Contexts nested in the global context have a canonical empty function // as their closure, not the anonymous closure containing the global // code. Pass a smi sentinel and let the runtime look up the empty diff --git a/src/ast.cc b/src/ast.cc index 4b6ae68..22645a8 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -993,138 +993,78 @@ CaseClause::CaseClause(Isolate* isolate, } -#define INCREASE_NODE_COUNT(NodeType) \ +#define REGULAR_NODE(NodeType) \ void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \ increase_node_count(); \ } +#define DONT_OPTIMIZE_NODE(NodeType) \ + void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \ + increase_node_count(); \ + add_flag(kDontOptimize); \ + add_flag(kDontInline); \ + add_flag(kDontSelfOptimize); \ + } +#define DONT_INLINE_NODE(NodeType) \ + void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \ + increase_node_count(); \ + add_flag(kDontInline); \ + } +#define DONT_SELFOPTIMIZE_NODE(NodeType) \ + void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \ + increase_node_count(); \ + add_flag(kDontSelfOptimize); \ + } -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) -INCREASE_NODE_COUNT(ModuleUrl) -INCREASE_NODE_COUNT(Block) -INCREASE_NODE_COUNT(ExpressionStatement) -INCREASE_NODE_COUNT(EmptyStatement) -INCREASE_NODE_COUNT(IfStatement) -INCREASE_NODE_COUNT(ContinueStatement) -INCREASE_NODE_COUNT(BreakStatement) -INCREASE_NODE_COUNT(ReturnStatement) -INCREASE_NODE_COUNT(Conditional) -INCREASE_NODE_COUNT(Literal) -INCREASE_NODE_COUNT(ObjectLiteral) -INCREASE_NODE_COUNT(Assignment) -INCREASE_NODE_COUNT(Throw) -INCREASE_NODE_COUNT(Property) -INCREASE_NODE_COUNT(UnaryOperation) -INCREASE_NODE_COUNT(CountOperation) -INCREASE_NODE_COUNT(BinaryOperation) -INCREASE_NODE_COUNT(CompareOperation) -INCREASE_NODE_COUNT(ThisFunction) -INCREASE_NODE_COUNT(Call) -INCREASE_NODE_COUNT(CallNew) - -#undef INCREASE_NODE_COUNT - - -void AstConstructionVisitor::VisitWithStatement(WithStatement* node) { - increase_node_count(); - add_flag(kDontOptimize); - add_flag(kDontInline); -} - - -void AstConstructionVisitor::VisitSwitchStatement(SwitchStatement* node) { - increase_node_count(); - add_flag(kDontInline); -} - - -void AstConstructionVisitor::VisitDoWhileStatement(DoWhileStatement* node) { - increase_node_count(); - add_flag(kDontSelfOptimize); -} - - -void AstConstructionVisitor::VisitWhileStatement(WhileStatement* node) { - increase_node_count(); - add_flag(kDontSelfOptimize); -} - - -void AstConstructionVisitor::VisitForStatement(ForStatement* node) { - increase_node_count(); - add_flag(kDontSelfOptimize); -} - - -void AstConstructionVisitor::VisitForInStatement(ForInStatement* node) { - increase_node_count(); - add_flag(kDontSelfOptimize); -} - - -void AstConstructionVisitor::VisitTryCatchStatement(TryCatchStatement* node) { - increase_node_count(); - add_flag(kDontOptimize); - add_flag(kDontInline); -} - - -void AstConstructionVisitor::VisitTryFinallyStatement( - TryFinallyStatement* node) { - increase_node_count(); - add_flag(kDontOptimize); - add_flag(kDontInline); -} - - -void AstConstructionVisitor::VisitDebuggerStatement(DebuggerStatement* node) { - increase_node_count(); - add_flag(kDontOptimize); - add_flag(kDontInline); -} - - -void AstConstructionVisitor::VisitFunctionLiteral(FunctionLiteral* node) { - increase_node_count(); - add_flag(kDontInline); -} - - -void AstConstructionVisitor::VisitSharedFunctionInfoLiteral( - SharedFunctionInfoLiteral* node) { - increase_node_count(); - add_flag(kDontOptimize); - add_flag(kDontInline); -} - - -void AstConstructionVisitor::VisitVariableProxy(VariableProxy* node) { - increase_node_count(); - // In theory, we'd have to add: - // if(node->var()->IsLookupSlot()) { add_flag(kDontInline); } - // However, node->var() is usually not bound yet at VariableProxy creation - // time, and LOOKUP variables only result from constructs that cannot - // be inlined anyway. -} - - -void AstConstructionVisitor::VisitRegExpLiteral(RegExpLiteral* node) { - increase_node_count(); - add_flag(kDontInline); // TODO(1322): Allow materialized literals. -} - - -void AstConstructionVisitor::VisitArrayLiteral(ArrayLiteral* node) { - increase_node_count(); - add_flag(kDontInline); // TODO(1322): Allow materialized literals. -} - +REGULAR_NODE(VariableDeclaration) +REGULAR_NODE(FunctionDeclaration) +REGULAR_NODE(Block) +REGULAR_NODE(ExpressionStatement) +REGULAR_NODE(EmptyStatement) +REGULAR_NODE(IfStatement) +REGULAR_NODE(ContinueStatement) +REGULAR_NODE(BreakStatement) +REGULAR_NODE(ReturnStatement) +REGULAR_NODE(Conditional) +REGULAR_NODE(Literal) +REGULAR_NODE(ObjectLiteral) +REGULAR_NODE(Assignment) +REGULAR_NODE(Throw) +REGULAR_NODE(Property) +REGULAR_NODE(UnaryOperation) +REGULAR_NODE(CountOperation) +REGULAR_NODE(BinaryOperation) +REGULAR_NODE(CompareOperation) +REGULAR_NODE(ThisFunction) +REGULAR_NODE(Call) +REGULAR_NODE(CallNew) +// In theory, for VariableProxy we'd have to add: +// if (node->var()->IsLookupSlot()) add_flag(kDontInline); +// But node->var() is usually not bound yet at VariableProxy creation time, and +// LOOKUP variables only result from constructs that cannot be inlined anyway. +REGULAR_NODE(VariableProxy) + +DONT_OPTIMIZE_NODE(ModuleDeclaration) +DONT_OPTIMIZE_NODE(ImportDeclaration) +DONT_OPTIMIZE_NODE(ExportDeclaration) +DONT_OPTIMIZE_NODE(ModuleLiteral) +DONT_OPTIMIZE_NODE(ModuleVariable) +DONT_OPTIMIZE_NODE(ModulePath) +DONT_OPTIMIZE_NODE(ModuleUrl) +DONT_OPTIMIZE_NODE(WithStatement) +DONT_OPTIMIZE_NODE(TryCatchStatement) +DONT_OPTIMIZE_NODE(TryFinallyStatement) +DONT_OPTIMIZE_NODE(DebuggerStatement) +DONT_OPTIMIZE_NODE(SharedFunctionInfoLiteral) + +DONT_INLINE_NODE(SwitchStatement) +DONT_INLINE_NODE(FunctionLiteral) +DONT_INLINE_NODE(RegExpLiteral) // TODO(1322): Allow materialized literals. +DONT_INLINE_NODE(ArrayLiteral) // TODO(1322): Allow materialized literals. + +DONT_SELFOPTIMIZE_NODE(DoWhileStatement) +DONT_SELFOPTIMIZE_NODE(WhileStatement) +DONT_SELFOPTIMIZE_NODE(ForStatement) +DONT_SELFOPTIMIZE_NODE(ForInStatement) void AstConstructionVisitor::VisitCallRuntime(CallRuntime* node) { increase_node_count(); @@ -1142,6 +1082,11 @@ void AstConstructionVisitor::VisitCallRuntime(CallRuntime* node) { } } +#undef REGULAR_NODE +#undef DONT_OPTIMIZE_NODE +#undef DONT_INLINE_NODE +#undef DONT_SELFOPTIMIZE_NODE + Handle Literal::ToString() { if (handle_->IsString()) return Handle::cast(handle_); diff --git a/src/ast.h b/src/ast.h index d6c47e2..dad8057 100644 --- a/src/ast.h +++ b/src/ast.h @@ -421,8 +421,8 @@ class Block: public BreakableStatement { ZoneList* statements() { return &statements_; } bool is_initializer_block() const { return is_initializer_block_; } - Scope* block_scope() const { return block_scope_; } - void set_block_scope(Scope* block_scope) { block_scope_ = block_scope; } + Scope* scope() const { return scope_; } + void set_scope(Scope* scope) { scope_ = scope; } protected: template friend class AstNodeFactory; @@ -434,13 +434,13 @@ class Block: public BreakableStatement { : BreakableStatement(isolate, labels, TARGET_FOR_NAMED_ONLY), statements_(capacity), is_initializer_block_(is_initializer_block), - block_scope_(NULL) { + scope_(NULL) { } private: ZoneList statements_; bool is_initializer_block_; - Scope* block_scope_; + Scope* scope_; }; @@ -608,6 +608,7 @@ class ModuleLiteral: public Module { DECLARE_NODE_TYPE(ModuleLiteral) Block* body() const { return body_; } + Handle context() const { return context_; } protected: template friend class AstNodeFactory; @@ -619,6 +620,7 @@ class ModuleLiteral: public Module { private: Block* body_; + Handle context_; }; diff --git a/src/factory.cc b/src/factory.cc index e8a9f26..6bb7893 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -291,6 +291,15 @@ Handle Factory::NewGlobalContext() { } +Handle Factory::NewModuleContext(Handle previous, + Handle scope_info) { + CALL_HEAP_FUNCTION( + isolate(), + isolate()->heap()->AllocateModuleContext(*previous, *scope_info), + Context); +} + + Handle Factory::NewFunctionContext(int length, Handle function) { CALL_HEAP_FUNCTION( @@ -324,10 +333,9 @@ Handle Factory::NewWithContext(Handle function, } -Handle Factory::NewBlockContext( - Handle function, - Handle previous, - Handle scope_info) { +Handle Factory::NewBlockContext(Handle function, + Handle previous, + Handle scope_info) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateBlockContext(*function, @@ -928,6 +936,13 @@ Handle Factory::NewJSObject(Handle constructor, } +Handle Factory::NewJSModule() { + CALL_HEAP_FUNCTION( + isolate(), + isolate()->heap()->AllocateJSModule(), JSModule); +} + + Handle Factory::NewGlobalObject( Handle constructor) { CALL_HEAP_FUNCTION(isolate(), diff --git a/src/factory.h b/src/factory.h index 786d4a9..06aad1b 100644 --- a/src/factory.h +++ b/src/factory.h @@ -162,9 +162,12 @@ class Factory { // Create a global (but otherwise uninitialized) context. Handle NewGlobalContext(); + // Create a module context. + Handle NewModuleContext(Handle previous, + Handle scope_info); + // Create a function context. - Handle NewFunctionContext(int length, - Handle function); + Handle NewFunctionContext(int length, Handle function); // Create a catch context. Handle NewCatchContext(Handle function, @@ -177,7 +180,7 @@ class Factory { Handle previous, Handle extension); - // Create a 'block' context. + // Create a block context. Handle NewBlockContext(Handle function, Handle previous, Handle scope_info); @@ -262,6 +265,9 @@ class Factory { // runtime. Handle NewJSObjectFromMap(Handle map); + // JS modules are pretenured. + Handle NewJSModule(); + // JS arrays are pretenured when allocated by the parser. Handle NewJSArray(int capacity, ElementsKind elements_kind = FAST_ELEMENTS, diff --git a/src/full-codegen.cc b/src/full-codegen.cc index 310805d..b8794c0 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -568,32 +568,91 @@ void FullCodeGenerator::DoTest(const TestContext* context) { void FullCodeGenerator::VisitDeclarations( ZoneList* declarations) { - ASSERT(globals_.is_empty()); + ZoneList >* saved_globals = globals_; + ZoneList > inner_globals(10); + globals_ = &inner_globals; + AstVisitor::VisitDeclarations(declarations); - if (!globals_.is_empty()) { + if (!globals_->is_empty()) { // Invoke the platform-dependent code generator to do the actual // declaration the global functions and variables. Handle array = - isolate()->factory()->NewFixedArray(globals_.length(), TENURED); - for (int i = 0; i < globals_.length(); ++i) array->set(i, *globals_.at(i)); + isolate()->factory()->NewFixedArray(globals_->length(), TENURED); + for (int i = 0; i < globals_->length(); ++i) + array->set(i, *globals_->at(i)); DeclareGlobals(array); - globals_.Clear(); } + + globals_ = saved_globals; } void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) { - // TODO(rossberg) + Handle instance = module->interface()->Instance(); + ASSERT(!instance.is_null()); + + // Allocate a module context statically. + Block* block = module->body(); + Scope* saved_scope = scope(); + scope_ = block->scope(); + Handle scope_info = scope_->GetScopeInfo(); + + // Generate code for module creation and linking. + Comment cmnt(masm_, "[ ModuleLiteral"); + SetStatementPosition(block); + + if (scope_info->HasContext()) { + // Set up module context. + __ Push(scope_info); + __ Push(instance); + __ CallRuntime(Runtime::kPushModuleContext, 2); + StoreToFrameField( + StandardFrameConstants::kContextOffset, context_register()); + } + + { + Comment cmnt(masm_, "[ Declarations"); + VisitDeclarations(scope_->declarations()); + } + + scope_ = saved_scope; + if (scope_info->HasContext()) { + // Pop module context. + LoadContextField(context_register(), Context::PREVIOUS_INDEX); + // Update local stack frame context field. + StoreToFrameField( + StandardFrameConstants::kContextOffset, context_register()); + } + + // Populate module instance object. + const PropertyAttributes attr = + static_cast(READ_ONLY | DONT_DELETE | DONT_ENUM); + for (Interface::Iterator it = module->interface()->iterator(); + !it.done(); it.Advance()) { + if (it.interface()->IsModule()) { + Handle value = it.interface()->Instance(); + ASSERT(!value.is_null()); + JSReceiver::SetProperty(instance, it.name(), value, attr, kStrictMode); + } else { + // TODO(rossberg): set proper getters instead of undefined... + // instance->DefineAccessor(*it.name(), ACCESSOR_GETTER, *getter, attr); + Handle value(isolate()->heap()->undefined_value()); + JSReceiver::SetProperty(instance, it.name(), value, attr, kStrictMode); + } + } + USE(instance->PreventExtensions()); } void FullCodeGenerator::VisitModuleVariable(ModuleVariable* module) { - // TODO(rossberg) + // Noting to do. + // The instance object is resolved statically through the module's interface. } void FullCodeGenerator::VisitModulePath(ModulePath* module) { - // TODO(rossberg) + // Noting to do. + // The instance object is resolved statically through the module's interface. } @@ -855,9 +914,9 @@ void FullCodeGenerator::VisitBlock(Block* stmt) { Scope* saved_scope = scope(); // Push a block context when entering a block with block scoped variables. - if (stmt->block_scope() != NULL) { + if (stmt->scope() != NULL) { { Comment cmnt(masm_, "[ Extend block context"); - scope_ = stmt->block_scope(); + scope_ = stmt->scope(); Handle scope_info = scope_->GetScopeInfo(); int heap_slots = scope_info->ContextLength() - Context::MIN_CONTEXT_SLOTS; __ Push(scope_info); @@ -884,7 +943,7 @@ void FullCodeGenerator::VisitBlock(Block* stmt) { PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); // Pop block context if necessary. - if (stmt->block_scope() != NULL) { + if (stmt->scope() != NULL) { LoadContextField(context_register(), Context::PREVIOUS_INDEX); // Update local stack frame context field. StoreToFrameField(StandardFrameConstants::kContextOffset, diff --git a/src/full-codegen.h b/src/full-codegen.h index 7c56766..0e0ffe9 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -83,7 +83,7 @@ class FullCodeGenerator: public AstVisitor { scope_(info->scope()), nesting_stack_(NULL), loop_depth_(0), - globals_(10), + globals_(NULL), context_(NULL), bailout_entries_(info->HasDeoptimizationSupport() ? info->function()->ast_node_count() : 0), @@ -202,7 +202,7 @@ class FullCodeGenerator: public AstVisitor { virtual ~NestedBlock() {} virtual NestedStatement* Exit(int* stack_depth, int* context_length) { - if (statement()->AsBlock()->block_scope() != NULL) { + if (statement()->AsBlock()->scope() != NULL) { ++(*context_length); } return previous_; @@ -778,7 +778,7 @@ class FullCodeGenerator: public AstVisitor { Label return_label_; NestedStatement* nesting_stack_; int loop_depth_; - ZoneList > globals_; + ZoneList >* globals_; const ExpressionContext* context_; ZoneList bailout_entries_; ZoneList stack_checks_; diff --git a/src/heap.cc b/src/heap.cc index 22dbbeb..ac9e029 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -3827,6 +3827,16 @@ MaybeObject* Heap::AllocateJSObject(JSFunction* constructor, } +MaybeObject* Heap::AllocateJSModule() { + // Allocate a fresh map. Modules do not have a prototype. + Map* map; + MaybeObject* maybe_map = AllocateMap(JS_MODULE_TYPE, JSModule::kSize); + if (!maybe_map->To(&map)) return maybe_map; + // Allocate the object based on the map. + return AllocateJSObjectFromMap(map, TENURED); +} + + MaybeObject* Heap::AllocateJSArrayAndStorage( ElementsKind elements_kind, int length, @@ -4701,6 +4711,22 @@ MaybeObject* Heap::AllocateGlobalContext() { } +MaybeObject* Heap::AllocateModuleContext(Context* previous, + ScopeInfo* scope_info) { + Object* result; + { MaybeObject* maybe_result = + AllocateFixedArrayWithHoles(scope_info->ContextLength(), TENURED); + if (!maybe_result->ToObject(&result)) return maybe_result; + } + Context* context = reinterpret_cast(result); + context->set_map_no_write_barrier(module_context_map()); + context->set_previous(previous); + context->set_extension(scope_info); + context->set_global(previous->global()); + return context; +} + + MaybeObject* Heap::AllocateFunctionContext(int length, JSFunction* function) { ASSERT(length >= Context::MIN_CONTEXT_SLOTS); Object* result; diff --git a/src/heap.h b/src/heap.h index d1cdda0..af86f44 100644 --- a/src/heap.h +++ b/src/heap.h @@ -529,6 +529,8 @@ class Heap { MUST_USE_RESULT MaybeObject* AllocateJSObject( JSFunction* constructor, PretenureFlag pretenure = NOT_TENURED); + MUST_USE_RESULT MaybeObject* AllocateJSModule(); + // Allocate a JSArray with no elements MUST_USE_RESULT MaybeObject* AllocateEmptyJSArray( ElementsKind elements_kind, @@ -820,6 +822,10 @@ class Heap { // Allocate a global (but otherwise uninitialized) context. MUST_USE_RESULT MaybeObject* AllocateGlobalContext(); + // Allocate a module context. + MUST_USE_RESULT MaybeObject* AllocateModuleContext(Context* previous, + ScopeInfo* scope_info); + // Allocate a function context. MUST_USE_RESULT MaybeObject* AllocateFunctionContext(int length, JSFunction* function); diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 8b8d327..32b6581 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -2768,7 +2768,7 @@ void HGraphBuilder::VisitBlock(Block* stmt) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - if (stmt->block_scope() != NULL) { + if (stmt->scope() != NULL) { return Bailout("ScopedBlock"); } BreakAndContinueInfo break_info(stmt); @@ -7250,67 +7250,37 @@ void HGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* declaration) { void HGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* declaration) { - VariableProxy* proxy = declaration->proxy(); - Variable* var = proxy->var(); - switch (var->location()) { - case Variable::UNALLOCATED: { - // TODO(rossberg) - return; - } - case Variable::CONTEXT: { - // TODO(rossberg) - break; - } - case Variable::PARAMETER: - case Variable::LOCAL: - case Variable::LOOKUP: - UNREACHABLE(); - } + UNREACHABLE(); } void HGraphBuilder::VisitImportDeclaration(ImportDeclaration* declaration) { - VariableProxy* proxy = declaration->proxy(); - Variable* var = proxy->var(); - switch (var->location()) { - case Variable::UNALLOCATED: { - // TODO(rossberg) - return; - } - case Variable::CONTEXT: { - // TODO(rossberg) - break; - } - case Variable::PARAMETER: - case Variable::LOCAL: - case Variable::LOOKUP: - UNREACHABLE(); - } + UNREACHABLE(); } void HGraphBuilder::VisitExportDeclaration(ExportDeclaration* declaration) { - // TODO(rossberg) + UNREACHABLE(); } void HGraphBuilder::VisitModuleLiteral(ModuleLiteral* module) { - // TODO(rossberg) + UNREACHABLE(); } void HGraphBuilder::VisitModuleVariable(ModuleVariable* module) { - // TODO(rossberg) + UNREACHABLE(); } void HGraphBuilder::VisitModulePath(ModulePath* module) { - // TODO(rossberg) + UNREACHABLE(); } void HGraphBuilder::VisitModuleUrl(ModuleUrl* module) { - // TODO(rossberg) + UNREACHABLE(); } diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 9242041..0fc5001 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -782,10 +782,10 @@ void FullCodeGenerator::VisitVariableDeclaration( bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET; switch (variable->location()) { case Variable::UNALLOCATED: - globals_.Add(variable->name()); - globals_.Add(variable->binding_needs_init() - ? isolate()->factory()->the_hole_value() - : isolate()->factory()->undefined_value()); + globals_->Add(variable->name()); + globals_->Add(variable->binding_needs_init() + ? isolate()->factory()->the_hole_value() + : isolate()->factory()->undefined_value()); break; case Variable::PARAMETER: @@ -840,12 +840,12 @@ void FullCodeGenerator::VisitFunctionDeclaration( Variable* variable = proxy->var(); switch (variable->location()) { case Variable::UNALLOCATED: { - globals_.Add(variable->name()); + globals_->Add(variable->name()); Handle function = Compiler::BuildFunctionInfo(declaration->fun(), script()); // Check for stack-overflow exception. if (function.is_null()) return SetStackOverflow(); - globals_.Add(function); + globals_->Add(function); break; } @@ -890,15 +890,23 @@ void FullCodeGenerator::VisitFunctionDeclaration( void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) { VariableProxy* proxy = declaration->proxy(); Variable* variable = proxy->var(); + Handle instance = declaration->module()->interface()->Instance(); + ASSERT(!instance.is_null()); + switch (variable->location()) { - case Variable::UNALLOCATED: - // TODO(rossberg): initialize module instance object + case Variable::UNALLOCATED: { + Comment cmnt(masm_, "[ ModuleDeclaration"); + globals_->Add(variable->name()); + globals_->Add(instance); + Visit(declaration->module()); break; + } case Variable::CONTEXT: { Comment cmnt(masm_, "[ ModuleDeclaration"); EmitDebugCheckDeclarationContext(variable); - // TODO(rossberg): initialize module instance object + __ mov(ContextOperand(esi, variable->index()), Immediate(instance)); + Visit(declaration->module()); break; } @@ -4521,7 +4529,8 @@ void FullCodeGenerator::LoadContextField(Register dst, int context_index) { void FullCodeGenerator::PushFunctionArgumentForContextAllocation() { Scope* declaration_scope = scope()->DeclarationScope(); - if (declaration_scope->is_global_scope()) { + if (declaration_scope->is_global_scope() || + declaration_scope->is_module_scope()) { // Contexts nested in the global context have a canonical empty function // as their closure, not the anonymous closure containing the global // code. Pass a smi sentinel and let the runtime look up the empty diff --git a/src/interface.cc b/src/interface.cc index e344b86..7836110 100644 --- a/src/interface.cc +++ b/src/interface.cc @@ -79,7 +79,7 @@ void Interface::DoAdd( PrintF("%*sthis = ", Nesting::current(), ""); this->Print(Nesting::current()); PrintF("%*s%s : ", Nesting::current(), "", - (*reinterpret_cast(name))->ToAsciiArray()); + (*static_cast(name))->ToAsciiArray()); interface->Print(Nesting::current()); } #endif @@ -97,7 +97,7 @@ void Interface::DoAdd( #ifdef DEBUG Nesting nested; #endif - reinterpret_cast(p->value)->Unify(interface, ok); + static_cast(p->value)->Unify(interface, ok); } #ifdef DEBUG @@ -180,6 +180,15 @@ void Interface::DoUnify(Interface* that, bool* ok) { return; } + // Merge instance. + if (!that->instance_.is_null()) { + if (!this->instance_.is_null() && *this->instance_ != *that->instance_) { + *ok = false; + return; + } + this->instance_ = that->instance_; + } + // Merge interfaces. this->flags_ |= that->flags_; that->forward_ = this; diff --git a/src/interface.h b/src/interface.h index c2991cb..580f082 100644 --- a/src/interface.h +++ b/src/interface.h @@ -86,6 +86,12 @@ class Interface : public ZoneObject { if (*ok) Chase()->flags_ |= MODULE; } + // Set associated instance object. + void MakeSingleton(Handle instance, bool* ok) { + *ok = IsModule() && Chase()->instance_.is_null(); + if (*ok) Chase()->instance_ = instance; + } + // Do not allow any further refinements, directly or through unification. void Freeze(bool* ok) { *ok = IsValue() || IsModule(); @@ -95,9 +101,6 @@ class Interface : public ZoneObject { // --------------------------------------------------------------------------- // Accessors. - // Look up an exported name. Returns NULL if not (yet) defined. - Interface* Lookup(Handle name); - // Check whether this is still a fully undetermined type. bool IsUnknown() { return Chase()->flags_ == NONE; } @@ -110,6 +113,42 @@ class Interface : public ZoneObject { // Check whether this is closed (i.e. fully determined). bool IsFrozen() { return Chase()->flags_ & FROZEN; } + Handle Instance() { return Chase()->instance_; } + + // Look up an exported name. Returns NULL if not (yet) defined. + Interface* Lookup(Handle name); + + // --------------------------------------------------------------------------- + // Iterators. + + // Use like: + // for (auto it = interface->iterator(); !it.done(); it.Advance()) { + // ... it.name() ... it.interface() ... + // } + class Iterator { + public: + bool done() const { return entry_ == NULL; } + Handle name() const { + ASSERT(!done()); + return Handle(*static_cast(entry_->key)); + } + Interface* interface() const { + ASSERT(!done()); + return static_cast(entry_->value); + } + void Advance() { entry_ = exports_->Next(entry_); } + + private: + friend class Interface; + explicit Iterator(const ZoneHashMap* exports) + : exports_(exports), entry_(exports ? exports->Start() : NULL) {} + + const ZoneHashMap* exports_; + ZoneHashMap::Entry* entry_; + }; + + Iterator iterator() const { return Iterator(this->exports_); } + // --------------------------------------------------------------------------- // Debugging. #ifdef DEBUG @@ -129,6 +168,7 @@ class Interface : public ZoneObject { int flags_; Interface* forward_; // Unification link ZoneHashMap* exports_; // Module exports and their types (allocated lazily) + Handle instance_; explicit Interface(int flags) : flags_(flags), diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 8eefb23..cd2ccf8 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -135,6 +135,9 @@ void HeapObject::HeapObjectVerify() { case JS_CONTEXT_EXTENSION_OBJECT_TYPE: JSObject::cast(this)->JSObjectVerify(); break; + case JS_MODULE_TYPE: + JSModule::cast(this)->JSModuleVerify(); + break; case JS_VALUE_TYPE: JSValue::cast(this)->JSValueVerify(); break; @@ -366,6 +369,15 @@ void FixedDoubleArray::FixedDoubleArrayVerify() { } +void JSModule::JSModuleVerify() { + Object* v = context(); + if (v->IsHeapObject()) { + VerifyHeapPointer(v); + } + CHECK(v->IsUndefined() || v->IsModuleContext()); +} + + void JSValue::JSValueVerify() { Object* v = value(); if (v->IsHeapObject()) { diff --git a/src/objects-inl.h b/src/objects-inl.h index 49c8db8..ac328ce 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -581,7 +581,8 @@ bool Object::IsContext() { map == heap->catch_context_map() || map == heap->with_context_map() || map == heap->global_context_map() || - map == heap->block_context_map()); + map == heap->block_context_map() || + map == heap->module_context_map()); } return false; } @@ -594,6 +595,13 @@ bool Object::IsGlobalContext() { } +bool Object::IsModuleContext() { + return Object::IsHeapObject() && + HeapObject::cast(this)->map() == + HeapObject::cast(this)->GetHeap()->module_context_map(); +} + + bool Object::IsScopeInfo() { return Object::IsHeapObject() && HeapObject::cast(this)->map() == @@ -613,6 +621,7 @@ TYPE_CHECKER(Code, CODE_TYPE) TYPE_CHECKER(Oddball, ODDBALL_TYPE) TYPE_CHECKER(JSGlobalPropertyCell, JS_GLOBAL_PROPERTY_CELL_TYPE) TYPE_CHECKER(SharedFunctionInfo, SHARED_FUNCTION_INFO_TYPE) +TYPE_CHECKER(JSModule, JS_MODULE_TYPE) TYPE_CHECKER(JSValue, JS_VALUE_TYPE) TYPE_CHECKER(JSDate, JS_DATE_TYPE) TYPE_CHECKER(JSMessageObject, JS_MESSAGE_OBJECT_TYPE) @@ -1436,6 +1445,8 @@ int JSObject::GetHeaderSize() { // field operations considerably on average. if (type == JS_OBJECT_TYPE) return JSObject::kHeaderSize; switch (type) { + case JS_MODULE_TYPE: + return JSModule::kSize; case JS_GLOBAL_PROXY_TYPE: return JSGlobalProxy::kSize; case JS_GLOBAL_OBJECT_TYPE: @@ -4078,6 +4089,16 @@ void Foreign::set_foreign_address(Address value) { } +ACCESSORS(JSModule, context, Object, kContextOffset) + + +JSModule* JSModule::cast(Object* obj) { + ASSERT(obj->IsJSModule()); + ASSERT(HeapObject::cast(obj)->Size() == JSModule::kSize); + return reinterpret_cast(obj); +} + + ACCESSORS(JSValue, value, Object, kValueOffset) diff --git a/src/objects-printer.cc b/src/objects-printer.cc index 2353a95..7d6ef67 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -135,6 +135,9 @@ void HeapObject::HeapObjectPrint(FILE* out) { case ODDBALL_TYPE: Oddball::cast(this)->to_string()->Print(out); break; + case JS_MODULE_TYPE: + JSModule::cast(this)->JSModulePrint(out); + break; case JS_FUNCTION_TYPE: JSFunction::cast(this)->JSFunctionPrint(out); break; @@ -152,7 +155,7 @@ void HeapObject::HeapObjectPrint(FILE* out) { JSValue::cast(this)->value()->Print(out); break; case JS_DATE_TYPE: - JSDate::cast(this)->value()->Print(out); + JSDate::cast(this)->JSDatePrint(out); break; case CODE_TYPE: Code::cast(this)->CodePrint(out); @@ -437,6 +440,19 @@ void JSObject::JSObjectPrint(FILE* out) { } +void JSModule::JSModulePrint(FILE* out) { + HeapObject::PrintHeader(out, "JSModule"); + PrintF(out, " - map = 0x%p\n", reinterpret_cast(map())); + PrintF(out, " - context = "); + context()->Print(out); + PrintElementsKind(out, this->map()->elements_kind()); + PrintF(out, " {\n"); + PrintProperties(out); + PrintElements(out); + PrintF(out, " }\n"); +} + + static const char* TypeToString(InstanceType type) { switch (type) { case INVALID_TYPE: return "INVALID"; @@ -483,6 +499,7 @@ static const char* TypeToString(InstanceType type) { case ODDBALL_TYPE: return "ODDBALL"; case JS_GLOBAL_PROPERTY_CELL_TYPE: return "JS_GLOBAL_PROPERTY_CELL"; case SHARED_FUNCTION_INFO_TYPE: return "SHARED_FUNCTION_INFO"; + case JS_MODULE_TYPE: return "JS_MODULE"; case JS_FUNCTION_TYPE: return "JS_FUNCTION"; case CODE_TYPE: return "CODE"; case JS_ARRAY_TYPE: return "JS_ARRAY"; diff --git a/src/objects-visiting.cc b/src/objects-visiting.cc index c7c8a87..a2dc43e 100644 --- a/src/objects-visiting.cc +++ b/src/objects-visiting.cc @@ -133,6 +133,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId( case JS_OBJECT_TYPE: case JS_CONTEXT_EXTENSION_OBJECT_TYPE: + case JS_MODULE_TYPE: case JS_VALUE_TYPE: case JS_DATE_TYPE: case JS_ARRAY_TYPE: diff --git a/src/objects.cc b/src/objects.cc index f96b69b..1fe27ae 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1338,6 +1338,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size, break; case JS_OBJECT_TYPE: case JS_CONTEXT_EXTENSION_OBJECT_TYPE: + case JS_MODULE_TYPE: case JS_VALUE_TYPE: case JS_DATE_TYPE: case JS_ARRAY_TYPE: diff --git a/src/objects.h b/src/objects.h index b728d0c..80c5445 100644 --- a/src/objects.h +++ b/src/objects.h @@ -59,6 +59,7 @@ // - JSWeakMap // - JSRegExp // - JSFunction +// - JSModule // - GlobalObject // - JSGlobalObject // - JSBuiltinsObject @@ -306,6 +307,7 @@ const int kVariableSizeSentinel = 0; V(JS_DATE_TYPE) \ V(JS_OBJECT_TYPE) \ V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \ + V(JS_MODULE_TYPE) \ V(JS_GLOBAL_OBJECT_TYPE) \ V(JS_BUILTINS_OBJECT_TYPE) \ V(JS_GLOBAL_PROXY_TYPE) \ @@ -626,6 +628,7 @@ enum InstanceType { JS_DATE_TYPE, JS_OBJECT_TYPE, JS_CONTEXT_EXTENSION_OBJECT_TYPE, + JS_MODULE_TYPE, JS_GLOBAL_OBJECT_TYPE, JS_BUILTINS_OBJECT_TYPE, JS_GLOBAL_PROXY_TYPE, @@ -803,6 +806,7 @@ class MaybeObject BASE_EMBEDDED { V(JSReceiver) \ V(JSObject) \ V(JSContextExtensionObject) \ + V(JSModule) \ V(Map) \ V(DescriptorArray) \ V(DeoptimizationInputData) \ @@ -812,6 +816,7 @@ class MaybeObject BASE_EMBEDDED { V(FixedDoubleArray) \ V(Context) \ V(GlobalContext) \ + V(ModuleContext) \ V(ScopeInfo) \ V(JSFunction) \ V(Code) \ @@ -5691,6 +5696,35 @@ class SharedFunctionInfo: public HeapObject { }; +// Representation for module instance objects. +class JSModule: public JSObject { + public: + // [context]: the context holding the module's locals, or undefined if none. + DECL_ACCESSORS(context, Object) + + // Casting. + static inline JSModule* cast(Object* obj); + + // Dispatched behavior. +#ifdef OBJECT_PRINT + inline void JSModulePrint() { + JSModulePrint(stdout); + } + void JSModulePrint(FILE* out); +#endif +#ifdef DEBUG + void JSModuleVerify(); +#endif + + // Layout description. + static const int kContextOffset = JSObject::kHeaderSize; + static const int kSize = kContextOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSModule); +}; + + // JSFunction describes JavaScript functions. class JSFunction: public JSObject { public: diff --git a/src/parser.cc b/src/parser.cc index 5bce764..8620519 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1333,11 +1333,19 @@ Module* Parser::ParseModuleLiteral(bool* ok) { Expect(Token::RBRACE, CHECK_OK); scope->set_end_position(scanner().location().end_pos); - body->set_block_scope(scope); + body->set_scope(scope); - scope->interface()->Freeze(ok); + // Instance objects have to be created ahead of time (before code generation + // linking them) because of potentially cyclic references between them. + // We create them here, to avoid another pass over the AST. + Interface* interface = scope->interface(); + interface->MakeModule(ok); ASSERT(ok); - return factory()->NewModuleLiteral(body, scope->interface()); + interface->MakeSingleton(Isolate::Current()->factory()->NewJSModule(), ok); + ASSERT(ok); + interface->Freeze(ok); + ASSERT(ok); + return factory()->NewModuleLiteral(body, interface); } @@ -1403,7 +1411,14 @@ Module* Parser::ParseModuleUrl(bool* ok) { #ifdef DEBUG if (FLAG_print_interface_details) PrintF("# Url "); #endif - return factory()->NewModuleUrl(symbol); + + Module* result = factory()->NewModuleUrl(symbol); + Interface* interface = result->interface(); + interface->MakeSingleton(Isolate::Current()->factory()->NewJSModule(), ok); + ASSERT(ok); + interface->Freeze(ok); + ASSERT(ok); + return result; } @@ -2015,7 +2030,7 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) { Expect(Token::RBRACE, CHECK_OK); block_scope->set_end_position(scanner().location().end_pos); block_scope = block_scope->FinalizeBlockScope(); - body->set_block_scope(block_scope); + body->set_scope(block_scope); return body; } @@ -2917,7 +2932,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { top_scope_ = saved_scope; for_scope->set_end_position(scanner().location().end_pos); for_scope = for_scope->FinalizeBlockScope(); - body_block->set_block_scope(for_scope); + body_block->set_scope(for_scope); // Parsed for-in loop w/ let declaration. return loop; @@ -2997,7 +3012,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Block* result = factory()->NewBlock(NULL, 2, false); result->AddStatement(init); result->AddStatement(loop); - result->set_block_scope(for_scope); + result->set_scope(for_scope); if (loop) loop->Initialize(NULL, cond, next, body); return result; } else { diff --git a/src/runtime.cc b/src/runtime.cc index ed89c6e..b978653 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -1292,7 +1292,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { bool is_var = value->IsUndefined(); bool is_const = value->IsTheHole(); bool is_function = value->IsSharedFunctionInfo(); - bool is_module = value->IsJSObject(); + bool is_module = value->IsJSModule(); ASSERT(is_var + is_const + is_function + is_module == 1); if (is_var || is_const) { @@ -8598,6 +8598,25 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushBlockContext) { } +RUNTIME_FUNCTION(MaybeObject*, Runtime_PushModuleContext) { + NoHandleAllocation ha; + ASSERT(args.length() == 2); + CONVERT_ARG_CHECKED(ScopeInfo, scope_info, 0); + CONVERT_ARG_HANDLE_CHECKED(JSModule, instance, 1); + + Context* context; + MaybeObject* maybe_context = + isolate->heap()->AllocateModuleContext(isolate->context(), + scope_info); + if (!maybe_context->To(&context)) return maybe_context; + // Also initialize the context slot of the instance object. + instance->set_context(context); + isolate->set_context(context); + + return context; +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteContextSlot) { HandleScope scope(isolate); ASSERT(args.length() == 2); diff --git a/src/runtime.h b/src/runtime.h index fe9cfd9..b8918cd 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -323,6 +323,7 @@ namespace internal { F(PushWithContext, 2, 1) \ F(PushCatchContext, 3, 1) \ F(PushBlockContext, 2, 1) \ + F(PushModuleContext, 2, 1) \ F(DeleteContextSlot, 2, 1) \ F(LoadContextSlot, 2, 2) \ F(LoadContextSlotNoReferenceError, 2, 2) \ diff --git a/src/scopeinfo.cc b/src/scopeinfo.cc index c9d91e1..f50af30 100644 --- a/src/scopeinfo.cc +++ b/src/scopeinfo.cc @@ -142,7 +142,9 @@ Handle ScopeInfo::Create(Scope* scope) { ASSERT(index == scope_info->length()); ASSERT(scope->num_parameters() == scope_info->ParameterCount()); ASSERT(scope->num_stack_slots() == scope_info->StackSlotCount()); - ASSERT(scope->num_heap_slots() == scope_info->ContextLength()); + ASSERT(scope->num_heap_slots() == scope_info->ContextLength() || + (scope->num_heap_slots() == kVariablePartIndex && + scope_info->ContextLength() == 0)); return scope_info; } diff --git a/src/scopes.cc b/src/scopes.cc index 08a567c..6f6032a 100644 --- a/src/scopes.cc +++ b/src/scopes.cc @@ -1099,7 +1099,7 @@ bool Scope::MustAllocateInContext(Variable* var) { // Exceptions: temporary variables are never allocated in a context; // catch-bound variables are always allocated in a context. if (var->mode() == TEMPORARY) return false; - if (is_catch_scope() || is_block_scope()) return true; + if (is_catch_scope() || is_block_scope() || is_module_scope()) return true; return var->has_forced_context_allocation() || scope_calls_eval_ || inner_scope_calls_eval_ || @@ -1243,7 +1243,8 @@ void Scope::AllocateVariablesRecursively() { // Force allocation of a context for this scope if necessary. For a 'with' // scope and for a function scope that makes an 'eval' call we need a context, // even if no local variables were statically allocated in the scope. - bool must_have_context = is_with_scope() || + // Likewise for modules. + bool must_have_context = is_with_scope() || is_module_scope() || (is_function_scope() && calls_eval()); // If we didn't allocate any locals in the local context, then we only diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index da04ca9..e0000f8 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -779,10 +779,10 @@ void FullCodeGenerator::VisitVariableDeclaration( bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET; switch (variable->location()) { case Variable::UNALLOCATED: - globals_.Add(variable->name()); - globals_.Add(variable->binding_needs_init() - ? isolate()->factory()->the_hole_value() - : isolate()->factory()->undefined_value()); + globals_->Add(variable->name()); + globals_->Add(variable->binding_needs_init() + ? isolate()->factory()->the_hole_value() + : isolate()->factory()->undefined_value()); break; case Variable::PARAMETER: @@ -837,12 +837,12 @@ void FullCodeGenerator::VisitFunctionDeclaration( Variable* variable = proxy->var(); switch (variable->location()) { case Variable::UNALLOCATED: { - globals_.Add(variable->name()); + globals_->Add(variable->name()); Handle function = Compiler::BuildFunctionInfo(declaration->fun(), script()); // Check for stack-overflow exception. if (function.is_null()) return SetStackOverflow(); - globals_.Add(function); + globals_->Add(function); break; } @@ -888,15 +888,23 @@ void FullCodeGenerator::VisitFunctionDeclaration( void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) { VariableProxy* proxy = declaration->proxy(); Variable* variable = proxy->var(); + Handle instance = declaration->module()->interface()->Instance(); + ASSERT(!instance.is_null()); + switch (variable->location()) { - case Variable::UNALLOCATED: - // TODO(rossberg): initialize module instance object + case Variable::UNALLOCATED: { + Comment cmnt(masm_, "[ ModuleDeclaration"); + globals_->Add(variable->name()); + globals_->Add(instance); + Visit(declaration->module()); break; + } case Variable::CONTEXT: { Comment cmnt(masm_, "[ ModuleDeclaration"); EmitDebugCheckDeclarationContext(variable); - // TODO(rossberg): initialize module instance object + __ Move(ContextOperand(rsi, variable->index()), instance); + Visit(declaration->module()); break; } @@ -4503,7 +4511,8 @@ void FullCodeGenerator::LoadContextField(Register dst, int context_index) { void FullCodeGenerator::PushFunctionArgumentForContextAllocation() { Scope* declaration_scope = scope()->DeclarationScope(); - if (declaration_scope->is_global_scope()) { + if (declaration_scope->is_global_scope() || + declaration_scope->is_module_scope()) { // Contexts nested in the global context have a canonical empty function // as their closure, not the anonymous closure containing the global // code. Pass a smi sentinel and let the runtime look up the empty diff --git a/test/mjsunit/harmony/module-linking.js b/test/mjsunit/harmony/module-linking.js new file mode 100644 index 0000000..13ca6f7 --- /dev/null +++ b/test/mjsunit/harmony/module-linking.js @@ -0,0 +1,121 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-modules --harmony-scoping + +// Test basic module linking. + +"use strict"; + +let log = ""; + +export let x = (log += "1"); + +export module B = A.B + +export module A { + export let x = (log += "2"); + let y = (log += "3"); + export function f() { log += "5" }; + export module B { + module BB = B; + export BB, x; + let x = (log += "4"); + f(); + let y = (log += "6"); + } + export let z = (log += "7"); + export module C { + export let z = (log += "8"); + export module D = B + export module C = A.C + } + module D {} +} + +export module M1 { + export module A2 = M2; + export let x = (log += "9"); +} +export module M2 { + export module A1 = M1; + export let x = (log += "0"); +} + +assertEquals("object", typeof A); +assertTrue('x' in A); +assertFalse('y' in A); +assertTrue('f' in A); +assertTrue('B' in A); +assertTrue('z' in A); +assertTrue('C' in A); +assertFalse('D' in A); + +assertEquals("object", typeof B); +assertTrue('BB' in B); +assertTrue('x' in B); +assertFalse('y' in B); + +assertEquals("object", typeof A.B); +assertTrue('BB' in A.B); +assertTrue('x' in A.B); +assertFalse('y' in A.B); + +assertEquals("object", typeof A.B.BB); +assertTrue('BB' in A.B.BB); +assertTrue('x' in A.B.BB); +assertFalse('y' in A.B.BB); + +assertEquals("object", typeof A.C); +assertTrue('z' in A.C); +assertTrue('D' in A.C); +assertTrue('C' in A.C); + +assertEquals("object", typeof M1); +assertEquals("object", typeof M2); +assertTrue('A2' in M1); +assertTrue('A1' in M2); +assertEquals("object", typeof M1.A2); +assertEquals("object", typeof M2.A1); +assertTrue('A1' in M1.A2); +assertTrue('A2' in M2.A1); +assertEquals("object", typeof M1.A2.A1); +assertEquals("object", typeof M2.A1.A2); + +assertSame(B, A.B); +assertSame(B, B.BB); +assertSame(B, A.C.D); +assertSame(A.C, A.C.C); +assertFalse(A.D === A.C.D); + +assertSame(M1, M2.A1); +assertSame(M2, M1.A2); +assertSame(M1, M1.A2.A1); +assertSame(M2, M2.A1.A2); + +// TODO(rossberg): inner declarations are not executed yet. +// assertEquals("1234567890", log); diff --git a/test/mjsunit/harmony/module-parsing.js b/test/mjsunit/harmony/module-parsing.js index 93e69e3..cdd0a2e 100644 --- a/test/mjsunit/harmony/module-parsing.js +++ b/test/mjsunit/harmony/module-parsing.js @@ -70,7 +70,7 @@ module B { import i0 from I import i1, i2, i3, M from I - import i4, i5 from "http://where" + //import i4, i5 from "http://where" } module I { @@ -85,7 +85,7 @@ module D3 = D2 module E1 at "http://where" module E2 at "http://where"; -module E3 = E1.F +module E3 = E1 // Check that ASI does not interfere. @@ -103,11 +103,11 @@ at "file://local" import -x +vx , -y +vy from -"file://local" +B module Wrap { diff --git a/test/mjsunit/harmony/module-resolution.js b/test/mjsunit/harmony/module-resolution.js index f9f492c..a1b9917 100644 --- a/test/mjsunit/harmony/module-resolution.js +++ b/test/mjsunit/harmony/module-resolution.js @@ -129,7 +129,7 @@ export module M2 { export module External at "external.js" export module External1 = External -export module ExternalA = External.A +//export module ExternalA = External.A export module InnerExternal { export module E at "external.js" } -- 2.7.4