[modules] Partial scope info support of modules

This introduces a new heap object ModuleInfo, which is to ModuleDescriptor
what ScopeInfo is to Scope.  When deserializing a scope info that contains
a module info, we deserialize the module info into a module descriptor and
put it into the synthesized module scope.

Currently, module infos only store exports.

R=adamk@chromium.org
BUG=v8:1569

Review-Url: https://codereview.chromium.org/2277253003
Cr-Commit-Position: refs/heads/master@{#39049}
This commit is contained in:
neis 2016-08-31 07:25:28 -07:00 committed by Commit bot
parent 60a783c246
commit 840d1e84f7
11 changed files with 256 additions and 39 deletions

View File

@ -11,6 +11,13 @@
namespace v8 {
namespace internal {
// An entry in ModuleVariableEntries consists of several slots:
enum ModuleVariableEntryOffset {
kModuleVariableNameOffset,
kModuleVariableIndexOffset,
kModuleVariablePropertiesOffset,
kModuleVariableEntryLength // Sentinel value.
};
Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
Scope* scope) {
@ -18,6 +25,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
ZoneList<Variable*>* locals = scope->locals();
int stack_local_count = 0;
int context_local_count = 0;
int module_vars_count = 0;
// Stack allocated block scope variables are allocated in the parent
// declaration scope, but are recorded in the block scope's scope info. First
// slot index indicates at which offset a particular scope starts in the
@ -25,13 +33,22 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
int first_slot_index = 0;
for (int i = 0; i < locals->length(); i++) {
Variable* var = locals->at(i);
if (var->IsStackLocal()) {
if (stack_local_count == 0) first_slot_index = var->index();
stack_local_count++;
} else if (var->IsContextSlot()) {
context_local_count++;
switch (var->location()) {
case VariableLocation::LOCAL:
if (stack_local_count == 0) first_slot_index = var->index();
stack_local_count++;
break;
case VariableLocation::CONTEXT:
context_local_count++;
break;
case VariableLocation::MODULE:
module_vars_count++;
break;
default:
break;
}
}
DCHECK(module_vars_count == 0 || scope->is_module_scope());
// Make sure we allocate the correct amount.
DCHECK_EQ(scope->ContextLocalCount(), context_local_count);
@ -82,7 +99,10 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
const int parameter_count = scope->num_parameters();
const int length = kVariablePartIndex + parameter_count +
(1 + stack_local_count) + 2 * context_local_count +
(has_receiver ? 1 : 0) + (has_function_name ? 2 : 0);
(has_receiver ? 1 : 0) + (has_function_name ? 2 : 0) +
(scope->is_module_scope()
? 2 + kModuleVariableEntryLength * module_vars_count
: 0);
Factory* factory = isolate->factory();
Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length);
@ -113,6 +133,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
HasSimpleParametersField::encode(has_simple_parameters) |
FunctionKindField::encode(function_kind);
scope_info->SetFlags(flags);
scope_info->SetParameterCount(parameter_count);
scope_info->SetStackLocalCount(stack_local_count);
scope_info->SetContextLocalCount(context_local_count);
@ -127,10 +148,10 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
}
}
// Add stack locals' names, context locals' names and info. We are assuming
// that the stack locals' slots are allocated in increasing order, so we can
// simply add them to the ScopeInfo object. Context locals are added using
// their index.
// Add stack locals' names, context locals' names and info, module variables'
// names and info. We are assuming that the stack locals' slots are allocated
// in increasing order, so we can simply add them to the ScopeInfo object.
// Context locals are added using their index.
DCHECK_EQ(index, scope_info->StackLocalFirstSlotIndex());
scope_info->set(index++, Smi::FromInt(first_slot_index));
DCHECK_EQ(index, scope_info->StackLocalEntriesIndex());
@ -138,26 +159,48 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
int stack_local_base = index;
int context_local_base = stack_local_base + stack_local_count;
int context_local_info_base = context_local_base + context_local_count;
int module_var_entry = scope_info->ModuleVariableEntriesIndex();
for (int i = 0; i < locals->length(); ++i) {
Variable* var = locals->at(i);
if (var->IsStackLocal()) {
int local_index = var->index() - first_slot_index;
DCHECK_LE(0, local_index);
DCHECK_LT(local_index, stack_local_count);
scope_info->set(stack_local_base + local_index, *var->name());
} else if (var->IsContextSlot()) {
// Due to duplicate parameters, context locals aren't guaranteed to come
// in order.
int local_index = var->index() - Context::MIN_CONTEXT_SLOTS;
DCHECK_LE(0, local_index);
DCHECK_LT(local_index, context_local_count);
uint32_t info = VariableModeField::encode(var->mode()) |
InitFlagField::encode(var->initialization_flag()) |
MaybeAssignedFlagField::encode(var->maybe_assigned());
scope_info->set(context_local_base + local_index, *var->name());
scope_info->set(context_local_info_base + local_index,
Smi::FromInt(info));
switch (var->location()) {
case VariableLocation::LOCAL: {
int local_index = var->index() - first_slot_index;
DCHECK_LE(0, local_index);
DCHECK_LT(local_index, stack_local_count);
scope_info->set(stack_local_base + local_index, *var->name());
break;
}
case VariableLocation::CONTEXT: {
// Due to duplicate parameters, context locals aren't guaranteed to come
// in order.
int local_index = var->index() - Context::MIN_CONTEXT_SLOTS;
DCHECK_LE(0, local_index);
DCHECK_LT(local_index, context_local_count);
uint32_t info = VariableModeField::encode(var->mode()) |
InitFlagField::encode(var->initialization_flag()) |
MaybeAssignedFlagField::encode(var->maybe_assigned());
scope_info->set(context_local_base + local_index, *var->name());
scope_info->set(context_local_info_base + local_index,
Smi::FromInt(info));
break;
}
case VariableLocation::MODULE: {
scope_info->set(module_var_entry + kModuleVariableNameOffset,
*var->name());
scope_info->set(module_var_entry + kModuleVariableIndexOffset,
Smi::FromInt(var->index()));
uint32_t properties =
VariableModeField::encode(var->mode()) |
InitFlagField::encode(var->initialization_flag()) |
MaybeAssignedFlagField::encode(var->maybe_assigned());
scope_info->set(module_var_entry + kModuleVariablePropertiesOffset,
Smi::FromInt(properties));
module_var_entry += kModuleVariableEntryLength;
break;
}
default:
break;
}
}
@ -183,6 +226,19 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
var_index == scope_info->ContextLength() - 1);
}
// Module-specific information (only for module scopes).
if (scope->is_module_scope()) {
Handle<ModuleInfo> module_info =
ModuleInfo::New(isolate, scope->AsModuleScope()->module());
DCHECK_EQ(index, scope_info->ModuleInfoEntryIndex());
scope_info->set(index++, *module_info);
DCHECK_EQ(index, scope_info->ModuleVariableCountIndex());
scope_info->set(index++, Smi::FromInt(module_vars_count));
DCHECK_EQ(index, scope_info->ModuleVariableEntriesIndex());
// The variable entries themselves have already been written above.
index += kModuleVariableEntryLength * module_vars_count;
}
DCHECK_EQ(index, scope_info->length());
DCHECK_EQ(scope->num_parameters(), scope_info->ParameterCount());
DCHECK_EQ(scope->num_heap_slots(), scope_info->ContextLength());
@ -367,6 +423,10 @@ String* ScopeInfo::FunctionName() {
return String::cast(get(FunctionNameEntryIndex()));
}
ModuleInfo* ScopeInfo::ModuleDescriptorInfo() {
DCHECK(scope_type() == MODULE_SCOPE);
return static_cast<ModuleInfo*>(get(ModuleInfoEntryIndex()));
}
String* ScopeInfo::ParameterName(int var) {
DCHECK_LE(0, var);
@ -461,6 +521,32 @@ int ScopeInfo::StackSlotIndex(String* name) {
return -1;
}
int ScopeInfo::ModuleIndex(Handle<String> name, VariableMode* mode,
InitializationFlag* init_flag,
MaybeAssignedFlag* maybe_assigned_flag) {
DCHECK_EQ(scope_type(), MODULE_SCOPE);
DCHECK(name->IsInternalizedString());
DCHECK_NOT_NULL(mode);
DCHECK_NOT_NULL(init_flag);
DCHECK_NOT_NULL(maybe_assigned_flag);
int module_vars_count = Smi::cast(get(ModuleVariableCountIndex()))->value();
int entry = ModuleVariableEntriesIndex();
for (int i = 0; i < module_vars_count; ++i) {
if (*name == get(entry + kModuleVariableNameOffset)) {
int index = Smi::cast(get(entry + kModuleVariableIndexOffset))->value();
int properties =
Smi::cast(get(entry + kModuleVariablePropertiesOffset))->value();
*mode = VariableModeField::decode(properties);
*init_flag = InitFlagField::decode(properties);
*maybe_assigned_flag = MaybeAssignedFlagField::decode(properties);
return index;
}
entry += kModuleVariableEntryLength;
}
return -1;
}
int ScopeInfo::ContextSlotIndex(Handle<ScopeInfo> scope_info,
Handle<String> name, VariableMode* mode,
@ -579,21 +665,28 @@ int ScopeInfo::ContextLocalNameEntriesIndex() {
return StackLocalEntriesIndex() + StackLocalCount();
}
int ScopeInfo::ContextLocalInfoEntriesIndex() {
return ContextLocalNameEntriesIndex() + ContextLocalCount();
}
int ScopeInfo::ReceiverEntryIndex() {
return ContextLocalInfoEntriesIndex() + ContextLocalCount();
}
int ScopeInfo::FunctionNameEntryIndex() {
return ReceiverEntryIndex() + (HasAllocatedReceiver() ? 1 : 0);
}
int ScopeInfo::ModuleInfoEntryIndex() {
return FunctionNameEntryIndex() + (HasFunctionName() ? 2 : 0);
}
int ScopeInfo::ModuleVariableCountIndex() { return ModuleInfoEntryIndex() + 1; }
int ScopeInfo::ModuleVariableEntriesIndex() {
return ModuleVariableCountIndex() + 1;
}
#ifdef DEBUG
static void PrintList(const char* list_name,
@ -638,6 +731,32 @@ void ScopeInfo::Print() {
}
#endif // DEBUG
Handle<ModuleInfo> ModuleInfo::New(Isolate* isolate, ModuleDescriptor* descr) {
// Serialize special exports.
Handle<FixedArray> special_exports =
isolate->factory()->NewFixedArray(descr->special_exports().length());
{
int i = 0;
for (auto entry : descr->special_exports()) {
special_exports->set(i++, *entry->Serialize(isolate));
}
}
// Serialize regular exports.
Handle<FixedArray> regular_exports = isolate->factory()->NewFixedArray(
static_cast<int>(descr->regular_exports().size()));
{
int i = 0;
for (const auto& it : descr->regular_exports()) {
regular_exports->set(i++, *it.second->Serialize(isolate));
}
}
Handle<ModuleInfo> result = isolate->factory()->NewModuleInfo();
result->set(kSpecialExportsIndex, *special_exports);
result->set(kRegularExportsIndex, *regular_exports);
return result;
}
} // namespace internal
} // namespace v8

View File

@ -164,6 +164,36 @@ ModuleScope::ModuleScope(DeclarationScope* script_scope,
DeclareThis(ast_value_factory);
}
ModuleScope::ModuleScope(Isolate* isolate, Handle<ScopeInfo> scope_info,
AstValueFactory* avfactory)
: DeclarationScope(avfactory->zone(), MODULE_SCOPE, scope_info) {
Zone* zone = avfactory->zone();
ModuleInfo* module_info = scope_info->ModuleDescriptorInfo();
set_language_mode(STRICT);
module_descriptor_ = new (zone) ModuleDescriptor(zone);
// Deserialize special exports.
Handle<FixedArray> special_exports = handle(module_info->special_exports());
for (int i = 0, n = special_exports->length(); i < n; ++i) {
Handle<FixedArray> serialized_entry(
FixedArray::cast(special_exports->get(i)), isolate);
module_descriptor_->AddSpecialExport(
ModuleDescriptor::Entry::Deserialize(isolate, avfactory,
serialized_entry),
avfactory->zone());
}
// Deserialize regular exports.
Handle<FixedArray> regular_exports = handle(module_info->regular_exports());
for (int i = 0, n = regular_exports->length(); i < n; ++i) {
Handle<FixedArray> serialized_entry(
FixedArray::cast(regular_exports->get(i)), isolate);
module_descriptor_->AddRegularExport(ModuleDescriptor::Entry::Deserialize(
isolate, avfactory, serialized_entry));
}
}
Scope::Scope(Zone* zone, ScopeType scope_type, Handle<ScopeInfo> scope_info)
: zone_(zone),
outer_scope_(nullptr),
@ -328,6 +358,11 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone,
} else {
outer_scope = new (zone) Scope(zone, BLOCK_SCOPE, scope_info);
}
} else if (context->IsModuleContext()) {
ScopeInfo* scope_info = context->closure()->shared()->scope_info();
DCHECK_EQ(scope_info->scope_type(), MODULE_SCOPE);
outer_scope = new (zone) ModuleScope(
isolate, Handle<ScopeInfo>(scope_info), ast_value_factory);
} else {
DCHECK(context->IsCatchContext());
String* name = context->catch_name();
@ -428,6 +463,12 @@ void DeclarationScope::Analyze(ParseInfo* info, AnalyzeMode mode) {
scope->outer_scope()->scope_type() == SCRIPT_SCOPE ||
scope->outer_scope()->already_resolved_);
// For modules, we want to start variable allocation at the surrounding script
// scope.
if (scope->is_module_scope()) {
scope = scope->outer_scope()->AsDeclarationScope();
}
scope->AllocateVariables(info, mode);
#ifdef DEBUG
@ -617,7 +658,8 @@ Variable* Scope::LookupInScopeInfo(const AstRawString* name) {
&init_flag, &maybe_assigned_flag);
if (index < 0 && scope_type() == MODULE_SCOPE) {
location = VariableLocation::MODULE;
index = -1; // TODO(neis): Find module variables in scope info.
index = scope_info_->ModuleIndex(name_handle, &mode, &init_flag,
&maybe_assigned_flag);
}
if (index < 0) return nullptr; // Nowhere found.

View File

@ -839,6 +839,8 @@ class ModuleScope final : public DeclarationScope {
public:
ModuleScope(DeclarationScope* script_scope,
AstValueFactory* ast_value_factory);
ModuleScope(Isolate* isolate, Handle<ScopeInfo> scope_info,
AstValueFactory* ast_value_factory);
ModuleDescriptor* module() const {
DCHECK_NOT_NULL(module_descriptor_);

View File

@ -49,7 +49,8 @@ class Variable final : public ZoneObject {
return ForceContextAllocationField::decode(bit_field_);
}
void ForceContextAllocation() {
DCHECK(IsUnallocated() || IsContextSlot());
DCHECK(IsUnallocated() || IsContextSlot() ||
location() == VariableLocation::MODULE);
bit_field_ = ForceContextAllocationField::update(bit_field_, true);
}
bool is_used() { return IsUsedField::decode(bit_field_); }

View File

@ -1393,6 +1393,12 @@ Handle<ScopeInfo> Factory::NewScopeInfo(int length) {
return scope_info;
}
Handle<ModuleInfo> Factory::NewModuleInfo() {
Handle<FixedArray> array = NewFixedArray(ModuleInfo::kLength, TENURED);
array->set_map_no_write_barrier(*module_info_map());
Handle<ModuleInfo> module_info = Handle<ModuleInfo>::cast(array);
return module_info;
}
Handle<JSObject> Factory::NewExternal(void* value) {
Handle<Foreign> foreign = NewForeign(static_cast<Address>(value));

View File

@ -557,6 +557,8 @@ class Factory final {
// Create a serialized scope info.
Handle<ScopeInfo> NewScopeInfo(int length);
Handle<ModuleInfo> NewModuleInfo();
// Create an External object for V8's external API.
Handle<JSObject> NewExternal(void* value);

View File

@ -2283,6 +2283,7 @@ bool Heap::CreateInitialMaps() {
DCHECK_NE(fixed_array_map(), fixed_cow_array_map());
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, scope_info)
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, module_info)
ALLOCATE_PRIMITIVE_MAP(HEAP_NUMBER_TYPE, HeapNumber::kSize, heap_number,
Context::NUMBER_FUNCTION_INDEX)
ALLOCATE_MAP(MUTABLE_HEAP_NUMBER_TYPE, HeapNumber::kSize,

View File

@ -49,6 +49,7 @@ using v8::MemoryPressureLevel;
V(Map, one_byte_string_map, OneByteStringMap) \
V(Map, one_byte_internalized_string_map, OneByteInternalizedStringMap) \
V(Map, scope_info_map, ScopeInfoMap) \
V(Map, module_info_map, ModuleInfoMap) \
V(Map, shared_function_info_map, SharedFunctionInfoMap) \
V(Map, code_map, CodeMap) \
V(Map, function_context_map, FunctionContextMap) \
@ -275,6 +276,7 @@ using v8::MemoryPressureLevel;
V(FixedArrayMap) \
V(CodeMap) \
V(ScopeInfoMap) \
V(ModuleInfoMap) \
V(FixedCOWArrayMap) \
V(FixedDoubleArrayMap) \
V(WeakCellMap) \

View File

@ -792,6 +792,9 @@ bool HeapObject::IsScopeInfo() const {
return map() == GetHeap()->scope_info_map();
}
bool HeapObject::IsModuleInfo() const {
return map() == GetHeap()->module_info_map();
}
TYPE_CHECKER(JSBoundFunction, JS_BOUND_FUNCTION_TYPE)
TYPE_CHECKER(JSFunction, JS_FUNCTION_TYPE)
@ -3281,6 +3284,7 @@ CAST_ACCESSOR(JSWeakMap)
CAST_ACCESSOR(JSWeakSet)
CAST_ACCESSOR(LayoutDescriptor)
CAST_ACCESSOR(Map)
CAST_ACCESSOR(ModuleInfo)
CAST_ACCESSOR(Name)
CAST_ACCESSOR(NameDictionary)
CAST_ACCESSOR(NormalizedMapCache)
@ -7916,6 +7920,13 @@ bool ScopeInfo::HasSimpleParameters() {
FOR_EACH_SCOPE_INFO_NUMERIC_FIELD(SCOPE_INFO_FIELD_ACCESSORS)
#undef SCOPE_INFO_FIELD_ACCESSORS
FixedArray* ModuleInfo::special_exports() const {
return FixedArray::cast(get(kSpecialExportsIndex));
}
FixedArray* ModuleInfo::regular_exports() const {
return FixedArray::cast(get(kRegularExportsIndex));
}
void Map::ClearCodeCache(Heap* heap) {
// No write barrier is needed since empty_fixed_array is not in new space.

View File

@ -94,6 +94,7 @@
// - TemplateList
// - TransitionArray
// - ScopeInfo
// - ModuleInfo
// - ScriptContextTable
// - WeakFixedArray
// - FixedDoubleArray
@ -879,6 +880,8 @@ class LayoutDescriptor;
class LiteralsArray;
class LookupIterator;
class FieldType;
class ModuleDescriptor;
class ModuleInfo;
class ObjectHashTable;
class ObjectVisitor;
class PropertyCell;
@ -979,6 +982,7 @@ template <class C> inline bool Is(Object* obj);
V(ScriptContextTable) \
V(NativeContext) \
V(ScopeInfo) \
V(ModuleInfo) \
V(JSBoundFunction) \
V(JSFunction) \
V(Code) \
@ -4317,6 +4321,8 @@ class ScopeInfo : public FixedArray {
// Return the function_name if present.
String* FunctionName();
ModuleInfo* ModuleDescriptorInfo();
// Return the name of the given parameter.
String* ParameterName(int var);
@ -4360,6 +4366,12 @@ class ScopeInfo : public FixedArray {
VariableMode* mode, InitializationFlag* init_flag,
MaybeAssignedFlag* maybe_assigned_flag);
// Lookup metadata of a MODULE-allocated variable. Return a negative value if
// there is no module variable with the given name.
int ModuleIndex(Handle<String> name, VariableMode* mode,
InitializationFlag* init_flag,
MaybeAssignedFlag* maybe_assigned_flag);
// Lookup the name of a certain context slot by its index.
String* ContextSlotName(int slot_index);
@ -4394,11 +4406,10 @@ class ScopeInfo : public FixedArray {
// The layout of the static part of a ScopeInfo is as follows. Each entry is
// numeric and occupies one array slot.
// 1. A set of properties of the scope
// 2. The number of parameters. This only applies to function scopes. For
// non-function scopes this is 0.
// 3. The number of non-parameter variables allocated on the stack.
// 4. The number of non-parameter and parameter variables allocated in the
// context.
// 2. The number of parameters. For non-function scopes this is 0.
// 3. The number of non-parameter variables allocated on the stack.
// 4. The number of non-parameter and parameter variables allocated in the
// context.
#define FOR_EACH_SCOPE_INFO_NUMERIC_FIELD(V) \
V(Flags) \
V(ParameterCount) \
@ -4445,14 +4456,18 @@ class ScopeInfo : public FixedArray {
// the context locals in ContextLocalNameEntries. One slot is used per
// context local, so in total this part occupies ContextLocalCount()
// slots in the array.
// 6. RecieverEntryIndex:
// 6. ReceiverEntry:
// If the scope binds a "this" value, one slot is reserved to hold the
// context or stack slot index for the variable.
// 7. FunctionNameEntryIndex:
// 7. FunctionNameEntry:
// If the scope belongs to a named function expression this part contains
// information about the function variable. It always occupies two array
// slots: a. The name of the function variable.
// b. The context or stack slot index for the variable.
// 8. ModuleInfoEntry, ModuleVariableCount, and ModuleVariableEntries:
// For a module scope, this part contains the ModuleInfo, the number of
// MODULE-allocated variables, and the metadata of those variables. For
// non-module scopes it is empty.
int ParameterEntriesIndex();
int StackLocalFirstSlotIndex();
int StackLocalEntriesIndex();
@ -4460,6 +4475,9 @@ class ScopeInfo : public FixedArray {
int ContextLocalInfoEntriesIndex();
int ReceiverEntryIndex();
int FunctionNameEntryIndex();
int ModuleInfoEntryIndex();
int ModuleVariableCountIndex();
int ModuleVariableEntriesIndex();
int Lookup(Handle<String> name, int start, int end, VariableMode* mode,
VariableLocation* location, InitializationFlag* init_flag,
@ -4502,6 +4520,18 @@ class ScopeInfo : public FixedArray {
friend class ScopeIterator;
};
// ModuleInfo is to ModuleDescriptor what ScopeInfo is to Scope.
class ModuleInfo : public FixedArray {
public:
DECLARE_CAST(ModuleInfo)
static Handle<ModuleInfo> New(Isolate* isolate, ModuleDescriptor* descr);
inline FixedArray* special_exports() const;
inline FixedArray* regular_exports() const;
private:
friend class Factory;
enum { kSpecialExportsIndex, kRegularExportsIndex, kLength };
};
// The cache for maps used by normalized (dictionary mode) objects.
// Such maps do not have property descriptors, so a typical program

View File

@ -566,6 +566,7 @@ void Parser::DeserializeScopeChain(
scope = Scope::DeserializeScopeChain(info->isolate(), zone(), *context,
script_scope, ast_value_factory(),
deserialization_mode);
DCHECK(!info->is_module() || scope->is_module_scope());
if (info->context().is_null()) {
DCHECK(deserialization_mode ==
Scope::DeserializationMode::kDeserializeOffHeap);