Implement proper module linking.
Specifically: - In parser, check that all exports are defined. - Move JSModule allocation from parser to scope resolution. - Move JSModule linking from full codegen to scope resolution. - Implement module accessors for exported value members. - Allocate module contexts statically along with JSModules (to allow static linking), but chain them when module literal is evaluated. - Make module contexts' extension slot refer to resp. JSModule (makes modules' ScopeInfo accessible from context). - Some other tweaks to context handling in general. - Make any code containing module literals (and thus embedding static references to JSModules) non-cacheable. This enables accessing module instance objects as expected. Import declarations are a separate feature and do not work yet. R=mstarzinger@chromium.org BUG=v8:1569 TEST= Review URL: https://chromiumcodereview.appspot.com/10690043 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12010 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
1d0aea7c3a
commit
98db1a369d
@ -802,4 +802,69 @@ const AccessorDescriptor Accessors::ObjectPrototype = {
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Accessors::MakeModuleExport
|
||||
//
|
||||
|
||||
static v8::Handle<v8::Value> ModuleGetExport(
|
||||
v8::Local<v8::String> property,
|
||||
const v8::AccessorInfo& info) {
|
||||
JSModule* instance = JSModule::cast(*v8::Utils::OpenHandle(*info.Holder()));
|
||||
Context* context = Context::cast(instance->context());
|
||||
ASSERT(context->IsModuleContext());
|
||||
int slot = info.Data()->Int32Value();
|
||||
Object* value = context->get(slot);
|
||||
if (value->IsTheHole()) {
|
||||
Handle<String> name = v8::Utils::OpenHandle(*property);
|
||||
Isolate* isolate = instance->GetIsolate();
|
||||
isolate->ScheduleThrow(
|
||||
*isolate->factory()->NewReferenceError("not_defined",
|
||||
HandleVector(&name, 1)));
|
||||
return v8::Handle<v8::Value>();
|
||||
}
|
||||
return v8::Utils::ToLocal(Handle<Object>(value));
|
||||
}
|
||||
|
||||
|
||||
static void ModuleSetExport(
|
||||
v8::Local<v8::String> property,
|
||||
v8::Local<v8::Value> value,
|
||||
const v8::AccessorInfo& info) {
|
||||
JSModule* instance = JSModule::cast(*v8::Utils::OpenHandle(*info.Holder()));
|
||||
Context* context = Context::cast(instance->context());
|
||||
ASSERT(context->IsModuleContext());
|
||||
int slot = info.Data()->Int32Value();
|
||||
Object* old_value = context->get(slot);
|
||||
if (old_value->IsTheHole()) {
|
||||
Handle<String> name = v8::Utils::OpenHandle(*property);
|
||||
Isolate* isolate = instance->GetIsolate();
|
||||
isolate->ScheduleThrow(
|
||||
*isolate->factory()->NewReferenceError("not_defined",
|
||||
HandleVector(&name, 1)));
|
||||
return;
|
||||
}
|
||||
context->set(slot, *v8::Utils::OpenHandle(*value));
|
||||
}
|
||||
|
||||
|
||||
Handle<AccessorInfo> Accessors::MakeModuleExport(
|
||||
Handle<String> name,
|
||||
int index,
|
||||
PropertyAttributes attributes) {
|
||||
Factory* factory = name->GetIsolate()->factory();
|
||||
Handle<AccessorInfo> info = factory->NewAccessorInfo();
|
||||
info->set_property_attributes(attributes);
|
||||
info->set_all_can_read(true);
|
||||
info->set_all_can_write(true);
|
||||
info->set_name(*name);
|
||||
info->set_data(Smi::FromInt(index));
|
||||
v8::AccessorGetter getter = &ModuleGetExport;
|
||||
v8::AccessorSetter setter = &ModuleSetExport;
|
||||
info->set_getter(*v8::FromCData(getter));
|
||||
if (!(attributes & ReadOnly)) info->set_setter(*v8::FromCData(setter));
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
@ -85,6 +85,10 @@ class Accessors : public AllStatic {
|
||||
void*);
|
||||
static MaybeObject* FunctionGetArguments(Object* object, void*);
|
||||
|
||||
// Accessor infos.
|
||||
static Handle<AccessorInfo> MakeModuleExport(
|
||||
Handle<String> name, int index, PropertyAttributes attributes);
|
||||
|
||||
private:
|
||||
// Accessor functions only used through the descriptor.
|
||||
static MaybeObject* FunctionGetLength(Object* object, void*);
|
||||
|
16
src/ast.cc
16
src/ast.cc
@ -1030,6 +1030,14 @@ CaseClause::CaseClause(Isolate* isolate,
|
||||
increase_node_count(); \
|
||||
add_flag(kDontSelfOptimize); \
|
||||
}
|
||||
#define DONT_CACHE_NODE(NodeType) \
|
||||
void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \
|
||||
increase_node_count(); \
|
||||
add_flag(kDontOptimize); \
|
||||
add_flag(kDontInline); \
|
||||
add_flag(kDontSelfOptimize); \
|
||||
add_flag(kDontCache); \
|
||||
}
|
||||
|
||||
REGULAR_NODE(VariableDeclaration)
|
||||
REGULAR_NODE(FunctionDeclaration)
|
||||
@ -1061,10 +1069,13 @@ 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.
|
||||
DONT_OPTIMIZE_NODE(ModuleDeclaration)
|
||||
DONT_OPTIMIZE_NODE(ImportDeclaration)
|
||||
DONT_OPTIMIZE_NODE(ExportDeclaration)
|
||||
DONT_OPTIMIZE_NODE(ModuleLiteral)
|
||||
DONT_OPTIMIZE_NODE(ModuleVariable)
|
||||
DONT_OPTIMIZE_NODE(ModulePath)
|
||||
DONT_OPTIMIZE_NODE(ModuleUrl)
|
||||
@ -1082,6 +1093,8 @@ DONT_SELFOPTIMIZE_NODE(WhileStatement)
|
||||
DONT_SELFOPTIMIZE_NODE(ForStatement)
|
||||
DONT_SELFOPTIMIZE_NODE(ForInStatement)
|
||||
|
||||
DONT_CACHE_NODE(ModuleLiteral)
|
||||
|
||||
void AstConstructionVisitor::VisitCallRuntime(CallRuntime* node) {
|
||||
increase_node_count();
|
||||
if (node->is_jsruntime()) {
|
||||
@ -1102,6 +1115,7 @@ void AstConstructionVisitor::VisitCallRuntime(CallRuntime* node) {
|
||||
#undef DONT_OPTIMIZE_NODE
|
||||
#undef DONT_INLINE_NODE
|
||||
#undef DONT_SELFOPTIMIZE_NODE
|
||||
#undef DONT_CACHE_NODE
|
||||
|
||||
|
||||
Handle<String> Literal::ToString() {
|
||||
|
31
src/ast.h
31
src/ast.h
@ -165,7 +165,8 @@ enum AstPropertiesFlag {
|
||||
kDontInline,
|
||||
kDontOptimize,
|
||||
kDontSelfOptimize,
|
||||
kDontSoftInline
|
||||
kDontSoftInline,
|
||||
kDontCache
|
||||
};
|
||||
|
||||
|
||||
@ -586,23 +587,27 @@ class ExportDeclaration: public Declaration {
|
||||
protected:
|
||||
template<class> friend class AstNodeFactory;
|
||||
|
||||
ExportDeclaration(VariableProxy* proxy,
|
||||
Scope* scope)
|
||||
: Declaration(proxy, LET, scope) {
|
||||
}
|
||||
ExportDeclaration(VariableProxy* proxy, Scope* scope)
|
||||
: Declaration(proxy, LET, scope) {}
|
||||
};
|
||||
|
||||
|
||||
class Module: public AstNode {
|
||||
public:
|
||||
Interface* interface() const { return interface_; }
|
||||
Block* body() const { return body_; }
|
||||
|
||||
protected:
|
||||
explicit Module(Zone* zone) : interface_(Interface::NewModule(zone)) {}
|
||||
explicit Module(Interface* interface) : interface_(interface) {}
|
||||
explicit Module(Zone* zone)
|
||||
: interface_(Interface::NewModule(zone)),
|
||||
body_(NULL) {}
|
||||
explicit Module(Interface* interface, Block* body = NULL)
|
||||
: interface_(interface),
|
||||
body_(body) {}
|
||||
|
||||
private:
|
||||
Interface* interface_;
|
||||
Block* body_;
|
||||
};
|
||||
|
||||
|
||||
@ -610,20 +615,10 @@ class ModuleLiteral: public Module {
|
||||
public:
|
||||
DECLARE_NODE_TYPE(ModuleLiteral)
|
||||
|
||||
Block* body() const { return body_; }
|
||||
Handle<Context> context() const { return context_; }
|
||||
|
||||
protected:
|
||||
template<class> friend class AstNodeFactory;
|
||||
|
||||
ModuleLiteral(Block* body, Interface* interface)
|
||||
: Module(interface),
|
||||
body_(body) {
|
||||
}
|
||||
|
||||
private:
|
||||
Block* body_;
|
||||
Handle<Context> context_;
|
||||
ModuleLiteral(Block* body, Interface* interface) : Module(interface, body) {}
|
||||
};
|
||||
|
||||
|
||||
|
@ -543,7 +543,7 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
|
||||
info.SetLanguageMode(FLAG_harmony_scoping ? EXTENDED_MODE : STRICT_MODE);
|
||||
}
|
||||
result = MakeFunctionInfo(&info);
|
||||
if (extension == NULL && !result.is_null()) {
|
||||
if (extension == NULL && !result.is_null() && !result->dont_cache()) {
|
||||
compilation_cache->PutScript(source, result);
|
||||
}
|
||||
} else {
|
||||
@ -602,8 +602,10 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source,
|
||||
// extended mode.
|
||||
ASSERT(language_mode != EXTENDED_MODE ||
|
||||
result->is_extended_mode());
|
||||
compilation_cache->PutEval(
|
||||
source, context, is_global, result, scope_position);
|
||||
if (!result->dont_cache()) {
|
||||
compilation_cache->PutEval(
|
||||
source, context, is_global, result, scope_position);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (result->ic_age() != HEAP->global_ic_age()) {
|
||||
@ -717,6 +719,7 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
|
||||
shared->set_code_age(0);
|
||||
shared->set_dont_optimize(lit->flags()->Contains(kDontOptimize));
|
||||
shared->set_dont_inline(lit->flags()->Contains(kDontInline));
|
||||
shared->set_dont_cache(lit->flags()->Contains(kDontCache));
|
||||
shared->set_ast_node_count(lit->ast_node_count());
|
||||
|
||||
if (V8::UseCrankshaft()&&
|
||||
@ -830,6 +833,7 @@ void Compiler::SetFunctionInfo(Handle<SharedFunctionInfo> function_info,
|
||||
function_info->set_is_function(lit->is_function());
|
||||
function_info->set_dont_optimize(lit->flags()->Contains(kDontOptimize));
|
||||
function_info->set_dont_inline(lit->flags()->Contains(kDontInline));
|
||||
function_info->set_dont_cache(lit->flags()->Contains(kDontCache));
|
||||
}
|
||||
|
||||
|
||||
|
@ -306,10 +306,15 @@ void Context::ClearOptimizedFunctions() {
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
bool Context::IsBootstrappingOrContext(Object* object) {
|
||||
bool Context::IsBootstrappingOrValidParentContext(
|
||||
Object* object, Context* child) {
|
||||
// During bootstrapping we allow all objects to pass as
|
||||
// contexts. This is necessary to fix circular dependencies.
|
||||
return Isolate::Current()->bootstrapper()->IsActive() || object->IsContext();
|
||||
if (Isolate::Current()->bootstrapper()->IsActive()) return true;
|
||||
if (!object->IsContext()) return false;
|
||||
Context* context = Context::cast(object);
|
||||
return context->IsGlobalContext() || context->IsModuleContext() ||
|
||||
!child->IsModuleContext();
|
||||
}
|
||||
|
||||
|
||||
|
@ -190,6 +190,8 @@ enum BindingFlags {
|
||||
// Dynamically declared variables/functions are also added
|
||||
// to lazily allocated extension object. Context::Lookup
|
||||
// searches the extension object for properties.
|
||||
// For block contexts, contains the respective ScopeInfo.
|
||||
// For module contexts, points back to the respective JSModule.
|
||||
//
|
||||
// [ global ] A pointer to the global object. Provided for quick
|
||||
// access to the global object from inside the code (since
|
||||
@ -217,7 +219,7 @@ class Context: public FixedArray {
|
||||
// The extension slot is used for either the global object (in global
|
||||
// contexts), eval extension object (function contexts), subject of with
|
||||
// (with contexts), or the variable name (catch contexts), the serialized
|
||||
// scope info (block contexts).
|
||||
// scope info (block contexts), or the module instance (module contexts).
|
||||
EXTENSION_INDEX,
|
||||
GLOBAL_INDEX,
|
||||
MIN_CONTEXT_SLOTS,
|
||||
@ -303,7 +305,7 @@ class Context: public FixedArray {
|
||||
|
||||
Context* previous() {
|
||||
Object* result = unchecked_previous();
|
||||
ASSERT(IsBootstrappingOrContext(result));
|
||||
ASSERT(IsBootstrappingOrValidParentContext(result, this));
|
||||
return reinterpret_cast<Context*>(result);
|
||||
}
|
||||
void set_previous(Context* context) { set(PREVIOUS_INDEX, context); }
|
||||
@ -312,6 +314,9 @@ class Context: public FixedArray {
|
||||
Object* extension() { return get(EXTENSION_INDEX); }
|
||||
void set_extension(Object* object) { set(EXTENSION_INDEX, object); }
|
||||
|
||||
JSModule* module() { return JSModule::cast(get(EXTENSION_INDEX)); }
|
||||
void set_module(JSModule* module) { set(EXTENSION_INDEX, module); }
|
||||
|
||||
// Get the context where var declarations will be hoisted to, which
|
||||
// may be the context itself.
|
||||
Context* declaration_context();
|
||||
@ -426,7 +431,7 @@ class Context: public FixedArray {
|
||||
|
||||
#ifdef DEBUG
|
||||
// Bootstrapping-aware type checks.
|
||||
static bool IsBootstrappingOrContext(Object* object);
|
||||
static bool IsBootstrappingOrValidParentContext(Object* object, Context* kid);
|
||||
static bool IsBootstrappingOrGlobalObject(Object* object);
|
||||
#endif
|
||||
};
|
||||
|
@ -293,11 +293,10 @@ Handle<Context> Factory::NewGlobalContext() {
|
||||
}
|
||||
|
||||
|
||||
Handle<Context> Factory::NewModuleContext(Handle<Context> previous,
|
||||
Handle<ScopeInfo> scope_info) {
|
||||
Handle<Context> Factory::NewModuleContext(Handle<ScopeInfo> scope_info) {
|
||||
CALL_HEAP_FUNCTION(
|
||||
isolate(),
|
||||
isolate()->heap()->AllocateModuleContext(*previous, *scope_info),
|
||||
isolate()->heap()->AllocateModuleContext(*scope_info),
|
||||
Context);
|
||||
}
|
||||
|
||||
@ -976,10 +975,11 @@ Handle<JSObject> Factory::NewJSObject(Handle<JSFunction> constructor,
|
||||
}
|
||||
|
||||
|
||||
Handle<JSModule> Factory::NewJSModule() {
|
||||
Handle<JSModule> Factory::NewJSModule(Handle<Context> context,
|
||||
Handle<ScopeInfo> scope_info) {
|
||||
CALL_HEAP_FUNCTION(
|
||||
isolate(),
|
||||
isolate()->heap()->AllocateJSModule(), JSModule);
|
||||
isolate()->heap()->AllocateJSModule(*context, *scope_info), JSModule);
|
||||
}
|
||||
|
||||
|
||||
|
@ -163,8 +163,7 @@ class Factory {
|
||||
Handle<Context> NewGlobalContext();
|
||||
|
||||
// Create a module context.
|
||||
Handle<Context> NewModuleContext(Handle<Context> previous,
|
||||
Handle<ScopeInfo> scope_info);
|
||||
Handle<Context> NewModuleContext(Handle<ScopeInfo> scope_info);
|
||||
|
||||
// Create a function context.
|
||||
Handle<Context> NewFunctionContext(int length, Handle<JSFunction> function);
|
||||
@ -267,7 +266,8 @@ class Factory {
|
||||
Handle<JSObject> NewJSObjectFromMap(Handle<Map> map);
|
||||
|
||||
// JS modules are pretenured.
|
||||
Handle<JSModule> NewJSModule();
|
||||
Handle<JSModule> NewJSModule(Handle<Context> context,
|
||||
Handle<ScopeInfo> scope_info);
|
||||
|
||||
// JS arrays are pretenured when allocated by the parser.
|
||||
Handle<JSArray> NewJSArray(
|
||||
|
@ -589,27 +589,21 @@ void FullCodeGenerator::VisitDeclarations(
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) {
|
||||
Handle<JSModule> instance = module->interface()->Instance();
|
||||
ASSERT(!instance.is_null());
|
||||
|
||||
// Allocate a module context statically.
|
||||
Block* block = module->body();
|
||||
Scope* saved_scope = scope();
|
||||
scope_ = block->scope();
|
||||
Handle<ScopeInfo> scope_info = scope_->GetScopeInfo();
|
||||
Interface* interface = module->interface();
|
||||
Handle<JSModule> instance = interface->Instance();
|
||||
|
||||
// Generate code for module creation and linking.
|
||||
Comment cmnt(masm_, "[ ModuleLiteral");
|
||||
SetStatementPosition(block);
|
||||
|
||||
if (scope_info->HasContext()) {
|
||||
// Set up module context.
|
||||
__ Push(scope_info);
|
||||
__ Push(instance);
|
||||
__ CallRuntime(Runtime::kPushModuleContext, 2);
|
||||
StoreToFrameField(
|
||||
StandardFrameConstants::kContextOffset, context_register());
|
||||
}
|
||||
// Set up module context.
|
||||
__ Push(instance);
|
||||
__ CallRuntime(Runtime::kPushModuleContext, 1);
|
||||
StoreToFrameField(StandardFrameConstants::kContextOffset, context_register());
|
||||
|
||||
{
|
||||
Comment cmnt(masm_, "[ Declarations");
|
||||
@ -617,42 +611,21 @@ void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) {
|
||||
}
|
||||
|
||||
scope_ = saved_scope;
|
||||
if (scope_info->HasContext()) {
|
||||
// Pop module context.
|
||||
LoadContextField(context_register(), Context::PREVIOUS_INDEX);
|
||||
// Update local stack frame context field.
|
||||
StoreToFrameField(
|
||||
StandardFrameConstants::kContextOffset, context_register());
|
||||
}
|
||||
|
||||
// Populate module instance object.
|
||||
const PropertyAttributes attr =
|
||||
static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE | DONT_ENUM);
|
||||
for (Interface::Iterator it = module->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, attr, kStrictMode);
|
||||
} else {
|
||||
// TODO(rossberg): set proper getters instead of undefined...
|
||||
// instance->DefineAccessor(*it.name(), ACCESSOR_GETTER, *getter, attr);
|
||||
Handle<Object> value(isolate()->heap()->undefined_value());
|
||||
JSReceiver::SetProperty(instance, it.name(), value, attr, kStrictMode);
|
||||
}
|
||||
}
|
||||
USE(instance->PreventExtensions());
|
||||
// Pop module context.
|
||||
LoadContextField(context_register(), Context::PREVIOUS_INDEX);
|
||||
// Update local stack frame context field.
|
||||
StoreToFrameField(StandardFrameConstants::kContextOffset, context_register());
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitModuleVariable(ModuleVariable* module) {
|
||||
// Noting to do.
|
||||
// Nothing to do.
|
||||
// The instance object is resolved statically through the module's interface.
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitModulePath(ModulePath* module) {
|
||||
// Noting to do.
|
||||
// Nothing to do.
|
||||
// The instance object is resolved statically through the module's interface.
|
||||
}
|
||||
|
||||
@ -916,25 +889,36 @@ void FullCodeGenerator::VisitBlock(Block* stmt) {
|
||||
Scope* saved_scope = scope();
|
||||
// Push a block context when entering a block with block scoped variables.
|
||||
if (stmt->scope() != NULL) {
|
||||
{ Comment cmnt(masm_, "[ Extend block context");
|
||||
scope_ = stmt->scope();
|
||||
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);
|
||||
}
|
||||
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());
|
||||
// Replace the context stored in the frame.
|
||||
StoreToFrameField(StandardFrameConstants::kContextOffset,
|
||||
context_register());
|
||||
}
|
||||
{ Comment cmnt(masm_, "[ Declarations");
|
||||
VisitDeclarations(scope_->declarations());
|
||||
}
|
||||
}
|
||||
}
|
||||
PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
|
||||
|
21
src/heap.cc
21
src/heap.cc
@ -4006,13 +4006,18 @@ MaybeObject* Heap::AllocateJSObject(JSFunction* constructor,
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* Heap::AllocateJSModule() {
|
||||
MaybeObject* Heap::AllocateJSModule(Context* context, ScopeInfo* scope_info) {
|
||||
// Allocate a fresh map. Modules do not have a prototype.
|
||||
Map* map;
|
||||
MaybeObject* maybe_map = AllocateMap(JS_MODULE_TYPE, JSModule::kSize);
|
||||
if (!maybe_map->To(&map)) return maybe_map;
|
||||
// Allocate the object based on the map.
|
||||
return AllocateJSObjectFromMap(map, TENURED);
|
||||
JSModule* module;
|
||||
MaybeObject* maybe_module = AllocateJSObjectFromMap(map, TENURED);
|
||||
if (!maybe_module->To(&module)) return maybe_module;
|
||||
module->set_context(context);
|
||||
module->set_scope_info(scope_info);
|
||||
return module;
|
||||
}
|
||||
|
||||
|
||||
@ -4911,18 +4916,16 @@ MaybeObject* Heap::AllocateGlobalContext() {
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* Heap::AllocateModuleContext(Context* previous,
|
||||
ScopeInfo* scope_info) {
|
||||
MaybeObject* Heap::AllocateModuleContext(ScopeInfo* scope_info) {
|
||||
Object* result;
|
||||
{ MaybeObject* maybe_result =
|
||||
AllocateFixedArrayWithHoles(scope_info->ContextLength(), TENURED);
|
||||
AllocateFixedArray(scope_info->ContextLength(), TENURED);
|
||||
if (!maybe_result->ToObject(&result)) return maybe_result;
|
||||
}
|
||||
Context* context = reinterpret_cast<Context*>(result);
|
||||
context->set_map_no_write_barrier(module_context_map());
|
||||
context->set_previous(previous);
|
||||
context->set_extension(scope_info);
|
||||
context->set_global(previous->global());
|
||||
// Context links will be set later.
|
||||
context->set_extension(Smi::FromInt(0));
|
||||
return context;
|
||||
}
|
||||
|
||||
@ -4937,7 +4940,7 @@ MaybeObject* Heap::AllocateFunctionContext(int length, JSFunction* function) {
|
||||
context->set_map_no_write_barrier(function_context_map());
|
||||
context->set_closure(function);
|
||||
context->set_previous(function->context());
|
||||
context->set_extension(NULL);
|
||||
context->set_extension(Smi::FromInt(0));
|
||||
context->set_global(function->context()->global());
|
||||
return context;
|
||||
}
|
||||
|
@ -530,7 +530,8 @@ class Heap {
|
||||
MUST_USE_RESULT MaybeObject* AllocateJSObject(
|
||||
JSFunction* constructor, PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
MUST_USE_RESULT MaybeObject* AllocateJSModule();
|
||||
MUST_USE_RESULT MaybeObject* AllocateJSModule(Context* context,
|
||||
ScopeInfo* scope_info);
|
||||
|
||||
// Allocate a JSArray with no elements
|
||||
MUST_USE_RESULT MaybeObject* AllocateEmptyJSArray(
|
||||
@ -824,8 +825,7 @@ class Heap {
|
||||
MUST_USE_RESULT MaybeObject* AllocateGlobalContext();
|
||||
|
||||
// Allocate a module context.
|
||||
MUST_USE_RESULT MaybeObject* AllocateModuleContext(Context* previous,
|
||||
ScopeInfo* scope_info);
|
||||
MUST_USE_RESULT MaybeObject* AllocateModuleContext(ScopeInfo* scope_info);
|
||||
|
||||
// Allocate a function context.
|
||||
MUST_USE_RESULT MaybeObject* AllocateFunctionContext(int length,
|
||||
|
@ -50,7 +50,12 @@ function FormatString(format, message) {
|
||||
try {
|
||||
str = ToDetailString(args[arg_num]);
|
||||
} catch (e) {
|
||||
str = "#<error>";
|
||||
if (%IsJSModule(args[arg_num]))
|
||||
str = "module";
|
||||
else if (IS_SPEC_OBJECT(args[arg_num]))
|
||||
str = "object";
|
||||
else
|
||||
str = "#<error>";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -251,6 +256,7 @@ function FormatMessage(message) {
|
||||
"harmony_const_assign", ["Assignment to constant variable."],
|
||||
"invalid_module_path", ["Module does not export '", "%0", "', or export is not itself a module"],
|
||||
"module_type_error", ["Module '", "%0", "' used improperly"],
|
||||
"module_export_undefined", ["Export '", "%0", "' is not defined in module"],
|
||||
];
|
||||
var messages = { __proto__ : null };
|
||||
for (var i = 0; i < messagesDictionary.length; i += 2) {
|
||||
|
@ -374,11 +374,9 @@ void FixedDoubleArray::FixedDoubleArrayVerify() {
|
||||
|
||||
|
||||
void JSModule::JSModuleVerify() {
|
||||
Object* v = context();
|
||||
if (v->IsHeapObject()) {
|
||||
VerifyHeapPointer(v);
|
||||
}
|
||||
CHECK(v->IsUndefined() || v->IsModuleContext());
|
||||
VerifyObjectField(kContextOffset);
|
||||
VerifyObjectField(kScopeInfoOffset);
|
||||
CHECK(context()->IsUndefined() || context()->IsModuleContext());
|
||||
}
|
||||
|
||||
|
||||
|
@ -3993,6 +3993,7 @@ BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_function, kIsFunction)
|
||||
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_optimize,
|
||||
kDontOptimize)
|
||||
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_inline, kDontInline)
|
||||
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_cache, kDontCache)
|
||||
|
||||
void SharedFunctionInfo::BeforeVisitingPointers() {
|
||||
if (IsInobjectSlackTrackingInProgress()) DetachInitialMap();
|
||||
@ -4443,6 +4444,7 @@ void Foreign::set_foreign_address(Address value) {
|
||||
|
||||
|
||||
ACCESSORS(JSModule, context, Object, kContextOffset)
|
||||
ACCESSORS(JSModule, scope_info, ScopeInfo, kScopeInfoOffset)
|
||||
|
||||
|
||||
JSModule* JSModule::cast(Object* obj) {
|
||||
|
@ -457,6 +457,8 @@ void JSModule::JSModulePrint(FILE* out) {
|
||||
PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
|
||||
PrintF(out, " - context = ");
|
||||
context()->Print(out);
|
||||
PrintF(out, " - scope_info = ");
|
||||
scope_info()->ShortPrint(out);
|
||||
PrintElementsKind(out, this->map()->elements_kind());
|
||||
PrintF(out, " {\n");
|
||||
PrintProperties(out);
|
||||
|
@ -3524,7 +3524,7 @@ class ScopeInfo : public FixedArray {
|
||||
FOR_EACH_NUMERIC_FIELD(DECL_INDEX)
|
||||
#undef DECL_INDEX
|
||||
#undef FOR_EACH_NUMERIC_FIELD
|
||||
kVariablePartIndex
|
||||
kVariablePartIndex
|
||||
};
|
||||
|
||||
// The layout of the variable part of a ScopeInfo is as follows:
|
||||
@ -5623,6 +5623,9 @@ class SharedFunctionInfo: public HeapObject {
|
||||
// Indicates that the function cannot be inlined.
|
||||
DECL_BOOLEAN_ACCESSORS(dont_inline)
|
||||
|
||||
// Indicates that code for this function cannot be cached.
|
||||
DECL_BOOLEAN_ACCESSORS(dont_cache)
|
||||
|
||||
// Indicates whether or not the code in the shared function support
|
||||
// deoptimization.
|
||||
inline bool has_deoptimization_support();
|
||||
@ -5857,6 +5860,7 @@ class SharedFunctionInfo: public HeapObject {
|
||||
kIsFunction,
|
||||
kDontOptimize,
|
||||
kDontInline,
|
||||
kDontCache,
|
||||
kCompilerHintsCount // Pseudo entry
|
||||
};
|
||||
|
||||
@ -5923,6 +5927,9 @@ class JSModule: public JSObject {
|
||||
// [context]: the context holding the module's locals, or undefined if none.
|
||||
DECL_ACCESSORS(context, Object)
|
||||
|
||||
// [scope_info]: Scope info.
|
||||
DECL_ACCESSORS(scope_info, ScopeInfo)
|
||||
|
||||
// Casting.
|
||||
static inline JSModule* cast(Object* obj);
|
||||
|
||||
@ -5939,7 +5946,8 @@ class JSModule: public JSObject {
|
||||
|
||||
// Layout description.
|
||||
static const int kContextOffset = JSObject::kHeaderSize;
|
||||
static const int kSize = kContextOffset + kPointerSize;
|
||||
static const int kScopeInfoOffset = kContextOffset + kPointerSize;
|
||||
static const int kSize = kScopeInfoOffset + kPointerSize;
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(JSModule);
|
||||
|
@ -1246,12 +1246,10 @@ Statement* Parser::ParseModuleElement(ZoneStringList* labels,
|
||||
}
|
||||
|
||||
|
||||
Block* Parser::ParseModuleDeclaration(ZoneStringList* names, bool* ok) {
|
||||
Statement* Parser::ParseModuleDeclaration(ZoneStringList* names, bool* ok) {
|
||||
// ModuleDeclaration:
|
||||
// 'module' Identifier Module
|
||||
|
||||
// Create new block with one expected declaration.
|
||||
Block* block = factory()->NewBlock(NULL, 1, true);
|
||||
Handle<String> name = ParseIdentifier(CHECK_OK);
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -1275,10 +1273,11 @@ Block* Parser::ParseModuleDeclaration(ZoneStringList* names, bool* ok) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO(rossberg): Add initialization statement to block.
|
||||
|
||||
if (names) names->Add(name, zone());
|
||||
return block;
|
||||
if (module->body() == NULL)
|
||||
return factory()->NewEmptyStatement();
|
||||
else
|
||||
return module->body();
|
||||
}
|
||||
|
||||
|
||||
@ -1344,16 +1343,23 @@ Module* Parser::ParseModuleLiteral(bool* ok) {
|
||||
scope->set_end_position(scanner().location().end_pos);
|
||||
body->set_scope(scope);
|
||||
|
||||
// Instance objects have to be created ahead of time (before code generation
|
||||
// linking them) because of potentially cyclic references between them.
|
||||
// We create them here, to avoid another pass over the AST.
|
||||
// Check that all exports are bound.
|
||||
Interface* interface = scope->interface();
|
||||
for (Interface::Iterator it = interface->iterator();
|
||||
!it.done(); it.Advance()) {
|
||||
if (scope->LocalLookup(it.name()) == NULL) {
|
||||
Handle<String> name(it.name());
|
||||
ReportMessage("module_export_undefined",
|
||||
Vector<Handle<String> >(&name, 1));
|
||||
*ok = false;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
interface->MakeModule(ok);
|
||||
ASSERT(ok);
|
||||
interface->MakeSingleton(Isolate::Current()->factory()->NewJSModule(), ok);
|
||||
ASSERT(ok);
|
||||
ASSERT(*ok);
|
||||
interface->Freeze(ok);
|
||||
ASSERT(ok);
|
||||
ASSERT(*ok);
|
||||
return factory()->NewModuleLiteral(body, interface);
|
||||
}
|
||||
|
||||
@ -1424,10 +1430,12 @@ Module* Parser::ParseModuleUrl(bool* ok) {
|
||||
|
||||
Module* result = factory()->NewModuleUrl(symbol);
|
||||
Interface* interface = result->interface();
|
||||
interface->MakeSingleton(Isolate::Current()->factory()->NewJSModule(), ok);
|
||||
ASSERT(ok);
|
||||
interface->Freeze(ok);
|
||||
ASSERT(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;
|
||||
}
|
||||
|
||||
@ -1743,7 +1751,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
|
||||
Scope* declaration_scope = DeclarationScope(mode);
|
||||
Variable* var = NULL;
|
||||
|
||||
// If a function scope exists, then we can statically declare this
|
||||
// If a suitable scope exists, then we can statically declare this
|
||||
// variable and also set its mode. In any case, a Declaration node
|
||||
// will be added to the scope so that the declaration can be added
|
||||
// to the corresponding activation frame at runtime if necessary.
|
||||
@ -1751,14 +1759,12 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
|
||||
// to the calling function context.
|
||||
// Similarly, strict mode eval scope does not leak variable declarations to
|
||||
// the caller's scope so we declare all locals, too.
|
||||
// Also for block scoped let/const bindings the variable can be
|
||||
// statically declared.
|
||||
if (declaration_scope->is_function_scope() ||
|
||||
declaration_scope->is_strict_or_extended_eval_scope() ||
|
||||
declaration_scope->is_block_scope() ||
|
||||
declaration_scope->is_module_scope() ||
|
||||
declaration->AsModuleDeclaration() != NULL) {
|
||||
// Declare the variable in the function scope.
|
||||
// Declare the variable in the declaration scope.
|
||||
var = declaration_scope->LocalLookup(name);
|
||||
if (var == NULL) {
|
||||
// Declare the name.
|
||||
|
@ -586,7 +586,7 @@ class Parser {
|
||||
void* ParseSourceElements(ZoneList<Statement*>* processor,
|
||||
int end_token, bool is_eval, bool* ok);
|
||||
Statement* ParseModuleElement(ZoneStringList* labels, bool* ok);
|
||||
Block* ParseModuleDeclaration(ZoneStringList* names, bool* ok);
|
||||
Statement* ParseModuleDeclaration(ZoneStringList* names, bool* ok);
|
||||
Module* ParseModule(bool* ok);
|
||||
Module* ParseModuleLiteral(bool* ok);
|
||||
Module* ParseModulePath(bool* ok);
|
||||
|
@ -8849,19 +8849,25 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushBlockContext) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSModule) {
|
||||
ASSERT(args.length() == 1);
|
||||
Object* obj = args[0];
|
||||
return isolate->heap()->ToBoolean(obj->IsJSModule());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_PushModuleContext) {
|
||||
NoHandleAllocation ha;
|
||||
ASSERT(args.length() == 2);
|
||||
CONVERT_ARG_CHECKED(ScopeInfo, scope_info, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSModule, instance, 1);
|
||||
ASSERT(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSModule, instance, 0);
|
||||
|
||||
Context* context;
|
||||
MaybeObject* maybe_context =
|
||||
isolate->heap()->AllocateModuleContext(isolate->context(),
|
||||
scope_info);
|
||||
if (!maybe_context->To(&context)) return maybe_context;
|
||||
// Also initialize the context slot of the instance object.
|
||||
instance->set_context(context);
|
||||
Context* context = Context::cast(instance->context());
|
||||
Context* previous = isolate->context();
|
||||
ASSERT(context->IsModuleContext());
|
||||
// Initialize the context links.
|
||||
context->set_previous(previous);
|
||||
context->set_closure(previous->closure());
|
||||
context->set_global(previous->global());
|
||||
isolate->set_context(context);
|
||||
|
||||
return context;
|
||||
|
@ -283,6 +283,9 @@ namespace internal {
|
||||
F(CreateArrayLiteral, 3, 1) \
|
||||
F(CreateArrayLiteralShallow, 3, 1) \
|
||||
\
|
||||
/* Harmony modules */ \
|
||||
F(IsJSModule, 1, 1) \
|
||||
\
|
||||
/* Harmony proxies */ \
|
||||
F(CreateJSProxy, 2, 1) \
|
||||
F(CreateJSFunctionProxy, 4, 1) \
|
||||
@ -330,7 +333,7 @@ namespace internal {
|
||||
F(PushWithContext, 2, 1) \
|
||||
F(PushCatchContext, 3, 1) \
|
||||
F(PushBlockContext, 2, 1) \
|
||||
F(PushModuleContext, 2, 1) \
|
||||
F(PushModuleContext, 1, 1) \
|
||||
F(DeleteContextSlot, 2, 1) \
|
||||
F(LoadContextSlot, 2, 2) \
|
||||
F(LoadContextSlotNoReferenceError, 2, 2) \
|
||||
|
@ -193,7 +193,8 @@ int ScopeInfo::ContextLength() {
|
||||
bool has_context = context_locals > 0 ||
|
||||
function_name_context_slot ||
|
||||
Type() == WITH_SCOPE ||
|
||||
(Type() == FUNCTION_SCOPE && CallsEval());
|
||||
(Type() == FUNCTION_SCOPE && CallsEval()) ||
|
||||
Type() == MODULE_SCOPE;
|
||||
if (has_context) {
|
||||
return Context::MIN_CONTEXT_SLOTS + context_locals +
|
||||
(function_name_context_slot ? 1 : 0);
|
||||
@ -222,11 +223,7 @@ bool ScopeInfo::HasHeapAllocatedLocals() {
|
||||
|
||||
|
||||
bool ScopeInfo::HasContext() {
|
||||
if (length() > 0) {
|
||||
return ContextLength() > 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return ContextLength() > 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "scopes.h"
|
||||
|
||||
#include "accessors.h"
|
||||
#include "bootstrapper.h"
|
||||
#include "compiler.h"
|
||||
#include "messages.h"
|
||||
@ -226,6 +227,12 @@ Scope* Scope::DeserializeScopeChain(Context* context, Scope* global_scope,
|
||||
for (Scope* s = innermost_scope; s != NULL; s = s->outer_scope()) {
|
||||
s->scope_inside_with_ = true;
|
||||
}
|
||||
} else if (context->IsModuleContext()) {
|
||||
ScopeInfo* scope_info = ScopeInfo::cast(context->module()->scope_info());
|
||||
current_scope = new(zone) Scope(current_scope,
|
||||
MODULE_SCOPE,
|
||||
Handle<ScopeInfo>(scope_info),
|
||||
zone);
|
||||
} else if (context->IsFunctionContext()) {
|
||||
ScopeInfo* scope_info = context->closure()->shared()->scope_info();
|
||||
current_scope = new(zone) Scope(current_scope,
|
||||
@ -634,6 +641,12 @@ bool Scope::AllocateVariables(CompilationInfo* info,
|
||||
// 3) 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;
|
||||
}
|
||||
|
||||
@ -1119,7 +1132,8 @@ bool Scope::MustAllocate(Variable* var) {
|
||||
inner_scope_calls_eval_ ||
|
||||
scope_contains_with_ ||
|
||||
is_catch_scope() ||
|
||||
is_block_scope())) {
|
||||
is_block_scope() ||
|
||||
is_module_scope())) {
|
||||
var->set_is_used(true);
|
||||
}
|
||||
// Global variables do not need to be allocated.
|
||||
@ -1307,4 +1321,76 @@ 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(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
|
||||
|
10
src/scopes.h
10
src/scopes.h
@ -280,7 +280,8 @@ class Scope: public ZoneObject {
|
||||
bool is_block_scope() const { return type_ == BLOCK_SCOPE; }
|
||||
bool is_with_scope() const { return type_ == WITH_SCOPE; }
|
||||
bool is_declaration_scope() const {
|
||||
return is_eval_scope() || is_function_scope() || is_global_scope();
|
||||
return is_eval_scope() || is_function_scope() ||
|
||||
is_module_scope() || is_global_scope();
|
||||
}
|
||||
bool is_classic_mode() const {
|
||||
return language_mode() == CLASSIC_MODE;
|
||||
@ -590,6 +591,13 @@ 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,
|
||||
|
@ -27,10 +27,188 @@
|
||||
|
||||
// Flags: --harmony-modules --harmony-scoping
|
||||
|
||||
// Test basic module linking.
|
||||
// Test basic module linking and initialization.
|
||||
|
||||
"use strict";
|
||||
|
||||
module R {
|
||||
// At this point, only functions and modules are initialized.
|
||||
assertEquals(undefined, v)
|
||||
assertEquals(undefined, vv)
|
||||
assertEquals(undefined, R.v)
|
||||
assertEquals(undefined, M.v)
|
||||
assertEquals(undefined, MM.v)
|
||||
assertEquals(undefined, F.v)
|
||||
assertEquals(undefined, G.v)
|
||||
assertThrows(function() { l }, ReferenceError)
|
||||
assertThrows(function() { ll }, ReferenceError)
|
||||
assertThrows(function() { R.l }, ReferenceError)
|
||||
assertThrows(function() { M.l }, ReferenceError)
|
||||
assertThrows(function() { MM.l }, ReferenceError)
|
||||
assertThrows(function() { F.l }, ReferenceError)
|
||||
assertThrows(function() { G.l }, ReferenceError)
|
||||
assertThrows(function() { c }, ReferenceError)
|
||||
assertThrows(function() { cc }, ReferenceError)
|
||||
assertThrows(function() { R.c }, ReferenceError)
|
||||
assertThrows(function() { M.c }, ReferenceError)
|
||||
assertThrows(function() { MM.c }, ReferenceError)
|
||||
assertThrows(function() { F.c }, ReferenceError)
|
||||
assertThrows(function() { G.c }, ReferenceError)
|
||||
assertEquals(4, f())
|
||||
assertEquals(24, ff())
|
||||
assertEquals(4, R.f())
|
||||
assertEquals(14, M.f())
|
||||
assertEquals(34, MM.f())
|
||||
assertEquals(44, F.f())
|
||||
assertEquals(14, G.f())
|
||||
|
||||
// All properties should already exist on the instance objects, though.
|
||||
assertTrue("v" in R)
|
||||
assertTrue("v" in RR)
|
||||
assertTrue("v" in M)
|
||||
assertTrue("v" in MM)
|
||||
assertTrue("v" in F)
|
||||
assertTrue("v" in G)
|
||||
assertTrue("l" in R)
|
||||
assertTrue("l" in RR)
|
||||
assertTrue("l" in M)
|
||||
assertTrue("l" in MM)
|
||||
assertTrue("l" in F)
|
||||
assertTrue("l" in G)
|
||||
assertTrue("c" in R)
|
||||
assertTrue("c" in RR)
|
||||
assertTrue("c" in M)
|
||||
assertTrue("c" in MM)
|
||||
assertTrue("c" in F)
|
||||
assertTrue("c" in G)
|
||||
assertTrue("f" in R)
|
||||
assertTrue("f" in RR)
|
||||
assertTrue("f" in M)
|
||||
assertTrue("f" in MM)
|
||||
assertTrue("f" in F)
|
||||
assertTrue("f" in G)
|
||||
assertTrue("M" in R)
|
||||
assertTrue("M" in RR)
|
||||
assertTrue("RR" in R)
|
||||
assertTrue("RR" in RR)
|
||||
|
||||
// And aliases should be identical.
|
||||
assertSame(R, RR)
|
||||
assertSame(R, R.RR)
|
||||
assertSame(M, R.M)
|
||||
assertSame(M, G)
|
||||
|
||||
// We can only assign to var.
|
||||
assertEquals(-1, v = -1)
|
||||
assertEquals(-2, R.v = -2)
|
||||
assertEquals(-2, v)
|
||||
assertEquals(-2, R.v)
|
||||
|
||||
assertThrows(function() { l = -1 }, ReferenceError)
|
||||
assertThrows(function() { R.l = -2 }, ReferenceError)
|
||||
assertThrows(function() { l }, ReferenceError)
|
||||
assertThrows(function() { R.l }, ReferenceError)
|
||||
|
||||
assertThrows(function() { eval("c = -1") }, SyntaxError)
|
||||
assertThrows(function() { R.c = -2 }, TypeError)
|
||||
|
||||
// Initialize first bunch or variables.
|
||||
export var v = 1
|
||||
export let l = 2
|
||||
export const c = 3
|
||||
export function f() { return 4 }
|
||||
|
||||
assertEquals(1, v)
|
||||
assertEquals(1, R.v)
|
||||
assertEquals(2, l)
|
||||
assertEquals(2, R.l)
|
||||
assertEquals(3, c)
|
||||
assertEquals(3, R.c)
|
||||
|
||||
assertEquals(-3, v = -3)
|
||||
assertEquals(-4, R.v = -4)
|
||||
assertEquals(-3, l = -3)
|
||||
assertEquals(-4, R.l = -4)
|
||||
assertThrows(function() { eval("c = -3") }, SyntaxError)
|
||||
assertThrows(function() { R.c = -4 }, TypeError)
|
||||
|
||||
assertEquals(-4, v)
|
||||
assertEquals(-4, R.v)
|
||||
assertEquals(-4, l)
|
||||
assertEquals(-4, R.l)
|
||||
assertEquals(3, c)
|
||||
assertEquals(3, R.c)
|
||||
|
||||
// Initialize nested module.
|
||||
export module M {
|
||||
export var v = 11
|
||||
export let l = 12
|
||||
export const c = 13
|
||||
export function f() { return 14 }
|
||||
}
|
||||
|
||||
assertEquals(11, M.v)
|
||||
assertEquals(11, G.v)
|
||||
assertEquals(12, M.l)
|
||||
assertEquals(12, G.l)
|
||||
assertEquals(13, M.c)
|
||||
assertEquals(13, G.c)
|
||||
|
||||
// Initialize non-exported variables.
|
||||
var vv = 21
|
||||
let ll = 22
|
||||
const cc = 23
|
||||
function ff() { return 24 }
|
||||
|
||||
assertEquals(21, vv)
|
||||
assertEquals(22, ll)
|
||||
assertEquals(23, cc)
|
||||
|
||||
// Initialize non-exported module.
|
||||
module MM {
|
||||
export var v = 31
|
||||
export let l = 32
|
||||
export const c = 33
|
||||
export function f() { return 34 }
|
||||
}
|
||||
|
||||
assertEquals(31, MM.v)
|
||||
assertEquals(32, MM.l)
|
||||
assertEquals(33, MM.c)
|
||||
|
||||
// Recursive self reference.
|
||||
export module RR = R
|
||||
}
|
||||
|
||||
// Initialize sibling module that was forward-used.
|
||||
module F {
|
||||
assertEquals(undefined, v)
|
||||
assertEquals(undefined, F.v)
|
||||
assertThrows(function() { l }, ReferenceError)
|
||||
assertThrows(function() { F.l }, ReferenceError)
|
||||
assertThrows(function() { c }, ReferenceError)
|
||||
assertThrows(function() { F.c }, ReferenceError)
|
||||
|
||||
export var v = 41
|
||||
export let l = 42
|
||||
export const c = 43
|
||||
export function f() { return 44 }
|
||||
|
||||
assertEquals(41, v)
|
||||
assertEquals(41, F.v)
|
||||
assertEquals(42, l)
|
||||
assertEquals(42, F.l)
|
||||
assertEquals(43, c)
|
||||
assertEquals(43, F.c)
|
||||
}
|
||||
|
||||
// Define recursive module alias.
|
||||
module G = R.M
|
||||
|
||||
|
||||
|
||||
// Second test with side effects and more module nesting.
|
||||
|
||||
let log = "";
|
||||
|
||||
export let x = (log += "1");
|
||||
@ -117,5 +295,4 @@ assertSame(M2, M1.A2);
|
||||
assertSame(M1, M1.A2.A1);
|
||||
assertSame(M2, M2.A1.A2);
|
||||
|
||||
// TODO(rossberg): inner declarations are not executed yet.
|
||||
// assertEquals("1234567890", log);
|
||||
assertEquals("1234567890", log);
|
||||
|
@ -116,6 +116,11 @@ x
|
||||
,
|
||||
y
|
||||
|
||||
var
|
||||
x
|
||||
,
|
||||
y
|
||||
|
||||
export
|
||||
var
|
||||
v1 = 1
|
||||
|
87
test/mjsunit/harmony/module-recompile.js
Normal file
87
test/mjsunit/harmony/module-recompile.js
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --harmony-modules
|
||||
|
||||
// Test that potential recompilation of the global scope does not screw up.
|
||||
|
||||
"use strict";
|
||||
|
||||
var N = 1e5; // Number of loop iterations that trigger optimization.
|
||||
|
||||
module A {
|
||||
export var x = 1
|
||||
export function f() { return x }
|
||||
}
|
||||
var f = A.f
|
||||
|
||||
assertEquals(1, A.x)
|
||||
assertEquals(1, A.f())
|
||||
assertEquals(1, f())
|
||||
|
||||
A.x = 2
|
||||
|
||||
assertEquals(2, A.x)
|
||||
assertEquals(2, A.f())
|
||||
assertEquals(2, f())
|
||||
|
||||
for (var i = 0; i < N; i++) {
|
||||
if (i > N) print("impossible");
|
||||
}
|
||||
|
||||
assertEquals(2, A.x)
|
||||
assertEquals(2, A.f())
|
||||
assertEquals(2, f())
|
||||
|
||||
|
||||
// Same test with loop inside a module.
|
||||
|
||||
module B {
|
||||
module A {
|
||||
export var x = 1
|
||||
export function f() { return x }
|
||||
}
|
||||
var f = A.f
|
||||
|
||||
assertEquals(1, A.x)
|
||||
assertEquals(1, A.f())
|
||||
assertEquals(1, f())
|
||||
|
||||
A.x = 2
|
||||
|
||||
assertEquals(2, A.x)
|
||||
assertEquals(2, A.f())
|
||||
assertEquals(2, f())
|
||||
|
||||
for (var i = 0; i < N; i++) {
|
||||
if (i > N) print("impossible");
|
||||
}
|
||||
|
||||
assertEquals(2, A.x)
|
||||
assertEquals(2, A.f())
|
||||
assertEquals(2, f())
|
||||
}
|
@ -33,6 +33,7 @@
|
||||
|
||||
print("begin.")
|
||||
|
||||
|
||||
export let x = print("0")
|
||||
|
||||
export module B = A.B
|
||||
@ -44,15 +45,25 @@ export module A {
|
||||
module BB = B
|
||||
export BB, x
|
||||
let x = print("2")
|
||||
let y = print("3")
|
||||
var y = print("3")
|
||||
let Ax = A.x
|
||||
try { A.y } catch (e) {} // throws
|
||||
let Az = A.z // undefined
|
||||
let Az2 = z // undefined
|
||||
A.g() // hoisted
|
||||
g() // hoisted
|
||||
let ABx = A.B.x
|
||||
let Ay = A.y
|
||||
let ABy = A.B.y
|
||||
let Bx = B.x
|
||||
let By = B.y
|
||||
let BBx = BB.x
|
||||
let BBy = BB.y
|
||||
let Af = A.f
|
||||
function f(x,y) { return x }
|
||||
}
|
||||
export let y = print("4")
|
||||
export var z = print("4.1")
|
||||
export function g() {}
|
||||
let Ax = A.x
|
||||
let Bx = B.x
|
||||
let ABx = A.B.x
|
||||
@ -92,6 +103,8 @@ export module E {
|
||||
let Bx = B.x
|
||||
// TODO(rossberg): Handle import *.
|
||||
// import A.*
|
||||
module B = A.B
|
||||
let y = A.y
|
||||
}
|
||||
|
||||
export module M1 {
|
||||
|
Loading…
Reference in New Issue
Block a user