Get rid of static module allocation, do it in code.

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
This commit is contained in:
rossberg@chromium.org 2012-11-22 10:25:22 +00:00
parent 80819f618d
commit ce05280bfc
35 changed files with 791 additions and 409 deletions

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

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

View 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)

View File

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

View 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:

View 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();

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

View 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_;

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

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

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

View 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());

View 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");

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

View 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: {

View 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.

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

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

View 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

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

View 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);
Context* context = Context::cast(instance->context());
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);
// 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();
}

View 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) \

View 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

View 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_

View 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,6 +1387,25 @@ void Scope::AllocateVariablesRecursively() {
}
void Scope::AllocateModulesRecursively(Scope* host_scope) {
if (already_resolved()) return;
if (is_module_scope()) {
ASSERT(interface_->IsFrozen());
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_;
}
for (int i = 0; i < inner_scopes_.length(); i++) {
Scope* inner_scope = inner_scopes_.at(i);
inner_scope->AllocateModulesRecursively(host_scope);
}
}
int Scope::StackLocalCount() const {
return num_stack_slots() -
(function_ != NULL && function_->proxy()->var()->IsStackLocal() ? 1 : 0);
@ -1354,77 +1418,4 @@ int Scope::ContextLocalCount() const {
(function_ != NULL && function_->proxy()->var()->IsContextSlot() ? 1 : 0);
}
void Scope::AllocateModules(CompilationInfo* info) {
ASSERT(is_global_scope() || is_module_scope());
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);
}
// 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);
}
}
}
void Scope::LinkModules(CompilationInfo* info) {
ASSERT(is_global_scope() || is_module_scope());
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);
}
}
}
} } // namespace v8::internal

View 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,

View 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);
}

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

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

View 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));
}
HandleScope scope;
{ 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));
}
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("let x = 2; x",
EXPECT_RESULT, Number::New(2));
context.Check("x",
EXPECT_RESULT, Number::New(2));
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(2));
}
{ SimpleContext context;
context.Check("const x = 3; x",
EXPECT_RESULT, Number::New(3));
context.Check("x",
EXPECT_RESULT, Number::New(3));
// 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);
}
{ 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);
}
{ SimpleContext context;
context.Check("const x = 1; x",
EXPECT_RESULT, Number::New(1));
context.Check("let x = 2; x",
EXPECT_ERROR);
}
{ SimpleContext context;
context.Check("const x = 1; x",
EXPECT_RESULT, Number::New(1));
context.Check("const x = 2; x",
EXPECT_ERROR);
}
{ SimpleContext context;
context.Check("const x = 1; x",
EXPECT_RESULT, Number::New(1));
context.Check("function x() { return 2 }; x()",
EXPECT_ERROR);
if (i/3 < 2) context.Check(decs[i+2], EXPECT_RESULT, Number::New(1));
}
}
TEST(CrossScriptConflicts) {
i::FLAG_use_strict = true;
i::FLAG_harmony_scoping = true;
i::FLAG_harmony_modules = true;
HandleScope scope;
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
};
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));
}
}
}

View File

@ -147,6 +147,7 @@ var knownProblems = {
"PushWithContext": true,
"PushCatchContext": true,
"PushBlockContext": true,
"PushModuleContext": true,
"LazyCompile": true,
"LazyRecompile": true,
"ParallelRecompile": true,

View File

@ -147,6 +147,7 @@ var knownProblems = {
"PushWithContext": true,
"PushCatchContext": true,
"PushBlockContext": true,
"PushModuleContext": true,
"LazyCompile": true,
"LazyRecompile": true,
"ParallelRecompile": true,

View File

@ -147,6 +147,7 @@ var knownProblems = {
"PushWithContext": true,
"PushCatchContext": true,
"PushBlockContext": true,
"PushModuleContext": true,
"LazyCompile": true,
"LazyRecompile": true,
"ParallelRecompile": true,

View File

@ -147,6 +147,7 @@ var knownProblems = {
"PushWithContext": true,
"PushCatchContext": true,
"PushBlockContext": true,
"PushModuleContext": true,
"LazyCompile": true,
"LazyRecompile": true,
"ParallelRecompile": true,

View 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