Chromium Code Reviews| Index: src/full-codegen.cc |
| diff --git a/src/full-codegen.cc b/src/full-codegen.cc |
| index 9592e0afa21c45aec364adb1e1557fae8f4d0551..cc86cd9fd7924d9e742a3e9d7483d57a0fcb3387 100644 |
| --- a/src/full-codegen.cc |
| +++ b/src/full-codegen.cc |
| @@ -86,6 +86,10 @@ void BreakableStatementChecker::VisitModuleUrl(ModuleUrl* module) { |
| } |
| +void BreakableStatementChecker::VisitModuleStatement(ModuleStatement* stmt) { |
| +} |
| + |
| + |
| void BreakableStatementChecker::VisitBlock(Block* stmt) { |
| } |
| @@ -582,16 +586,137 @@ void FullCodeGenerator::DoTest(const TestContext* context) { |
| } |
| +void FullCodeGenerator::AllocateModules(ZoneList<Declaration*>* declarations) { |
| + ASSERT(scope_->is_global_scope()); |
| + |
| + for (int i = 0; i < declarations->length(); i++) { |
| + ModuleDeclaration* declaration = declarations->at(i)->AsModuleDeclaration(); |
| + if (declaration != NULL) { |
| + ModuleLiteral* module = declaration->module()->AsModuleLiteral(); |
| + if (module != NULL) { |
| + Comment cmnt(masm_, "[ Link nested modules"); |
| + Scope* scope = module->body()->scope(); |
| + Interface* interface = scope->interface(); |
| + ASSERT(interface->IsModule() && interface->IsFrozen()); |
| + |
| + interface->Allocate(scope->module_var()->index()); |
| + |
| + // Set up module context. |
| + ASSERT(scope->interface()->Index() >= 0); |
| + __ push(Immediate(Smi::FromInt(scope->interface()->Index()))); |
| + __ push(Immediate(scope->GetScopeInfo())); |
| + __ CallRuntime(Runtime::kPushModuleContext, 2); |
| + StoreToFrameField(StandardFrameConstants::kContextOffset, |
| + context_register()); |
| + |
| + AllocateModules(scope->declarations()); |
| + |
| + // Pop module context. |
| + LoadContextField(context_register(), Context::PREVIOUS_INDEX); |
| + // Update local stack frame context field. |
| + StoreToFrameField(StandardFrameConstants::kContextOffset, |
| + context_register()); |
| + } |
| + } |
| + } |
| +} |
| + |
| + |
| +// Modules have their own local scope, represented by their own context. |
| +// Module instance objects have an accessor for every export that forwards |
| +// access to the respective slot from the module's context. (Exports that are |
| +// modules themselves, however, are simple data properties.) |
| +// |
| +// All modules have a _hosting_ scope/context, which (currently) is the |
| +// (innermost) enclosing global scope. To deal with recursion, nested modules |
| +// are hosted by the same scope as global ones. |
| +// |
| +// For every (global or nested) module literal, the hosting context has an |
| +// internal slot that points directly to the respective module context. This |
| +// enables quick access to (statically resolved) module members by 2-dimensional |
| +// access through the hosting context. For example, |
| +// |
| +// module A { |
| +// let x; |
| +// module B { let y; } |
| +// } |
| +// module C { let z; } |
| +// |
| +// allocates contexts as follows: |
| +// |
| +// [header| .A | .B | .C | A | C ] (global) |
| +// | | | |
| +// | | +-- [header| z ] (module) |
| +// | | |
| +// | +------- [header| y ] (module) |
| +// | |
| +// +------------ [header| x | B ] (module) |
| +// |
| +// Here, .A, .B, .C are the internal slots pointing to the hosted module |
| +// contexts, whereas A, B, C hold the actual instance objects (note that every |
| +// module context also points to the respective instance object through its |
| +// extension slot in the header). |
| +// |
| +// To deal with arbitrary recursion and aliases between modules, |
| +// they are created and initialized in several stages. Each stage applies to |
| +// all modules in the hosting global scope, including nested ones. |
| +// |
| +// 1. Allocate: for each module _literal_, allocate the module contexts and |
| +// respective instance object and wire them up. This happens in the |
| +// PushModuleContext runtime function, as generated by AllocateModules |
| +// (invoked by VisitDeclarations in the hosting scope). |
| +// |
| +// 2. Bind: for each module _declaration_ (i.e. literals as well as aliases), |
| +// assign the respective instance object to respective local variables. This |
| +// happens in VisitModuleDeclaration, and uses the instance objects created |
| +// in the previous stage. |
| +// For each module _literal_, this phase also constructs a module descriptor |
| +// for the next stage. This happens in VisitModuleLiteral. |
| +// |
| +// 3. Populate: invoke the DeclareModules runtime function to populate each |
| +// _instance_ object with accessors for it exports. This is generated by |
| +// DeclareModules (invoked by VisitDeclarations in the hosting scope again), |
| +// and uses the descriptors generated in the previous stage. |
| +// |
| +// 4. Initialize: execute the module bodies (and other code) in sequence. This |
| +// happens by the separate statements generated for module bodies. To reenter |
| +// the module scopes properly, the parser inserted ModuleStatements. |
| + |
| void FullCodeGenerator::VisitDeclarations( |
| ZoneList<Declaration*>* declarations) { |
| + Handle<FixedArray> saved_modules = modules_; |
| + int saved_module_index = module_index_; |
| ZoneList<Handle<Object> >* saved_globals = globals_; |
| ZoneList<Handle<Object> > inner_globals(10, zone()); |
| globals_ = &inner_globals; |
| + if (scope_->num_modules() != 0) { |
| + // This is a scope hosting modules. Allocate a descriptor array to pass |
| + // to the runtime for initialization. |
| + Comment cmnt(masm_, "[ Allocate modules"); |
| + ASSERT(scope_->is_global_scope()); |
| + modules_ = |
| + isolate()->factory()->NewFixedArray(scope_->num_modules(), TENURED); |
| + module_index_ = 0; |
| + |
| + // Generate code for allocating all modules, including nested ones. |
| + // The allocated contexts are stored in internal variables in this scope. |
| + AllocateModules(declarations); |
| + } |
| + |
| AstVisitor::VisitDeclarations(declarations); |
| + |
| + if (scope_->num_modules() != 0) { |
| + // Initialize modules from descriptor array. |
| + ASSERT(module_index_ == modules_->length()); |
| + DeclareModules(modules_); |
| + modules_ = saved_modules; |
| + module_index_ = saved_module_index; |
| + } |
| + |
| if (!globals_->is_empty()) { |
| // Invoke the platform-dependent code generator to do the actual |
| - // declaration the global functions and variables. |
| + // declaration of the global functions and variables. |
| Handle<FixedArray> array = |
| isolate()->factory()->NewFixedArray(globals_->length(), TENURED); |
| for (int i = 0; i < globals_->length(); ++i) |
| @@ -604,19 +729,23 @@ void FullCodeGenerator::VisitDeclarations( |
| void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) { |
| - // Allocate a module context statically. |
| Block* block = module->body(); |
| Scope* saved_scope = scope(); |
| scope_ = block->scope(); |
| - Interface* interface = module->interface(); |
| - Handle<JSModule> instance = interface->Instance(); |
| + Interface* interface = scope_->interface(); |
| Comment cmnt(masm_, "[ ModuleLiteral"); |
| SetStatementPosition(block); |
| + ASSERT(!modules_.is_null()); |
| + ASSERT(module_index_ < modules_->length()); |
| + int index = module_index_++; |
| + |
| // Set up module context. |
| - __ Push(instance); |
| - __ CallRuntime(Runtime::kPushModuleContext, 1); |
| + ASSERT(interface->Index() >= 0); |
| + __ push(Immediate(Smi::FromInt(interface->Index()))); |
|
Michael Starzinger
2012/11/20 12:05:05
See comment further down the file.
rossberg
2012/11/20 17:23:45
Done.
|
| + __ push(Immediate(Smi::FromInt(0))); |
| + __ CallRuntime(Runtime::kPushModuleContext, 2); |
| StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); |
| { |
| @@ -624,6 +753,39 @@ void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) { |
| VisitDeclarations(scope_->declarations()); |
| } |
| + // Populate the module description. The format is: |
| + // |
| + // Index, (Name, VariableMode, Index)* |
| + // |
| + // That is, the first slot is the index of the module's context in the |
| + // host context, then a sequence of triples follows that describes its |
| + // exports. Index is either the slot index in the module context, or |
| + // (for exported modules) the slot index of the referred module's |
| + // context in the host context. |
|
Sven Panne
2012/11/20 14:39:13
Can we abstract this index magic into a separate c
rossberg
2012/11/20 17:23:45
Done. Of course, in C++ that means thrice as much
|
| + // TODO(rossberg): Cannot yet handle exports of modules declared in |
| + // earlier scripts. |
| + Handle<FixedArray> description = |
| + isolate()->factory()->NewFixedArray(1 + 3 * interface->Length()); |
| + description->set(0, Smi::FromInt(interface->Index())); |
| + int i = 1; |
| + for (Interface::Iterator it = interface->iterator(); |
| + !it.done(); it.Advance()) { |
| + Variable* var = scope_->LocalLookup(it.name()); |
| + description->set(i++, *it.name()); |
| + description->set(i++, Smi::FromInt(var->mode())); |
| + ASSERT((var->mode() == MODULE) == (it.interface()->IsModule())); |
| + if (var->mode() == MODULE) { |
| + ASSERT(it.interface()->IsFrozen()); |
| + ASSERT(it.interface()->Index() >= 0); |
| + description->set(i++, Smi::FromInt(it.interface()->Index())); |
| + } else { |
| + ASSERT(var->index() >= 0); |
| + description->set(i++, Smi::FromInt(var->index())); |
| + } |
| + } |
| + ASSERT(i == description->length()); |
| + modules_->set(index, *description); |
| + |
| scope_ = saved_scope; |
| // Pop module context. |
| LoadContextField(context_register(), Context::PREVIOUS_INDEX); |
| @@ -644,8 +806,21 @@ void FullCodeGenerator::VisitModulePath(ModulePath* module) { |
| } |
| -void FullCodeGenerator::VisitModuleUrl(ModuleUrl* decl) { |
| - // TODO(rossberg) |
| +void FullCodeGenerator::VisitModuleUrl(ModuleUrl* module) { |
| + // TODO(rossberg): dummy allocation for now. |
| + Scope* scope = module->body()->scope(); |
| + Interface* interface = scope_->interface(); |
| + |
| + ASSERT(interface->IsModule() && interface->IsFrozen()); |
| + ASSERT(!modules_.is_null()); |
| + ASSERT(module_index_ < modules_->length()); |
| + interface->Allocate(scope->module_var()->index()); |
| + int index = module_index_++; |
| + |
| + Handle<FixedArray> description = isolate()->factory()->NewFixedArray(2); |
| + description->set(0, Smi::FromInt(interface->Index())); |
| + description->set(1, *scope->GetScopeInfo()); |
|
Michael Starzinger
2012/11/20 12:05:05
Pattern is not GC-safe.
Sven Panne
2012/11/20 14:39:13
See my comment above about FixedArray. The descrip
rossberg
2012/11/20 17:23:45
Obsolete.
rossberg
2012/11/20 17:23:45
That was actually a bug (leftover from earlier for
|
| + modules_->set(index, *description); |
| } |
| @@ -904,37 +1079,28 @@ void FullCodeGenerator::VisitBlock(Block* stmt) { |
| // Push a block context when entering a block with block scoped variables. |
| if (stmt->scope() != NULL) { |
| scope_ = stmt->scope(); |
| - if (scope_->is_module_scope()) { |
| - // If this block is a module body, then we have already allocated and |
| - // initialized the declarations earlier. Just push the context. |
| - ASSERT(!scope_->interface()->Instance().is_null()); |
| - __ Push(scope_->interface()->Instance()); |
| - __ CallRuntime(Runtime::kPushModuleContext, 1); |
| - StoreToFrameField( |
| - StandardFrameConstants::kContextOffset, context_register()); |
| - } else { |
| - { Comment cmnt(masm_, "[ Extend block context"); |
| - Handle<ScopeInfo> scope_info = scope_->GetScopeInfo(); |
| - int heap_slots = |
| - scope_info->ContextLength() - Context::MIN_CONTEXT_SLOTS; |
| - __ Push(scope_info); |
| - PushFunctionArgumentForContextAllocation(); |
| - if (heap_slots <= FastNewBlockContextStub::kMaximumSlots) { |
| - FastNewBlockContextStub stub(heap_slots); |
| - __ CallStub(&stub); |
| - } else { |
| - __ CallRuntime(Runtime::kPushBlockContext, 2); |
| - } |
| - |
| - // Replace the context stored in the frame. |
| - StoreToFrameField(StandardFrameConstants::kContextOffset, |
| - context_register()); |
| - } |
| - { Comment cmnt(masm_, "[ Declarations"); |
| - VisitDeclarations(scope_->declarations()); |
| + ASSERT(!scope_->is_module_scope()); |
| + { Comment cmnt(masm_, "[ Extend block context"); |
| + Handle<ScopeInfo> scope_info = scope_->GetScopeInfo(); |
| + int heap_slots = scope_info->ContextLength() - Context::MIN_CONTEXT_SLOTS; |
| + __ Push(scope_info); |
| + PushFunctionArgumentForContextAllocation(); |
| + if (heap_slots <= FastNewBlockContextStub::kMaximumSlots) { |
| + FastNewBlockContextStub stub(heap_slots); |
| + __ CallStub(&stub); |
| + } else { |
| + __ CallRuntime(Runtime::kPushBlockContext, 2); |
| } |
| + |
| + // Replace the context stored in the frame. |
| + StoreToFrameField(StandardFrameConstants::kContextOffset, |
| + context_register()); |
| + } |
| + { Comment cmnt(masm_, "[ Declarations"); |
| + VisitDeclarations(scope_->declarations()); |
| } |
| } |
| + |
| PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); |
| VisitStatements(stmt->statements()); |
| scope_ = saved_scope; |
| @@ -951,6 +1117,26 @@ void FullCodeGenerator::VisitBlock(Block* stmt) { |
| } |
| +void FullCodeGenerator::VisitModuleStatement(ModuleStatement* stmt) { |
| + Comment cmnt(masm_, "[ Module context"); |
| + |
| + __ push(Immediate(Smi::FromInt(stmt->proxy()->interface()->Index()))); |
|
Michael Starzinger
2012/11/20 12:05:05
You will have a hard time porting this to other ar
rossberg
2012/11/20 17:23:45
Done.
|
| + __ push(Immediate(Smi::FromInt(0))); |
| + __ CallRuntime(Runtime::kPushModuleContext, 2); |
| + StoreToFrameField( |
| + StandardFrameConstants::kContextOffset, context_register()); |
| + |
| + Scope* saved_scope = scope_; |
| + scope_ = stmt->body()->scope(); |
| + VisitStatements(stmt->body()->statements()); |
| + scope_ = saved_scope; |
| + LoadContextField(context_register(), Context::PREVIOUS_INDEX); |
| + // Update local stack frame context field. |
| + StoreToFrameField(StandardFrameConstants::kContextOffset, |
| + context_register()); |
| +} |
| + |
| + |
| void FullCodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) { |
| Comment cmnt(masm_, "[ ExpressionStatement"); |
| SetStatementPosition(stmt); |