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:
rossberg@chromium.org 2012-07-09 08:59:03 +00:00
parent 1d0aea7c3a
commit 98db1a369d
28 changed files with 640 additions and 157 deletions

View File

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

View File

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

View File

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

View File

@ -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) {}
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -116,6 +116,11 @@ x
,
y
var
x
,
y
export
var
v1 = 1

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

View File

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