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());
}
}
+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);
} 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);
// 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) {
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
// 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)
#define STATEMENT_NODE_LIST(V) \
V(Block) \
+ V(ModuleStatement) \
V(ExpressionStatement) \
V(EmptyStatement) \
V(IfStatement) \
ModuleDeclaration(VariableProxy* proxy,
Module* module,
Scope* scope)
- : Declaration(proxy, LET, scope),
+ : Declaration(proxy, MODULE, scope),
module_(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.
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:
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)
}
+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
? 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:
// 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();
}
+void BreakableStatementChecker::VisitModuleStatement(ModuleStatement* stmt) {
+}
+
+
void BreakableStatementChecker::VisitBlock(Block* stmt) {
}
}
+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)
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());
{
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);
}
-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);
}
// 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;
}
+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);
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.
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_;
}
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;
}
}
+void HGraphBuilder::VisitModuleStatement(ModuleStatement* stmt) {
+ UNREACHABLE();
+}
+
+
// Generators for inline runtime functions.
// Support for types.
void HGraphBuilder::GenerateIsSmi(CallRuntime* call) {
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.
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());
}
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);
} 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) {
// 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());
ASSERT(that->forward_ == NULL);
ASSERT(!this->IsValue());
ASSERT(!that->IsValue());
+ ASSERT(this->index_ == -1);
+ ASSERT(that->index_ == -1);
ASSERT(*ok);
#ifdef DEBUG
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;
} 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");
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.
// 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);
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));
}
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: {
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.
#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);
if (module->body() == NULL)
return factory()->NewEmptyStatement();
else
- return module->body();
+ return factory()->NewModuleStatement(proxy, module->body());
}
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;
*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);
}
+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(";");
}
+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());
}
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
}
+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()) {
// 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);
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
// 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();
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();
}
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) \
\
/* 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) \
return result;
}
}
+ // Cache as not found. Mode and init flag don't matter.
context_slot_cache->Update(this, name, INTERNAL, kNeedsInitialization, -1);
}
return -1;
}
#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
// 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_;
};
+
+
+//---------------------------------------------------------------------------
+// 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_
: isolate_(Isolate::Current()),
inner_scopes_(4, zone),
variables_(zone),
+ internals_(4, zone),
temps_(4, zone),
params_(4, zone),
unresolved_(16, zone),
: isolate_(Isolate::Current()),
inner_scopes_(4, zone),
variables_(zone),
+ internals_(4, zone),
temps_(4, zone),
params_(4, zone),
unresolved_(16, zone),
: isolate_(Isolate::Current()),
inner_scopes_(1, zone),
variables_(zone),
+ internals_(0, zone),
temps_(0, zone),
params_(0, zone),
unresolved_(0, zone),
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;
Scope* Scope::FinalizeBlockScope() {
ASSERT(is_block_scope());
+ ASSERT(internals_.is_empty());
ASSERT(temps_.is_empty());
ASSERT(params_.is_empty());
}
+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,
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];
}
}
- 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)) {
}
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;
}
}
+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()) {
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_);
}
ASSERT(var != NULL);
- proxy->BindTo(var);
if (FLAG_harmony_modules) {
bool ok;
}
}
+ proxy->BindTo(var);
+
return true;
}
// 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() ||
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++) {
}
-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
// 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*
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; }
// 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();
// 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.
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_; }
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
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,
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)
// 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
};
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);
}
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";
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();
}
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.
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());
}
}
+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);
} 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) {
};
-TEST(MultiScriptConflicts) {
+TEST(CrossScriptReferences) {
HandleScope scope;
{ SimpleContext context;
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));
+ }
}
}
"PushWithContext": true,
"PushCatchContext": true,
"PushBlockContext": true,
+ "PushModuleContext": true,
"LazyCompile": true,
"LazyRecompile": true,
"ParallelRecompile": true,
"PushWithContext": true,
"PushCatchContext": true,
"PushBlockContext": true,
+ "PushModuleContext": true,
"LazyCompile": true,
"LazyRecompile": true,
"ParallelRecompile": true,
"PushWithContext": true,
"PushCatchContext": true,
"PushBlockContext": true,
+ "PushModuleContext": true,
"LazyCompile": true,
"LazyRecompile": true,
"ParallelRecompile": true,
"PushWithContext": true,
"PushCatchContext": true,
"PushBlockContext": true,
+ "PushModuleContext": true,
"LazyCompile": true,
"LazyRecompile": true,
"ParallelRecompile": true,
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