Get rid of static module allocation, do it in code.
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 22 Nov 2012 10:25:22 +0000 (10:25 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 22 Nov 2012 10:25:22 +0000 (10:25 +0000)
Modules now 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.

R=mstarzinger@chromium.org,svenpanne@chromium.org
BUG=

Review URL: https://codereview.chromium.org/11093074

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13033 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

35 files changed:
src/arm/full-codegen-arm.cc
src/arm/macro-assembler-arm.h
src/ast.cc
src/ast.h
src/contexts.cc
src/contexts.h
src/full-codegen.cc
src/full-codegen.h
src/heap.cc
src/hydrogen.cc
src/ia32/full-codegen-ia32.cc
src/ia32/macro-assembler-ia32.h
src/interface.cc
src/interface.h
src/objects.cc
src/objects.h
src/parser.cc
src/prettyprinter.cc
src/property-details.h
src/rewriter.cc
src/runtime.cc
src/runtime.h
src/scopeinfo.cc
src/scopeinfo.h
src/scopes.cc
src/scopes.h
src/v8globals.h
src/variables.cc
src/x64/full-codegen-x64.cc
test/cctest/test-decls.cc
test/mjsunit/fuzz-natives-part1.js
test/mjsunit/fuzz-natives-part2.js
test/mjsunit/fuzz-natives-part3.js
test/mjsunit/fuzz-natives-part4.js
test/mjsunit/harmony/module-linking.js

index 83b438b1c7b3ed4be420acbd6861461ab03bb0fc..2e92937d0165700c1ed54d896ae3ebeab20ad1e3 100644 (file)
@@ -917,34 +917,33 @@ void FullCodeGenerator::VisitFunctionDeclaration(
 
 
 void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
-  VariableProxy* proxy = declaration->proxy();
-  Variable* variable = proxy->var();
-  Handle<JSModule> instance = declaration->module()->interface()->Instance();
-  ASSERT(!instance.is_null());
+  Variable* variable = declaration->proxy()->var();
+  ASSERT(variable->location() == Variable::CONTEXT);
+  ASSERT(variable->interface()->IsFrozen());
 
-  switch (variable->location()) {
-    case Variable::UNALLOCATED: {
-      Comment cmnt(masm_, "[ ModuleDeclaration");
-      globals_->Add(variable->name(), zone());
-      globals_->Add(instance, zone());
-      Visit(declaration->module());
-      break;
-    }
+  Comment cmnt(masm_, "[ ModuleDeclaration");
+  EmitDebugCheckDeclarationContext(variable);
 
-    case Variable::CONTEXT: {
-      Comment cmnt(masm_, "[ ModuleDeclaration");
-      EmitDebugCheckDeclarationContext(variable);
-      __ mov(r1, Operand(instance));
-      __ str(r1, ContextOperand(cp, variable->index()));
-      Visit(declaration->module());
-      break;
-    }
+  // Load instance object.
+  __ LoadContext(r1, scope_->ContextChainLength(scope_->GlobalScope()));
+  __ ldr(r1, ContextOperand(r1, variable->interface()->Index()));
+  __ ldr(r1, ContextOperand(r1, Context::EXTENSION_INDEX));
 
-    case Variable::PARAMETER:
-    case Variable::LOCAL:
-    case Variable::LOOKUP:
-      UNREACHABLE();
-  }
+  // Assign it.
+  __ str(r1, ContextOperand(cp, variable->index()));
+  // We know that we have written a module, which is not a smi.
+  __ RecordWriteContextSlot(cp,
+                            Context::SlotOffset(variable->index()),
+                            r1,
+                            r3,
+                            kLRHasBeenSaved,
+                            kDontSaveFPRegs,
+                            EMIT_REMEMBERED_SET,
+                            OMIT_SMI_CHECK);
+  PrepareForBailoutForId(declaration->proxy()->id(), NO_REGISTERS);
+
+  // Traverse into body.
+  Visit(declaration->module());
 }
 
 
@@ -987,6 +986,14 @@ void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
 }
 
 
+void FullCodeGenerator::DeclareModules(Handle<FixedArray> descriptions) {
+  // Call the runtime to declare the modules.
+  __ Push(descriptions);
+  __ CallRuntime(Runtime::kDeclareModules, 1);
+  // Return value is ignored.
+}
+
+
 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
   Comment cmnt(masm_, "[ SwitchStatement");
   Breakable nested_statement(this, stmt);
@@ -1394,9 +1401,9 @@ void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var,
   } else if (var->mode() == DYNAMIC_LOCAL) {
     Variable* local = var->local_if_not_shadowed();
     __ ldr(r0, ContextSlotOperandCheckExtensions(local, slow));
-    if (local->mode() == CONST ||
-        local->mode() == CONST_HARMONY ||
-        local->mode() == LET) {
+    if (local->mode() == LET ||
+        local->mode() == CONST ||
+        local->mode() == CONST_HARMONY) {
       __ CompareRoot(r0, Heap::kTheHoleValueRootIndex);
       if (local->mode() == CONST) {
         __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
index ab8418dabb046fb34c5d1c3b741a58a1d264f506..3c05e003841c6a895308bd63c93e79fa9463439b 100644 (file)
@@ -322,6 +322,7 @@ class MacroAssembler: public Assembler {
 
   // Push a handle.
   void Push(Handle<Object> handle);
+  void Push(Smi* smi) { Push(Handle<Smi>(smi)); }
 
   // Push two registers.  Pushes leftmost register first (to highest address).
   void Push(Register src1, Register src2, Condition cond = al) {
index 9cdae545da4f9d586e0f43fe556c32354103d4a4..232cb739a1db52769d3ea182658c821ac105033c 100644 (file)
@@ -103,6 +103,7 @@ VariableProxy::VariableProxy(Isolate* isolate,
 void VariableProxy::BindTo(Variable* var) {
   ASSERT(var_ == NULL);  // must be bound only once
   ASSERT(var != NULL);  // must bind
+  ASSERT(!FLAG_harmony_modules || interface_->IsUnified(var->interface()));
   ASSERT((is_this() && var->is_this()) || name_.is_identical_to(var->name()));
   // Ideally CONST-ness should match. However, this is very hard to achieve
   // because we don't know the exact semantics of conflicting (const and
@@ -1059,16 +1060,14 @@ REGULAR_NODE(CallNew)
 // LOOKUP variables only result from constructs that cannot be inlined anyway.
 REGULAR_NODE(VariableProxy)
 
-// We currently do not optimize any modules. Note in particular, that module
-// instance objects associated with ModuleLiterals are allocated during
-// scope resolution, and references to them are embedded into the code.
-// That code may hence neither be cached nor re-compiled.
+// We currently do not optimize any modules.
 DONT_OPTIMIZE_NODE(ModuleDeclaration)
 DONT_OPTIMIZE_NODE(ImportDeclaration)
 DONT_OPTIMIZE_NODE(ExportDeclaration)
 DONT_OPTIMIZE_NODE(ModuleVariable)
 DONT_OPTIMIZE_NODE(ModulePath)
 DONT_OPTIMIZE_NODE(ModuleUrl)
+DONT_OPTIMIZE_NODE(ModuleStatement)
 DONT_OPTIMIZE_NODE(WithStatement)
 DONT_OPTIMIZE_NODE(TryCatchStatement)
 DONT_OPTIMIZE_NODE(TryFinallyStatement)
index 6fb930973cc859e08b31f20deb4b09cba9aab43e..d299f19a23511a818b01c5e703e6626fc1df42ac 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -75,6 +75,7 @@ namespace internal {
 
 #define STATEMENT_NODE_LIST(V)                  \
   V(Block)                                      \
+  V(ModuleStatement)                            \
   V(ExpressionStatement)                        \
   V(EmptyStatement)                             \
   V(IfStatement)                                \
@@ -522,7 +523,7 @@ class ModuleDeclaration: public Declaration {
   ModuleDeclaration(VariableProxy* proxy,
                     Module* module,
                     Scope* scope)
-      : Declaration(proxy, LET, scope),
+      : Declaration(proxy, MODULE, scope),
         module_(module) {
   }
 
@@ -645,6 +646,25 @@ class ModuleUrl: public Module {
 };
 
 
+class ModuleStatement: public Statement {
+ public:
+  DECLARE_NODE_TYPE(ModuleStatement)
+
+  VariableProxy* proxy() const { return proxy_; }
+  Block* body() const { return body_; }
+
+ protected:
+  ModuleStatement(VariableProxy* proxy, Block* body)
+      : proxy_(proxy),
+        body_(body) {
+  }
+
+ private:
+  VariableProxy* proxy_;
+  Block* body_;
+};
+
+
 class IterationStatement: public BreakableStatement {
  public:
   // Type testing & conversion.
@@ -1417,7 +1437,7 @@ class VariableProxy: public Expression {
   void MarkAsTrivial() { is_trivial_ = true; }
   void MarkAsLValue() { is_lvalue_ = true; }
 
-  // Bind this proxy to the variable var.
+  // Bind this proxy to the variable var. Interfaces must match.
   void BindTo(Variable* var);
 
  protected:
@@ -2640,6 +2660,11 @@ class AstNodeFactory BASE_EMBEDDED {
   STATEMENT_WITH_LABELS(SwitchStatement)
 #undef STATEMENT_WITH_LABELS
 
+  ModuleStatement* NewModuleStatement(VariableProxy* proxy, Block* body) {
+    ModuleStatement* stmt = new(zone_) ModuleStatement(proxy, body);
+    VISIT_AND_RETURN(ModuleStatement, stmt)
+  }
+
   ExpressionStatement* NewExpressionStatement(Expression* expression) {
     ExpressionStatement* stmt = new(zone_) ExpressionStatement(expression);
     VISIT_AND_RETURN(ExpressionStatement, stmt)
index fbb2f1a33c6f85c86e0978bcc8011630aeb8ef1b..4cb52d3a6c0cd34ee8597ba4f846853cb0f057fc 100644 (file)
@@ -55,6 +55,15 @@ JSBuiltinsObject* Context::builtins() {
 }
 
 
+Context* Context::global_context() {
+  Context* current = this;
+  while (!current->IsGlobalContext()) {
+    current = current->previous();
+  }
+  return current;
+}
+
+
 Context* Context::native_context() {
   // Fast case: the global object for this context has been set.  In
   // that case, the global object has a direct pointer to the global
@@ -183,6 +192,10 @@ Handle<Object> Context::Lookup(Handle<String> name,
                 ? IMMUTABLE_CHECK_INITIALIZED_HARMONY :
                 IMMUTABLE_IS_INITIALIZED_HARMONY;
             break;
+          case MODULE:
+            *attributes = READ_ONLY;
+            *binding_flags = IMMUTABLE_IS_INITIALIZED_HARMONY;
+            break;
           case DYNAMIC:
           case DYNAMIC_GLOBAL:
           case DYNAMIC_LOCAL:
index 7a3ab49dda471cae9ab14df6dc51b85e93433b0b..592b548cb6d226ed02cba48d2b325e30ef249286 100644 (file)
@@ -345,6 +345,9 @@ class Context: public FixedArray {
   // The builtins object.
   JSBuiltinsObject* builtins();
 
+  // Get the innermost global context by traversing the context chain.
+  Context* global_context();
+
   // Compute the native context by traversing the context chain.
   Context* native_context();
 
index 9592e0afa21c45aec364adb1e1557fae8f4d0551..01d9bd0714be04b4ff4de2f7a66e98da32c67349 100644 (file)
@@ -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(Smi::FromInt(scope->interface()->Index()));
+        __ Push(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(Smi::FromInt(interface->Index()));
+  __ Push(Smi::FromInt(0));
+  __ CallRuntime(Runtime::kPushModuleContext, 2);
   StoreToFrameField(StandardFrameConstants::kContextOffset, context_register());
 
   {
@@ -624,6 +753,11 @@ void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) {
     VisitDeclarations(scope_->declarations());
   }
 
+  // Populate the module description.
+  Handle<ModuleInfo> description =
+      ModuleInfo::Create(isolate(), interface, scope_);
+  modules_->set(index, *description);
+
   scope_ = saved_scope;
   // Pop module context.
   LoadContextField(context_register(), Context::PREVIOUS_INDEX);
@@ -644,8 +778,20 @@ 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<ModuleInfo> description =
+      ModuleInfo::Create(isolate(), interface, scope_);
+  modules_->set(index, *description);
 }
 
 
@@ -904,37 +1050,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 +1088,26 @@ void FullCodeGenerator::VisitBlock(Block* stmt) {
 }
 
 
+void FullCodeGenerator::VisitModuleStatement(ModuleStatement* stmt) {
+  Comment cmnt(masm_, "[ Module context");
+
+  __ Push(Smi::FromInt(stmt->proxy()->interface()->Index()));
+  __ Push(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);
index 89b51f9582b2a3520af96155f3817ca12831ac91..cfa7da3c741663f63a66f5b4ce21f4e16d2279e1 100644 (file)
@@ -396,9 +396,15 @@ class FullCodeGenerator: public AstVisitor {
   void VisitInDuplicateContext(Expression* expr);
 
   void VisitDeclarations(ZoneList<Declaration*>* declarations);
+  void DeclareModules(Handle<FixedArray> descriptions);
   void DeclareGlobals(Handle<FixedArray> pairs);
   int DeclareGlobalsFlags();
 
+  // Generate code to allocate all (including nested) modules and contexts.
+  // Because of recursive linking and the presence of module alias declarations,
+  // this has to be a separate pass _before_ populating or executing any module.
+  void AllocateModules(ZoneList<Declaration*>* declarations);
+
   // Try to perform a comparison as a fast inlined literal compare if
   // the operands allow it.  Returns true if the compare operations
   // has been matched and all code generated; false otherwise.
@@ -804,6 +810,8 @@ class FullCodeGenerator: public AstVisitor {
   NestedStatement* nesting_stack_;
   int loop_depth_;
   ZoneList<Handle<Object> >* globals_;
+  Handle<FixedArray> modules_;
+  int module_index_;
   const ExpressionContext* context_;
   ZoneList<BailoutEntry> bailout_entries_;
   ZoneList<BailoutEntry> stack_checks_;
index 976d65e959fdc35e50a8efe8964ff9e00a1388c4..75a73a1dfa4fe7b79662d92d4bc4e220aeddf401 100644 (file)
@@ -5141,7 +5141,7 @@ MaybeObject* Heap::AllocateModuleContext(ScopeInfo* scope_info) {
   }
   Context* context = reinterpret_cast<Context*>(result);
   context->set_map_no_write_barrier(module_context_map());
-  // Context links will be set later.
+  // Instance link will be set later.
   context->set_extension(Smi::FromInt(0));
   return context;
 }
index ddf86c5e4706c7e2168d6ace7096bb3edb337db9..ddff6c0ff5092f5836acd12791f597d03d8fa6e5 100644 (file)
@@ -8996,6 +8996,11 @@ void HGraphBuilder::VisitModuleUrl(ModuleUrl* module) {
 }
 
 
+void HGraphBuilder::VisitModuleStatement(ModuleStatement* stmt) {
+  UNREACHABLE();
+}
+
+
 // Generators for inline runtime functions.
 // Support for types.
 void HGraphBuilder::GenerateIsSmi(CallRuntime* call) {
index c7c6f8a509df1d84720224c68e98659b9fd57fe7..88f204b139604237ab62f45982e2919a2131c1aa 100644 (file)
@@ -758,8 +758,7 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr,
 
 
 void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) {
-  // The variable in the declaration always resides in the current function
-  // context.
+  // The variable in the declaration always resides in the current context.
   ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
   if (generate_debug_code_) {
     // Check that we're not inside a with or catch context.
@@ -888,33 +887,32 @@ void FullCodeGenerator::VisitFunctionDeclaration(
 
 
 void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
-  VariableProxy* proxy = declaration->proxy();
-  Variable* variable = proxy->var();
-  Handle<JSModule> instance = declaration->module()->interface()->Instance();
-  ASSERT(!instance.is_null());
+  Variable* variable = declaration->proxy()->var();
+  ASSERT(variable->location() == Variable::CONTEXT);
+  ASSERT(variable->interface()->IsFrozen());
 
-  switch (variable->location()) {
-    case Variable::UNALLOCATED: {
-      Comment cmnt(masm_, "[ ModuleDeclaration");
-      globals_->Add(variable->name(), zone());
-      globals_->Add(instance, zone());
-      Visit(declaration->module());
-      break;
-    }
+  Comment cmnt(masm_, "[ ModuleDeclaration");
+  EmitDebugCheckDeclarationContext(variable);
 
-    case Variable::CONTEXT: {
-      Comment cmnt(masm_, "[ ModuleDeclaration");
-      EmitDebugCheckDeclarationContext(variable);
-      __ mov(ContextOperand(esi, variable->index()), Immediate(instance));
-      Visit(declaration->module());
-      break;
-    }
+  // Load instance object.
+  __ LoadContext(eax, scope_->ContextChainLength(scope_->GlobalScope()));
+  __ mov(eax, ContextOperand(eax, variable->interface()->Index()));
+  __ mov(eax, ContextOperand(eax, Context::EXTENSION_INDEX));
 
-    case Variable::PARAMETER:
-    case Variable::LOCAL:
-    case Variable::LOOKUP:
-      UNREACHABLE();
-  }
+  // Assign it.
+  __ mov(ContextOperand(esi, variable->index()), eax);
+  // We know that we have written a module, which is not a smi.
+  __ RecordWriteContextSlot(esi,
+                            Context::SlotOffset(variable->index()),
+                            eax,
+                            ecx,
+                            kDontSaveFPRegs,
+                            EMIT_REMEMBERED_SET,
+                            OMIT_SMI_CHECK);
+  PrepareForBailoutForId(declaration->proxy()->id(), NO_REGISTERS);
+
+  // Traverse into body.
+  Visit(declaration->module());
 }
 
 
@@ -949,13 +947,21 @@ void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* declaration) {
 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
   // Call the runtime to declare the globals.
   __ push(esi);  // The context is the first argument.
-  __ push(Immediate(pairs));
-  __ push(Immediate(Smi::FromInt(DeclareGlobalsFlags())));
+  __ Push(pairs);
+  __ Push(Smi::FromInt(DeclareGlobalsFlags()));
   __ CallRuntime(Runtime::kDeclareGlobals, 3);
   // Return value is ignored.
 }
 
 
+void FullCodeGenerator::DeclareModules(Handle<FixedArray> descriptions) {
+  // Call the runtime to declare the modules.
+  __ Push(descriptions);
+  __ CallRuntime(Runtime::kDeclareModules, 1);
+  // Return value is ignored.
+}
+
+
 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
   Comment cmnt(masm_, "[ SwitchStatement");
   Breakable nested_statement(this, stmt);
@@ -1350,9 +1356,9 @@ void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var,
   } else if (var->mode() == DYNAMIC_LOCAL) {
     Variable* local = var->local_if_not_shadowed();
     __ mov(eax, ContextSlotOperandCheckExtensions(local, slow));
-    if (local->mode() == CONST ||
-        local->mode() == CONST_HARMONY ||
-        local->mode() == LET) {
+    if (local->mode() == LET ||
+        local->mode() == CONST ||
+        local->mode() == CONST_HARMONY) {
       __ cmp(eax, isolate()->factory()->the_hole_value());
       __ j(not_equal, done);
       if (local->mode() == CONST) {
index 3c74f193a6825e91357b43297769fa063d91477e..7abb29b10aa2b6bf9cc4232cb4163b3c562655f3 100644 (file)
@@ -789,6 +789,7 @@ class MacroAssembler: public Assembler {
 
   // Push a handle value.
   void Push(Handle<Object> handle) { push(Immediate(handle)); }
+  void Push(Smi* smi) { Push(Handle<Smi>(smi)); }
 
   Handle<Object> CodeObject() {
     ASSERT(!code_object_.is_null());
index 336be82c60f28089ac26d364c6d79771f7a279d1..1634a37113fca9c3266d9ca248354250725b1fb0 100644 (file)
@@ -170,6 +170,8 @@ void Interface::DoUnify(Interface* that, bool* ok, Zone* zone) {
   ASSERT(that->forward_ == NULL);
   ASSERT(!this->IsValue());
   ASSERT(!that->IsValue());
+  ASSERT(this->index_ == -1);
+  ASSERT(that->index_ == -1);
   ASSERT(*ok);
 
 #ifdef DEBUG
@@ -194,15 +196,6 @@ void Interface::DoUnify(Interface* that, bool* ok, Zone* zone) {
     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;
@@ -227,7 +220,7 @@ void Interface::Print(int n) {
   } else if (IsValue()) {
     PrintF("value\n");
   } else if (IsModule()) {
-    PrintF("module %s{", IsFrozen() ? "" : "(unresolved) ");
+    PrintF("module %d %s{", Index(), IsFrozen() ? "" : "(unresolved) ");
     ZoneHashMap* map = Chase()->exports_;
     if (map == NULL || map->occupancy() == 0) {
       PrintF("}\n");
index 94ef11ba5c29d40f40e3755cafd16f2837252cfc..f824a9a8749b91b59805034d924eff5fd70f7fab 100644 (file)
@@ -108,18 +108,18 @@ class Interface : public ZoneObject {
     if (*ok) Chase()->flags_ |= MODULE;
   }
 
-  // Set associated instance object.
-  void MakeSingleton(Handle<JSModule> 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();
     if (*ok) Chase()->flags_ |= FROZEN;
   }
 
+  // Assign an index.
+  void Allocate(int index) {
+    ASSERT(IsModule() && IsFrozen() && Chase()->index_ == -1);
+    Chase()->index_ = index;
+  }
+
   // ---------------------------------------------------------------------------
   // Accessors.
 
@@ -138,7 +138,23 @@ class Interface : public ZoneObject {
   // Check whether this is closed (i.e. fully determined).
   bool IsFrozen() { return Chase()->flags_ & FROZEN; }
 
-  Handle<JSModule> Instance() { return Chase()->instance_; }
+  bool IsUnified(Interface* that) {
+    return Chase() == that->Chase()
+        || (this->IsValue() == that->IsValue() &&
+            this->IsConst() == that->IsConst());
+  }
+
+  int Length() {
+    ASSERT(IsModule() && IsFrozen());
+    ZoneHashMap* exports = Chase()->exports_;
+    return exports ? exports->occupancy() : 0;
+  }
+
+  // The context slot in the hosting global context pointing to this module.
+  int Index() {
+    ASSERT(IsModule() && IsFrozen());
+    return Chase()->index_;
+  }
 
   // Look up an exported name. Returns NULL if not (yet) defined.
   Interface* Lookup(Handle<String> name, Zone* zone);
@@ -194,12 +210,13 @@ class Interface : public ZoneObject {
   int flags_;
   Interface* forward_;     // Unification link
   ZoneHashMap* exports_;   // Module exports and their types (allocated lazily)
-  Handle<JSModule> instance_;
+  int index_;
 
   explicit Interface(int flags)
     : flags_(flags),
       forward_(NULL),
-      exports_(NULL) {
+      exports_(NULL),
+      index_(-1) {
 #ifdef DEBUG
     if (FLAG_print_interface_details)
       PrintF("# Creating %p\n", static_cast<void*>(this));
index 05c00824009233bb90642c648d78d262f6a73e17..3894eea3e1608eefe2284b26d894260a07a3dc1b 100644 (file)
@@ -1132,6 +1132,10 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) {
       }
       break;
     }
+    case JS_MODULE_TYPE: {
+      accumulator->Add("<JS Module>");
+      break;
+    }
     // All other JSObjects are rather similar to each other (JSObject,
     // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
     default: {
index 275c706d3bc8a8b8ff6f0dc824be8ae4488c428f..e9a1e935f5e937a40dcc81aff151585389a53a55 100644 (file)
@@ -6318,7 +6318,7 @@ class GlobalObject: public JSObject {
       Handle<GlobalObject> global,
       Handle<String> name);
   // TODO(kmillikin): This function can be eliminated once the stub cache is
-  // full handlified (and the static helper can be written directly).
+  // fully handlified (and the static helper can be written directly).
   MUST_USE_RESULT MaybeObject* EnsurePropertyCell(String* name);
 
   // Casting.
index 16f41c2ab71c9b4aab697b1973ccc0e8a10b3ccc..a7bb4e7985f7a3ac75de531d41415a4d73f47b5a 100644 (file)
@@ -1160,7 +1160,7 @@ Statement* Parser::ParseModuleDeclaration(ZoneStringList* names, bool* ok) {
 #endif
 
   Module* module = ParseModule(CHECK_OK);
-  VariableProxy* proxy = NewUnresolved(name, LET, module->interface());
+  VariableProxy* proxy = NewUnresolved(name, MODULE, module->interface());
   Declaration* declaration =
       factory()->NewModuleDeclaration(proxy, module, top_scope_);
   Declare(declaration, true, CHECK_OK);
@@ -1179,7 +1179,7 @@ Statement* Parser::ParseModuleDeclaration(ZoneStringList* names, bool* ok) {
   if (module->body() == NULL)
     return factory()->NewEmptyStatement();
   else
-    return module->body();
+    return factory()->NewModuleStatement(proxy, module->body());
 }
 
 
@@ -1328,12 +1328,15 @@ Module* Parser::ParseModuleUrl(bool* ok) {
   if (FLAG_print_interface_details) PrintF("# Url ");
 #endif
 
-  Module* result = factory()->NewModuleUrl(symbol);
-  Interface* interface = result->interface();
+  // Create an empty literal as long as the feature isn't finished.
+  USE(symbol);
+  Scope* scope = NewScope(top_scope_, MODULE_SCOPE);
+  Block* body = factory()->NewBlock(NULL, 1, false);
+  body->set_scope(scope);
+  Interface* interface = scope->interface();
+  Module* result = factory()->NewModuleLiteral(body, interface);
   interface->Freeze(ok);
   ASSERT(*ok);
-  // Create dummy scope to avoid errors as long as the feature isn't finished.
-  Scope* scope = NewScope(top_scope_, MODULE_SCOPE);
   interface->Unify(scope->interface(), zone(), ok);
   ASSERT(*ok);
   return result;
@@ -1702,10 +1705,9 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
         *ok = false;
         return;
       }
-      const char* type =
-          (var->mode() == VAR) ? "var" : var->is_const_mode() ? "const" : "let";
       Handle<String> type_string =
-          isolate()->factory()->NewStringFromUtf8(CStrVector(type), TENURED);
+          isolate()->factory()->NewStringFromUtf8(CStrVector("Variable"),
+                                                  TENURED);
       Expression* expression =
           NewThrowTypeError(isolate()->factory()->redeclaration_symbol(),
                             type_string, name);
index 0d8dadce1a0257f7eef04fe12ade67cd157dd62c..602fbb40b9840178da9da9c48c24483f72daa26f 100644 (file)
@@ -122,6 +122,14 @@ void PrettyPrinter::VisitModuleUrl(ModuleUrl* node) {
 }
 
 
+void PrettyPrinter::VisitModuleStatement(ModuleStatement* node) {
+  Print("module ");
+  PrintLiteral(node->proxy()->name(), false);
+  Print(" ");
+  Visit(node->body());
+}
+
+
 void PrettyPrinter::VisitExpressionStatement(ExpressionStatement* node) {
   Visit(node->expression());
   Print(";");
@@ -822,6 +830,13 @@ void AstPrinter::VisitModuleUrl(ModuleUrl* node) {
 }
 
 
+void AstPrinter::VisitModuleStatement(ModuleStatement* node) {
+  IndentedScope indent(this, "MODULE");
+  PrintLiteralIndented("NAME", node->proxy()->name(), true);
+  PrintStatements(node->body()->statements());
+}
+
+
 void AstPrinter::VisitExpressionStatement(ExpressionStatement* node) {
   Visit(node->expression());
 }
index 64e320514e3ca41f0a3bc7275ffa2718abf19761..510e9852a987e0c175ac9121d6da77cbd6458b43 100644 (file)
@@ -38,6 +38,10 @@ enum PropertyAttributes {
   READ_ONLY         = v8::ReadOnly,
   DONT_ENUM         = v8::DontEnum,
   DONT_DELETE       = v8::DontDelete,
+
+  SEALED            = DONT_ENUM | DONT_DELETE,
+  FROZEN            = SEALED | READ_ONLY,
+
   ABSENT            = 16  // Used in runtime to indicate a property is absent.
   // ABSENT can never be stored in or returned from a descriptor's attributes
   // bitfield.  It is only used as a return value meaning the attributes of
index 6541546cb6023f8a72e918b83952dacd46b5e8c0..2a98787177b614258727d3c793492a09bd75c42b 100644 (file)
@@ -109,6 +109,13 @@ void Processor::VisitBlock(Block* node) {
 }
 
 
+void Processor::VisitModuleStatement(ModuleStatement* node) {
+  bool set_after_body = is_set_;
+  Visit(node->body());
+  is_set_ = is_set_ && set_after_body;
+}
+
+
 void Processor::VisitExpressionStatement(ExpressionStatement* node) {
   // Rewrite : <x>; -> .result = <x>;
   if (!is_set_ && !node->expression()->IsThrow()) {
@@ -257,7 +264,7 @@ bool Rewriter::Rewrite(CompilationInfo* info) {
       // coincides with the end of the with scope which is the position of '1'.
       int position = function->end_position();
       VariableProxy* result_proxy = processor.factory()->NewVariableProxy(
-          result->name(), false, Interface::NewValue(), position);
+          result->name(), false, result->interface(), position);
       result_proxy->BindTo(result);
       Statement* result_statement =
           processor.factory()->NewReturnStatement(result_proxy);
index 183be857a41dafa019bc3f86cb522c2f4b33e9ca..a15e1f50539de0b18ac3d4526f83d63cb1076eb5 100644 (file)
@@ -1282,8 +1282,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->IsJSModule();
-    ASSERT(is_var + is_const + is_function + is_module == 1);
+    ASSERT(is_var + is_const + is_function == 1);
 
     if (is_var || is_const) {
       // Lookup the property in the global object, and don't set the
@@ -1321,24 +1320,23 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) {
     // the property must be non-configurable except in eval.
     int attr = NONE;
     bool is_eval = DeclareGlobalsEvalFlag::decode(flags);
-    if (!is_eval || is_module) {
+    if (!is_eval) {
       attr |= DONT_DELETE;
     }
     bool is_native = DeclareGlobalsNativeFlag::decode(flags);
-    if (is_const || is_module || (is_native && is_function)) {
+    if (is_const || (is_native && is_function)) {
       attr |= READ_ONLY;
     }
 
     LanguageMode language_mode = DeclareGlobalsLanguageMode::decode(flags);
 
-    if (!lookup.IsFound() || is_function || is_module) {
+    if (!lookup.IsFound() || is_function) {
       // If the local property exists, check that we can reconfigure it
       // as required for function declarations.
       if (lookup.IsFound() && lookup.IsDontDelete()) {
         if (lookup.IsReadOnly() || lookup.IsDontEnum() ||
             lookup.IsPropertyCallbacks()) {
-          return ThrowRedeclarationError(
-              isolate, is_function ? "function" : "module", name);
+          return ThrowRedeclarationError(isolate, "function", name);
         }
         // If the existing property is not configurable, keep its attributes.
         attr = lookup.GetAttributes();
@@ -8419,20 +8417,89 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSModule) {
 
 
 RUNTIME_FUNCTION(MaybeObject*, Runtime_PushModuleContext) {
-  NoHandleAllocation ha;
-  ASSERT(args.length() == 1);
-  CONVERT_ARG_HANDLE_CHECKED(JSModule, instance, 0);
+  ASSERT(args.length() == 2);
+  CONVERT_SMI_ARG_CHECKED(index, 0);
+
+  if (!args[1]->IsScopeInfo()) {
+    // Module already initialized. Find hosting context and retrieve context.
+    Context* host = Context::cast(isolate->context())->global_context();
+    Context* context = Context::cast(host->get(index));
+    ASSERT(context->previous() == isolate->context());
+    isolate->set_context(context);
+    return context;
+  }
+
+  CONVERT_ARG_HANDLE_CHECKED(ScopeInfo, scope_info, 1);
 
-  Context* context = Context::cast(instance->context());
+  // Allocate module context.
+  HandleScope scope(isolate);
+  Factory* factory = isolate->factory();
+  Handle<Context> context = factory->NewModuleContext(scope_info);
+  Handle<JSModule> module = factory->NewJSModule(context, scope_info);
+  context->set_module(*module);
   Context* previous = isolate->context();
-  ASSERT(context->IsModuleContext());
-  // Initialize the context links.
   context->set_previous(previous);
   context->set_closure(previous->closure());
   context->set_global_object(previous->global_object());
-  isolate->set_context(context);
+  isolate->set_context(*context);
 
-  return context;
+  // Find hosting scope and initialize internal variable holding module there.
+  previous->global_context()->set(index, *context);
+
+  return *context;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareModules) {
+  HandleScope scope(isolate);
+  ASSERT(args.length() == 1);
+  CONVERT_ARG_HANDLE_CHECKED(FixedArray, descriptions, 0);
+  Context* host_context = isolate->context();
+
+  for (int i = 0; i < descriptions->length(); ++i) {
+    Handle<ModuleInfo> description(ModuleInfo::cast(descriptions->get(i)));
+    int host_index = description->host_index();
+    Handle<Context> context(Context::cast(host_context->get(host_index)));
+    Handle<JSModule> module(context->module());
+
+    for (int j = 0; j < description->length(); ++j) {
+      Handle<String> name(description->name(j));
+      VariableMode mode = description->mode(j);
+      int index = description->index(j);
+      switch (mode) {
+        case VAR:
+        case LET:
+        case CONST:
+        case CONST_HARMONY: {
+          PropertyAttributes attr =
+              IsImmutableVariableMode(mode) ? FROZEN : SEALED;
+          Handle<AccessorInfo> info =
+              Accessors::MakeModuleExport(name, index, attr);
+          Handle<Object> result = SetAccessor(module, info);
+          ASSERT(!(result.is_null() || result->IsUndefined()));
+          USE(result);
+          break;
+        }
+        case MODULE: {
+          Object* referenced_context = Context::cast(host_context)->get(index);
+          Handle<JSModule> value(Context::cast(referenced_context)->module());
+          JSReceiver::SetProperty(module, name, value, FROZEN, kStrictMode);
+          break;
+        }
+        case INTERNAL:
+        case TEMPORARY:
+        case DYNAMIC:
+        case DYNAMIC_GLOBAL:
+        case DYNAMIC_LOCAL:
+          UNREACHABLE();
+      }
+    }
+
+    JSObject::PreventExtensions(module);
+  }
+
+  ASSERT(!isolate->has_pending_exception());
+  return isolate->heap()->undefined_value();
 }
 
 
index 146b8dc48fafa4d885c37c975da1660a20379a81..19ff62def296aa8e3e23048e22d2219cc324df26 100644 (file)
@@ -352,7 +352,7 @@ namespace internal {
   F(PushWithContext, 2, 1) \
   F(PushCatchContext, 3, 1) \
   F(PushBlockContext, 2, 1) \
-  F(PushModuleContext, 1, 1) \
+  F(PushModuleContext, 2, 1) \
   F(DeleteContextSlot, 2, 1) \
   F(LoadContextSlot, 2, 2) \
   F(LoadContextSlotNoReferenceError, 2, 2) \
@@ -360,6 +360,7 @@ namespace internal {
   \
   /* Declarations and initialization */ \
   F(DeclareGlobals, 3, 1) \
+  F(DeclareModules, 1, 1) \
   F(DeclareContextSlot, 4, 1) \
   F(InitializeVarGlobal, -1 /* 2 or 3 */, 1) \
   F(InitializeConstGlobal, 2, 1) \
index 02b4323980f30218e51059a5ebc287fd64c72b09..c0b2c4c8e6cf4799ccf7a5257cea87ba989f1c31 100644 (file)
@@ -321,6 +321,7 @@ int ScopeInfo::ContextSlotIndex(String* name,
         return result;
       }
     }
+    // Cache as not found. Mode and init flag don't matter.
     context_slot_cache->Update(this, name, INTERNAL, kNeedsInitialization, -1);
   }
   return -1;
@@ -504,4 +505,32 @@ void ScopeInfo::Print() {
 }
 #endif  // DEBUG
 
+
+//---------------------------------------------------------------------------
+// ModuleInfo.
+
+Handle<ModuleInfo> ModuleInfo::Create(
+    Isolate* isolate, Interface* interface, Scope* scope) {
+  Handle<ModuleInfo> info = Allocate(isolate, interface->Length());
+  info->set_host_index(interface->Index());
+  int i = 0;
+  for (Interface::Iterator it = interface->iterator();
+       !it.done(); it.Advance(), ++i) {
+    Variable* var = scope->LocalLookup(it.name());
+    info->set_name(i, *it.name());
+    info->set_mode(i, var->mode());
+    ASSERT((var->mode() == MODULE) == (it.interface()->IsModule()));
+    if (var->mode() == MODULE) {
+      ASSERT(it.interface()->IsFrozen());
+      ASSERT(it.interface()->Index() >= 0);
+      info->set_index(i, it.interface()->Index());
+    } else {
+      ASSERT(var->index() >= 0);
+      info->set_index(i, var->index());
+    }
+  }
+  ASSERT(i == info->length());
+  return info;
+}
+
 } }  // namespace v8::internal
index 93734f5a16c2b4842915cc83b46a0c8a77f25eb3..a884b3b9ed8c03713d12414ca951386016cdebf1 100644 (file)
@@ -114,9 +114,9 @@ class ContextSlotCache {
 
     // Bit fields in value_ (type, shift, size). Must be public so the
     // constants can be embedded in generated code.
-    class ModeField:  public BitField<VariableMode,       0, 3> {};
-    class InitField:  public BitField<InitializationFlag, 3, 1> {};
-    class IndexField: public BitField<int,                4, 32-4> {};
+    class ModeField:  public BitField<VariableMode,       0, 4> {};
+    class InitField:  public BitField<InitializationFlag, 4, 1> {};
+    class IndexField: public BitField<int,                5, 32-5> {};
 
    private:
     uint32_t value_;
@@ -130,6 +130,67 @@ class ContextSlotCache {
 };
 
 
+
+
+//---------------------------------------------------------------------------
+// Auxiliary class used for the description of module instances.
+// Used by Runtime_DeclareModules.
+
+class ModuleInfo: public FixedArray {
+ public:
+  static ModuleInfo* cast(Object* description) {
+    return static_cast<ModuleInfo*>(FixedArray::cast(description));
+  }
+
+  static Handle<ModuleInfo> Create(
+      Isolate* isolate, Interface* interface, Scope* scope);
+
+  // Index of module's context in host context.
+  int host_index() { return Smi::cast(get(HOST_OFFSET))->value(); }
+
+  // Name, mode, and index of the i-th export, respectively.
+  // For value exports, the index is the slot of the value in the module
+  // context, for exported modules it is the slot index of the
+  // referred module's context in the host context.
+  // TODO(rossberg): This format cannot yet handle exports of modules declared
+  // in earlier scripts.
+  String* name(int i) { return String::cast(get(name_offset(i))); }
+  VariableMode mode(int i) {
+    return static_cast<VariableMode>(Smi::cast(get(mode_offset(i)))->value());
+  }
+  int index(int i) { return Smi::cast(get(index_offset(i)))->value(); }
+
+  int length() { return (FixedArray::length() - HEADER_SIZE) / ITEM_SIZE; }
+
+ private:
+  // The internal format is: Index, (Name, VariableMode, Index)*
+  enum {
+    HOST_OFFSET,
+    NAME_OFFSET,
+    MODE_OFFSET,
+    INDEX_OFFSET,
+    HEADER_SIZE = NAME_OFFSET,
+    ITEM_SIZE = INDEX_OFFSET - NAME_OFFSET + 1
+  };
+  inline int name_offset(int i) { return NAME_OFFSET + i * ITEM_SIZE; }
+  inline int mode_offset(int i) { return MODE_OFFSET + i * ITEM_SIZE; }
+  inline int index_offset(int i) { return INDEX_OFFSET + i * ITEM_SIZE; }
+
+  static Handle<ModuleInfo> Allocate(Isolate* isolate, int length) {
+    return Handle<ModuleInfo>::cast(
+        isolate->factory()->NewFixedArray(HEADER_SIZE + ITEM_SIZE * length));
+  }
+  void set_host_index(int index) { set(HOST_OFFSET, Smi::FromInt(index)); }
+  void set_name(int i, String* name) { set(name_offset(i), name); }
+  void set_mode(int i, VariableMode mode) {
+    set(mode_offset(i), Smi::FromInt(mode));
+  }
+  void set_index(int i, int index) {
+    set(index_offset(i), Smi::FromInt(index));
+  }
+};
+
+
 } }  // namespace v8::internal
 
 #endif  // V8_SCOPEINFO_H_
index c9612577afa22c56e5461e543c8edcb3a9adeb63..8ef969e653f1229ddf9f5c122b7378507024119a 100644 (file)
@@ -108,6 +108,7 @@ Scope::Scope(Scope* outer_scope, ScopeType type, Zone* zone)
     : isolate_(Isolate::Current()),
       inner_scopes_(4, zone),
       variables_(zone),
+      internals_(4, zone),
       temps_(4, zone),
       params_(4, zone),
       unresolved_(16, zone),
@@ -131,6 +132,7 @@ Scope::Scope(Scope* inner_scope,
     : isolate_(Isolate::Current()),
       inner_scopes_(4, zone),
       variables_(zone),
+      internals_(4, zone),
       temps_(4, zone),
       params_(4, zone),
       unresolved_(16, zone),
@@ -153,6 +155,7 @@ Scope::Scope(Scope* inner_scope, Handle<String> catch_variable_name, Zone* zone)
     : isolate_(Isolate::Current()),
       inner_scopes_(1, zone),
       variables_(zone),
+      internals_(0, zone),
       temps_(0, zone),
       params_(0, zone),
       unresolved_(0, zone),
@@ -197,6 +200,8 @@ void Scope::SetDefaults(ScopeType type,
   num_var_or_const_ = 0;
   num_stack_slots_ = 0;
   num_heap_slots_ = 0;
+  num_modules_ = 0;
+  module_var_ = NULL,
   scope_info_ = scope_info;
   start_position_ = RelocInfo::kNoPosition;
   end_position_ = RelocInfo::kNoPosition;
@@ -375,6 +380,7 @@ void Scope::Initialize() {
 
 Scope* Scope::FinalizeBlockScope() {
   ASSERT(is_block_scope());
+  ASSERT(internals_.is_empty());
   ASSERT(temps_.is_empty());
   ASSERT(params_.is_empty());
 
@@ -515,6 +521,19 @@ void Scope::RemoveUnresolved(VariableProxy* var) {
 }
 
 
+Variable* Scope::NewInternal(Handle<String> name) {
+  ASSERT(!already_resolved());
+  Variable* var = new(zone()) Variable(this,
+                                       name,
+                                       INTERNAL,
+                                       false,
+                                       Variable::NORMAL,
+                                       kCreatedInitialized);
+  internals_.Add(var, zone());
+  return var;
+}
+
+
 Variable* Scope::NewTemporary(Handle<String> name) {
   ASSERT(!already_resolved());
   Variable* var = new(zone()) Variable(this,
@@ -615,6 +634,15 @@ void Scope::CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals,
   ASSERT(stack_locals != NULL);
   ASSERT(context_locals != NULL);
 
+  // Collect internals which are always allocated on the heap.
+  for (int i = 0; i < internals_.length(); i++) {
+    Variable* var = internals_[i];
+    if (var->is_used()) {
+      ASSERT(var->IsContextSlot());
+      context_locals->Add(var, zone());
+    }
+  }
+
   // Collect temporaries which are always allocated on the stack.
   for (int i = 0; i < temps_.length(); i++) {
     Variable* var = temps_[i];
@@ -624,9 +652,8 @@ void Scope::CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals,
     }
   }
 
-  ZoneList<VarAndOrder> vars(variables_.occupancy(), zone());
-
   // Collect declared local variables.
+  ZoneList<VarAndOrder> vars(variables_.occupancy(), zone());
   for (VariableMap::Entry* p = variables_.Start();
        p != NULL;
        p = variables_.Next(p)) {
@@ -659,18 +686,18 @@ bool Scope::AllocateVariables(CompilationInfo* info,
   }
   PropagateScopeInfo(outer_scope_calls_non_strict_eval);
 
-  // 2) Resolve variables.
+  // 2) Allocate module instances.
+  if (FLAG_harmony_modules && (is_global_scope() || is_module_scope())) {
+    ASSERT(num_modules_ == 0);
+    AllocateModulesRecursively(this);
+  }
+
+  // 3) Resolve variables.
   if (!ResolveVariablesRecursively(info, factory)) return false;
 
-  // 3) Allocate variables.
+  // 4) Allocate variables.
   AllocateVariablesRecursively();
 
-  // 4) Allocate and link module instance objects.
-  if (FLAG_harmony_modules && (is_global_scope() || is_module_scope())) {
-    AllocateModules(info);
-    LinkModules(info);
-  }
-
   return true;
 }
 
@@ -742,6 +769,15 @@ int Scope::ContextChainLength(Scope* scope) {
 }
 
 
+Scope* Scope::GlobalScope() {
+  Scope* scope = this;
+  while (!scope->is_global_scope()) {
+    scope = scope->outer_scope();
+  }
+  return scope;
+}
+
+
 Scope* Scope::DeclarationScope() {
   Scope* scope = this;
   while (!scope->is_declaration_scope()) {
@@ -915,6 +951,11 @@ void Scope::Print(int n) {
     PrintVar(n1, temps_[i]);
   }
 
+  Indent(n1, "// internal vars\n");
+  for (int i = 0; i < internals_.length(); i++) {
+    PrintVar(n1, internals_[i]);
+  }
+
   Indent(n1, "// local vars\n");
   PrintMap(n1, &variables_);
 
@@ -1065,7 +1106,6 @@ bool Scope::ResolveVariable(CompilationInfo* info,
   }
 
   ASSERT(var != NULL);
-  proxy->BindTo(var);
 
   if (FLAG_harmony_modules) {
     bool ok;
@@ -1101,6 +1141,8 @@ bool Scope::ResolveVariable(CompilationInfo* info,
     }
   }
 
+  proxy->BindTo(var);
+
   return true;
 }
 
@@ -1175,6 +1217,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 (var->mode() == INTERNAL) return true;
   if (is_catch_scope() || is_block_scope() || is_module_scope()) return true;
   if (is_global_scope() && IsLexicalVariableMode(var->mode())) return true;
   return var->has_forced_context_allocation() ||
@@ -1281,15 +1324,17 @@ void Scope::AllocateNonParameterLocals() {
     AllocateNonParameterLocal(temps_[i]);
   }
 
-  ZoneList<VarAndOrder> vars(variables_.occupancy(), zone());
+  for (int i = 0; i < internals_.length(); i++) {
+    AllocateNonParameterLocal(internals_[i]);
+  }
 
+  ZoneList<VarAndOrder> vars(variables_.occupancy(), zone());
   for (VariableMap::Entry* p = variables_.Start();
        p != NULL;
        p = variables_.Next(p)) {
     Variable* var = reinterpret_cast<Variable*>(p->value);
     vars.Add(VarAndOrder(var, p->order), zone());
   }
-
   vars.Sort(VarAndOrder::Compare);
   int var_count = vars.length();
   for (int i = 0; i < var_count; i++) {
@@ -1342,89 +1387,35 @@ void Scope::AllocateVariablesRecursively() {
 }
 
 
-int Scope::StackLocalCount() const {
-  return num_stack_slots() -
-      (function_ != NULL && function_->proxy()->var()->IsStackLocal() ? 1 : 0);
-}
-
-
-int Scope::ContextLocalCount() const {
-  if (num_heap_slots() == 0) return 0;
-  return num_heap_slots() - Context::MIN_CONTEXT_SLOTS -
-      (function_ != NULL && function_->proxy()->var()->IsContextSlot() ? 1 : 0);
-}
-
-
-void Scope::AllocateModules(CompilationInfo* info) {
-  ASSERT(is_global_scope() || is_module_scope());
-
+void Scope::AllocateModulesRecursively(Scope* host_scope) {
+  if (already_resolved()) return;
   if (is_module_scope()) {
     ASSERT(interface_->IsFrozen());
-    ASSERT(scope_info_.is_null());
-
-    // TODO(rossberg): This has to be the initial compilation of this code.
-    // We currently do not allow recompiling any module definitions.
-    Handle<ScopeInfo> scope_info = GetScopeInfo();
-    Factory* factory = info->isolate()->factory();
-    Handle<Context> context = factory->NewModuleContext(scope_info);
-    Handle<JSModule> instance = factory->NewJSModule(context, scope_info);
-    context->set_module(*instance);
-
-    bool ok;
-    interface_->MakeSingleton(instance, &ok);
-    ASSERT(ok);
+    const char raw_name[] = ".module";
+    Handle<String> name = isolate_->factory()->LookupSymbol(
+        Vector<const char>(raw_name, StrLength(raw_name)));
+    ASSERT(module_var_ == NULL);
+    module_var_ = host_scope->NewInternal(name);
+    ++host_scope->num_modules_;
   }
 
-  // Allocate nested modules.
   for (int i = 0; i < inner_scopes_.length(); i++) {
     Scope* inner_scope = inner_scopes_.at(i);
-    if (inner_scope->is_module_scope()) {
-      inner_scope->AllocateModules(info);
-    }
+    inner_scope->AllocateModulesRecursively(host_scope);
   }
 }
 
 
-void Scope::LinkModules(CompilationInfo* info) {
-  ASSERT(is_global_scope() || is_module_scope());
+int Scope::StackLocalCount() const {
+  return num_stack_slots() -
+      (function_ != NULL && function_->proxy()->var()->IsStackLocal() ? 1 : 0);
+}
 
-  if (is_module_scope()) {
-    Handle<JSModule> instance = interface_->Instance();
-
-    // Populate the module instance object.
-    const PropertyAttributes ro_attr =
-        static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE | DONT_ENUM);
-    const PropertyAttributes rw_attr =
-        static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM);
-    for (Interface::Iterator it = interface_->iterator();
-         !it.done(); it.Advance()) {
-      if (it.interface()->IsModule()) {
-        Handle<Object> value = it.interface()->Instance();
-        ASSERT(!value.is_null());
-        JSReceiver::SetProperty(
-            instance, it.name(), value, ro_attr, kStrictMode);
-      } else {
-        Variable* var = LocalLookup(it.name());
-        ASSERT(var != NULL && var->IsContextSlot());
-        PropertyAttributes attr = var->is_const_mode() ? ro_attr : rw_attr;
-        Handle<AccessorInfo> info =
-            Accessors::MakeModuleExport(it.name(), var->index(), attr);
-        Handle<Object> result = SetAccessor(instance, info);
-        ASSERT(!(result.is_null() || result->IsUndefined()));
-        USE(result);
-      }
-    }
-    USE(JSObject::PreventExtensions(instance));
-  }
 
-  // Link nested modules.
-  for (int i = 0; i < inner_scopes_.length(); i++) {
-    Scope* inner_scope = inner_scopes_.at(i);
-    if (inner_scope->is_module_scope()) {
-      inner_scope->LinkModules(info);
-    }
-  }
+int Scope::ContextLocalCount() const {
+  if (num_heap_slots() == 0) return 0;
+  return num_heap_slots() - Context::MIN_CONTEXT_SLOTS -
+      (function_ != NULL && function_->proxy()->var()->IsContextSlot() ? 1 : 0);
 }
 
-
 } }  // namespace v8::internal
index b9d151cba5d69af23793c93e8e54da103fc825c1..c60c2e7d49c2f0587c79120e8e62ba3ea78cef1e 100644 (file)
@@ -186,6 +186,12 @@ class Scope: public ZoneObject {
   // such a variable again if it was added; otherwise this is a no-op.
   void RemoveUnresolved(VariableProxy* var);
 
+  // Creates a new internal variable in this scope.  The name is only used
+  // for printing and cannot be used to find the variable.  In particular,
+  // the only way to get hold of the temporary is by keeping the Variable*
+  // around.
+  Variable* NewInternal(Handle<String> name);
+
   // Creates a new temporary variable in this scope.  The name is only used
   // for printing and cannot be used to find the variable.  In particular,
   // the only way to get hold of the temporary is by keeping the Variable*
@@ -369,6 +375,12 @@ class Scope: public ZoneObject {
   int StackLocalCount() const;
   int ContextLocalCount() const;
 
+  // For global scopes, the number of module literals (including nested ones).
+  int num_modules() const { return num_modules_; }
+
+  // For module scopes, the host scope's internal variable binding this module.
+  Variable* module_var() const { return module_var_; }
+
   // Make sure this scope and all outer scopes are eagerly compiled.
   void ForceEagerCompilation()  { force_eager_compilation_ = true; }
 
@@ -387,6 +399,9 @@ class Scope: public ZoneObject {
   // The number of contexts between this and scope; zero if this == scope.
   int ContextChainLength(Scope* scope);
 
+  // Find the innermost global scope.
+  Scope* GlobalScope();
+
   // Find the first function, global, or eval scope.  This is the scope
   // where var declarations will be hoisted to in the implementation.
   Scope* DeclarationScope();
@@ -441,6 +456,8 @@ class Scope: public ZoneObject {
   // variables may be implicitly 'declared' by being used (possibly in
   // an inner scope) with no intervening with statements or eval calls.
   VariableMap variables_;
+  // Compiler-allocated (user-invisible) internals.
+  ZoneList<Variable*> internals_;
   // Compiler-allocated (user-invisible) temporaries.
   ZoneList<Variable*> temps_;
   // Parameter list in source order.
@@ -494,6 +511,12 @@ class Scope: public ZoneObject {
   int num_stack_slots_;
   int num_heap_slots_;
 
+  // The number of modules (including nested ones).
+  int num_modules_;
+
+  // For module scopes, the host scope's internal variable binding this module.
+  Variable* module_var_;
+
   // Serialized scope info support.
   Handle<ScopeInfo> scope_info_;
   bool already_resolved() { return already_resolved_; }
@@ -578,6 +601,7 @@ class Scope: public ZoneObject {
   void AllocateNonParameterLocal(Variable* var);
   void AllocateNonParameterLocals();
   void AllocateVariablesRecursively();
+  void AllocateModulesRecursively(Scope* host_scope);
 
   // Resolve and fill in the allocation information for all variables
   // in this scopes. Must be called *after* all scopes have been
@@ -591,13 +615,6 @@ class Scope: public ZoneObject {
   bool AllocateVariables(CompilationInfo* info,
                          AstNodeFactory<AstNullVisitor>* factory);
 
-  // Instance objects have to be created ahead of time (before code generation)
-  // because of potentially cyclic references between them.
-  // Linking also has to be a separate stage, since populating one object may
-  // potentially require (forward) references to others.
-  void AllocateModules(CompilationInfo* info);
-  void LinkModules(CompilationInfo* info);
-
  private:
   // Construct a scope based on the scope info.
   Scope(Scope* inner_scope, ScopeType type, Handle<ScopeInfo> scope_info,
index 95390adcf69a7d445f18276189b9e5b447d3ab9a..0e71a93dad228cf58ab8c64b5ee5987fea9c10ee 100644 (file)
@@ -483,11 +483,19 @@ enum VariableMode {
 
   CONST,           // declared via 'const' declarations
 
-  LET,             // declared via 'let' declarations
+  LET,             // declared via 'let' declarations (first lexical)
 
   CONST_HARMONY,   // declared via 'const' declarations in harmony mode
 
+  MODULE,          // declared via 'module' declaration (last lexical)
+
   // Variables introduced by the compiler:
+  INTERNAL,        // like VAR, but not user-visible (may or may not
+                   // be in a context)
+
+  TEMPORARY,       // temporary variables (not user-visible), never
+                   // in a context
+
   DYNAMIC,         // always require dynamic lookup (we don't know
                    // the declaration)
 
@@ -495,16 +503,10 @@ enum VariableMode {
                    // variable is global unless it has been shadowed
                    // by an eval-introduced variable
 
-  DYNAMIC_LOCAL,   // requires dynamic lookup, but we know that the
+  DYNAMIC_LOCAL    // requires dynamic lookup, but we know that the
                    // variable is local and where it is unless it
                    // has been shadowed by an eval-introduced
                    // variable
-
-  INTERNAL,        // like VAR, but not user-visible (may or may not
-                   // be in a context)
-
-  TEMPORARY        // temporary variables (not user-visible), never
-                   // in a context
 };
 
 
@@ -514,17 +516,17 @@ inline bool IsDynamicVariableMode(VariableMode mode) {
 
 
 inline bool IsDeclaredVariableMode(VariableMode mode) {
-  return mode >= VAR && mode <= CONST_HARMONY;
+  return mode >= VAR && mode <= MODULE;
 }
 
 
 inline bool IsLexicalVariableMode(VariableMode mode) {
-  return mode >= LET && mode <= CONST_HARMONY;
+  return mode >= LET && mode <= MODULE;
 }
 
 
 inline bool IsImmutableVariableMode(VariableMode mode) {
-  return mode == CONST || mode == CONST_HARMONY;
+  return mode == CONST || (mode >= CONST_HARMONY && mode <= MODULE);
 }
 
 
index 0416f3a3908d321b35d2c647145e2ce7173ac4e0..916121791798489d434acb8637f088327891d62a 100644 (file)
@@ -41,8 +41,9 @@ const char* Variable::Mode2String(VariableMode mode) {
   switch (mode) {
     case VAR: return "VAR";
     case CONST: return "CONST";
-    case CONST_HARMONY: return "CONST_HARMONY";
     case LET: return "LET";
+    case CONST_HARMONY: return "CONST_HARMONY";
+    case MODULE: return "MODULE";
     case DYNAMIC: return "DYNAMIC";
     case DYNAMIC_GLOBAL: return "DYNAMIC_GLOBAL";
     case DYNAMIC_LOCAL: return "DYNAMIC_LOCAL";
@@ -84,7 +85,8 @@ Variable::Variable(Scope* scope,
 bool Variable::IsGlobalObjectProperty() const {
   // Temporaries are never global, they must always be allocated in the
   // activation frame.
-  return mode_ != TEMPORARY && !IsLexicalVariableMode(mode_)
+  return (IsDynamicVariableMode(mode_) ||
+          (IsDeclaredVariableMode(mode_) && !IsLexicalVariableMode(mode_)))
       && scope_ != NULL && scope_->is_global_scope();
 }
 
index 6bab01bb85267500236c5eaf30a851f0a123c646..1e16c666b2f87bbca80d99a912276173a0ea8371 100644 (file)
@@ -763,8 +763,7 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr,
 
 
 void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) {
-  // The variable in the declaration always resides in the current function
-  // context.
+  // The variable in the declaration always resides in the current context.
   ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
   if (generate_debug_code_) {
     // Check that we're not inside a with or catch context.
@@ -895,33 +894,32 @@ void FullCodeGenerator::VisitFunctionDeclaration(
 
 
 void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
-  VariableProxy* proxy = declaration->proxy();
-  Variable* variable = proxy->var();
-  Handle<JSModule> instance = declaration->module()->interface()->Instance();
-  ASSERT(!instance.is_null());
+  Variable* variable = declaration->proxy()->var();
+  ASSERT(variable->location() == Variable::CONTEXT);
+  ASSERT(variable->interface()->IsFrozen());
 
-  switch (variable->location()) {
-    case Variable::UNALLOCATED: {
-      Comment cmnt(masm_, "[ ModuleDeclaration");
-      globals_->Add(variable->name(), zone());
-      globals_->Add(instance, zone());
-      Visit(declaration->module());
-      break;
-    }
+  Comment cmnt(masm_, "[ ModuleDeclaration");
+  EmitDebugCheckDeclarationContext(variable);
 
-    case Variable::CONTEXT: {
-      Comment cmnt(masm_, "[ ModuleDeclaration");
-      EmitDebugCheckDeclarationContext(variable);
-      __ Move(ContextOperand(rsi, variable->index()), instance);
-      Visit(declaration->module());
-      break;
-    }
+  // Load instance object.
+  __ LoadContext(rax, scope_->ContextChainLength(scope_->GlobalScope()));
+  __ movq(rax, ContextOperand(rax, variable->interface()->Index()));
+  __ movq(rax, ContextOperand(rax, Context::EXTENSION_INDEX));
 
-    case Variable::PARAMETER:
-    case Variable::LOCAL:
-    case Variable::LOOKUP:
-      UNREACHABLE();
-  }
+  // Assign it.
+  __ movq(ContextOperand(rsi, variable->index()), rax);
+  // We know that we have written a module, which is not a smi.
+  __ RecordWriteContextSlot(rsi,
+                            Context::SlotOffset(variable->index()),
+                            rax,
+                            rcx,
+                            kDontSaveFPRegs,
+                            EMIT_REMEMBERED_SET,
+                            OMIT_SMI_CHECK);
+  PrepareForBailoutForId(declaration->proxy()->id(), NO_REGISTERS);
+
+  // Traverse into body.
+  Visit(declaration->module());
 }
 
 
@@ -963,6 +961,14 @@ void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
 }
 
 
+void FullCodeGenerator::DeclareModules(Handle<FixedArray> descriptions) {
+  // Call the runtime to declare the modules.
+  __ Push(descriptions);
+  __ CallRuntime(Runtime::kDeclareModules, 1);
+  // Return value is ignored.
+}
+
+
 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
   Comment cmnt(masm_, "[ SwitchStatement");
   Breakable nested_statement(this, stmt);
@@ -1372,9 +1378,9 @@ void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var,
   } else if (var->mode() == DYNAMIC_LOCAL) {
     Variable* local = var->local_if_not_shadowed();
     __ movq(rax, ContextSlotOperandCheckExtensions(local, slow));
-    if (local->mode() == CONST ||
-        local->mode() == CONST_HARMONY ||
-        local->mode() == LET) {
+    if (local->mode() == LET ||
+        local->mode() == CONST ||
+        local->mode() == CONST_HARMONY) {
       __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
       __ j(not_equal, done);
       if (local->mode() == CONST) {
index e398cbb307559450d88b9c34c970ee94bc87d969..824c4e764a2b8ebf9f5b8cee64d30d357ddc4869 100644 (file)
@@ -735,7 +735,7 @@ class SimpleContext {
 };
 
 
-TEST(MultiScriptConflicts) {
+TEST(CrossScriptReferences) {
   HandleScope scope;
 
   { SimpleContext context;
@@ -773,135 +773,70 @@ TEST(MultiScriptConflicts) {
     context.Check("function x() { return 7 }; x",
                   EXPECT_EXCEPTION);
   }
+}
+
 
+TEST(CrossScriptReferencesHarmony) {
   i::FLAG_use_strict = true;
   i::FLAG_harmony_scoping = true;
+  i::FLAG_harmony_modules = true;
 
-  { SimpleContext context;
-    context.Check("var x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("this.x",
-                  EXPECT_RESULT, Number::New(1));
-  }
-
-  { SimpleContext context;
-    context.Check("function x() { return 4 }; x()",
-                  EXPECT_RESULT, Number::New(4));
-    context.Check("x()",
-                  EXPECT_RESULT, Number::New(4));
-    context.Check("this.x()",
-                  EXPECT_RESULT, Number::New(4));
-  }
+  HandleScope scope;
 
-  { SimpleContext context;
-    context.Check("let x = 2; x",
-                  EXPECT_RESULT, Number::New(2));
-    context.Check("x",
-                  EXPECT_RESULT, Number::New(2));
-    // TODO(rossberg): The current ES6 draft spec does not reflect lexical
-    // bindings on the global object. However, this will probably change, in
-    // which case we reactivate the following test.
-    // context.Check("this.x",
-    //               EXPECT_RESULT, Number::New(2));
-  }
+  const char* decs[] = {
+    "var x = 1; x", "x", "this.x",
+    "function x() { return 1 }; x()", "x()", "this.x()",
+    "let x = 1; x", "x", "this.x",
+    "const x = 1; x", "x", "this.x",
+    "module x { export let a = 1 }; x.a", "x.a", "this.x.a",
+    NULL
+  };
 
-  { SimpleContext context;
-    context.Check("const x = 3; x",
-                  EXPECT_RESULT, Number::New(3));
-    context.Check("x",
-                  EXPECT_RESULT, Number::New(3));
+  for (int i = 0; decs[i] != NULL; i += 3) {
+    SimpleContext context;
+    context.Check(decs[i], EXPECT_RESULT, Number::New(1));
+    context.Check(decs[i+1], EXPECT_RESULT, Number::New(1));
     // TODO(rossberg): The current ES6 draft spec does not reflect lexical
     // bindings on the global object. However, this will probably change, in
     // which case we reactivate the following test.
-    // context.Check("this.x",
-    //              EXPECT_RESULT, Number::New(3));
-  }
-
-  // TODO(rossberg): All of the below should actually be errors in Harmony.
-
-  { SimpleContext context;
-    context.Check("var x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("let x = 2; x",
-                  EXPECT_RESULT, Number::New(2));
-  }
-
-  { SimpleContext context;
-    context.Check("var x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("const x = 2; x",
-                  EXPECT_RESULT, Number::New(2));
-  }
-
-  { SimpleContext context;
-    context.Check("function x() { return 1 }; x()",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("let x = 2; x",
-                  EXPECT_RESULT, Number::New(2));
-  }
-
-  { SimpleContext context;
-    context.Check("function x() { return 1 }; x()",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("const x = 2; x",
-                  EXPECT_RESULT, Number::New(2));
-  }
-
-  { SimpleContext context;
-    context.Check("let x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("var x = 2; x",
-                  EXPECT_ERROR);
-  }
-
-  { SimpleContext context;
-    context.Check("let x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("let x = 2; x",
-                  EXPECT_ERROR);
-  }
-
-  { SimpleContext context;
-    context.Check("let x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("const x = 2; x",
-                  EXPECT_ERROR);
+    if (i/3 < 2) context.Check(decs[i+2], EXPECT_RESULT, Number::New(1));
   }
+}
 
-  { SimpleContext context;
-    context.Check("let x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("function x() { return 2 }; x()",
-                  EXPECT_ERROR);
-  }
 
-  { SimpleContext context;
-    context.Check("const x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("var x = 2; x",
-                  EXPECT_ERROR);
-  }
+TEST(CrossScriptConflicts) {
+  i::FLAG_use_strict = true;
+  i::FLAG_harmony_scoping = true;
+  i::FLAG_harmony_modules = true;
 
-  { SimpleContext context;
-    context.Check("const x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("let x = 2; x",
-                  EXPECT_ERROR);
-  }
+  HandleScope scope;
 
-  { SimpleContext context;
-    context.Check("const x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("const x = 2; x",
-                  EXPECT_ERROR);
-  }
+  const char* firsts[] = {
+    "var x = 1; x",
+    "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[] = {
+    "var x = 2; x",
+    "function x() { return 2 }; x()",
+    "let x = 2; x",
+    "const x = 2; x",
+    "module x { export let a = 2 }; x.a",
+    NULL
+  };
 
-  { SimpleContext context;
-    context.Check("const x = 1; x",
-                  EXPECT_RESULT, Number::New(1));
-    context.Check("function x() { return 2 }; x()",
-                  EXPECT_ERROR);
+  for (int i = 0; firsts[i] != NULL; ++i) {
+    for (int j = 0; seconds[j] != NULL; ++j) {
+      SimpleContext context;
+      context.Check(firsts[i], EXPECT_RESULT, Number::New(1));
+      // TODO(rossberg): All tests should actually be errors in Harmony,
+      // but we currently do not detect the cases where the first declaration
+      // is not lexical.
+      context.Check(seconds[j],
+                    i < 2 ? EXPECT_RESULT : EXPECT_ERROR, Number::New(2));
+    }
   }
 }
index 54869bb3a32a93744e51afbe840f67113a9b423b..7cc462e38a3dc2cc68f147da6d910a396a8a76b7 100644 (file)
@@ -147,6 +147,7 @@ var knownProblems = {
   "PushWithContext": true,
   "PushCatchContext": true,
   "PushBlockContext": true,
+  "PushModuleContext": true,
   "LazyCompile": true,
   "LazyRecompile": true,
   "ParallelRecompile": true,
index ea8a2cfe1d0e5ff53c19103b62dbb8a13eeb4518..6fb8df120fa49f5b2360a30c1ecbf0ede0314bad 100644 (file)
@@ -147,6 +147,7 @@ var knownProblems = {
   "PushWithContext": true,
   "PushCatchContext": true,
   "PushBlockContext": true,
+  "PushModuleContext": true,
   "LazyCompile": true,
   "LazyRecompile": true,
   "ParallelRecompile": true,
index ecfdf9737dc76d35c98836c8c0b96eba8e59f3e5..df9bb7e3e0757f018a552ed1ca308995b9653b33 100644 (file)
@@ -147,6 +147,7 @@ var knownProblems = {
   "PushWithContext": true,
   "PushCatchContext": true,
   "PushBlockContext": true,
+  "PushModuleContext": true,
   "LazyCompile": true,
   "LazyRecompile": true,
   "ParallelRecompile": true,
index da045963f0e013340b64c08cf1061646a6dee707..c34061963e1b0bd287c2e6d04fb785d83dc37430 100644 (file)
@@ -147,6 +147,7 @@ var knownProblems = {
   "PushWithContext": true,
   "PushCatchContext": true,
   "PushBlockContext": true,
+  "PushModuleContext": true,
   "LazyCompile": true,
   "LazyRecompile": true,
   "ParallelRecompile": true,
index a4b272f4680aab9c54981f0ed6416b6a7dd30ce2..3c0f18c37dbe5c02bed700cf894efc493e738328 100644 (file)
@@ -112,7 +112,7 @@ module R {
   assertThrows(function() { eval("c = -1") }, SyntaxError)
   assertThrows(function() { R.c = -2 }, TypeError)
 
-  // Initialize first bunch or variables.
+  // Initialize first bunch of variables.
   export var v = 1
   export let l = 2
   export const c = 3