// Copyright 2011 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <stdlib.h> #include "src/objects/scope-info.h" #include "src/ast/context-slot-cache.h" #include "src/ast/scopes.h" #include "src/ast/variables.h" #include "src/bootstrapper.h" #include "src/objects-inl.h" namespace v8 { namespace internal { // An entry in ModuleVariableEntries consists of several slots: enum ModuleVariableEntryOffset { kModuleVariableNameOffset, kModuleVariableIndexOffset, kModuleVariablePropertiesOffset, kModuleVariableEntryLength // Sentinel value. }; #ifdef DEBUG bool ScopeInfo::Equals(ScopeInfo* other) const { if (length() != other->length()) return false; for (int index = 0; index < length(); ++index) { Object* entry = get(index); Object* other_entry = other->get(index); if (entry->IsSmi()) { if (entry != other_entry) return false; } else { if (HeapObject::cast(entry)->map()->instance_type() != HeapObject::cast(other_entry)->map()->instance_type()) { return false; } if (entry->IsString()) { if (!String::cast(entry)->Equals(String::cast(other_entry))) { return false; } } else if (entry->IsScopeInfo()) { if (!ScopeInfo::cast(entry)->Equals(ScopeInfo::cast(other_entry))) { return false; } } else if (entry->IsModuleInfo()) { if (!ModuleInfo::cast(entry)->Equals(ModuleInfo::cast(other_entry))) { return false; } } else { UNREACHABLE(); return false; } } } return true; } #endif Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope, MaybeHandle<ScopeInfo> outer_scope) { // Collect variables. 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 // parent declaration scope. int first_slot_index = 0; for (Variable* var : *scope->locals()) { 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); // Determine use and location of the "this" binding if it is present. VariableAllocationInfo receiver_info; if (scope->is_declaration_scope() && scope->AsDeclarationScope()->has_this_declaration()) { Variable* var = scope->AsDeclarationScope()->receiver(); if (!var->is_used()) { receiver_info = UNUSED; } else if (var->IsContextSlot()) { receiver_info = CONTEXT; } else { DCHECK(var->IsParameter()); receiver_info = STACK; } } else { receiver_info = NONE; } bool has_new_target = scope->is_declaration_scope() && scope->AsDeclarationScope()->new_target_var() != nullptr; // Determine use and location of the function variable if it is present. VariableAllocationInfo function_name_info; if (scope->is_function_scope() && scope->AsDeclarationScope()->function_var() != nullptr) { Variable* var = scope->AsDeclarationScope()->function_var(); if (!var->is_used()) { function_name_info = UNUSED; } else if (var->IsContextSlot()) { function_name_info = CONTEXT; } else { DCHECK(var->IsStackLocal()); function_name_info = STACK; } } else { function_name_info = NONE; } const bool has_function_name = function_name_info != NONE; const bool has_receiver = receiver_info == STACK || receiver_info == CONTEXT; const int parameter_count = scope->num_parameters(); const bool has_outer_scope_info = !outer_scope.is_null(); const int length = kVariablePartIndex + parameter_count + (1 + stack_local_count) + 2 * context_local_count + (has_receiver ? 1 : 0) + (has_function_name ? 2 : 0) + (has_outer_scope_info ? 1 : 0) + (scope->is_module_scope() ? 2 + kModuleVariableEntryLength * module_vars_count : 0); Factory* factory = isolate->factory(); Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length); bool has_simple_parameters = false; bool asm_module = false; bool asm_function = false; if (scope->is_function_scope()) { DeclarationScope* function_scope = scope->AsDeclarationScope(); has_simple_parameters = function_scope->has_simple_parameters(); asm_module = function_scope->asm_module(); asm_function = function_scope->asm_function(); } FunctionKind function_kind = kNormalFunction; if (scope->is_declaration_scope()) { function_kind = scope->AsDeclarationScope()->function_kind(); } // Encode the flags. int flags = ScopeTypeField::encode(scope->scope_type()) | CallsEvalField::encode(scope->calls_eval()) | LanguageModeField::encode(scope->language_mode()) | DeclarationScopeField::encode(scope->is_declaration_scope()) | ReceiverVariableField::encode(receiver_info) | HasNewTargetField::encode(has_new_target) | FunctionVariableField::encode(function_name_info) | AsmModuleField::encode(asm_module) | AsmFunctionField::encode(asm_function) | HasSimpleParametersField::encode(has_simple_parameters) | FunctionKindField::encode(function_kind) | HasOuterScopeInfoField::encode(has_outer_scope_info) | IsDebugEvaluateScopeField::encode(scope->is_debug_evaluate_scope()); scope_info->SetFlags(flags); scope_info->SetParameterCount(parameter_count); scope_info->SetStackLocalCount(stack_local_count); scope_info->SetContextLocalCount(context_local_count); int index = kVariablePartIndex; // Add parameters. DCHECK_EQ(index, scope_info->ParameterNamesIndex()); if (scope->is_declaration_scope()) { for (int i = 0; i < parameter_count; ++i) { scope_info->set(index++, *scope->AsDeclarationScope()->parameter(i)->name()); } } // 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->StackLocalNamesIndex()); 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->ModuleVariablesIndex(); for (Variable* var : *scope->locals()) { 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; } } index += stack_local_count + 2 * context_local_count; // If the receiver is allocated, add its index. DCHECK_EQ(index, scope_info->ReceiverInfoIndex()); if (has_receiver) { int var_index = scope->AsDeclarationScope()->receiver()->index(); scope_info->set(index++, Smi::FromInt(var_index)); // ?? DCHECK(receiver_info != CONTEXT || var_index == // scope_info->ContextLength() - 1); } // If present, add the function variable name and its index. DCHECK_EQ(index, scope_info->FunctionNameInfoIndex()); if (has_function_name) { int var_index = scope->AsDeclarationScope()->function_var()->index(); scope_info->set(index++, *scope->AsDeclarationScope()->function_var()->name()); scope_info->set(index++, Smi::FromInt(var_index)); DCHECK(function_name_info != CONTEXT || var_index == scope_info->ContextLength() - 1); } // If present, add the outer scope info. DCHECK(index == scope_info->OuterScopeInfoIndex()); if (has_outer_scope_info) { scope_info->set(index++, *outer_scope.ToHandleChecked()); } // Module-specific information (only for module scopes). if (scope->is_module_scope()) { Handle<ModuleInfo> module_info = ModuleInfo::New(isolate, zone, scope->AsModuleScope()->module()); DCHECK_EQ(index, scope_info->ModuleInfoIndex()); 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->ModuleVariablesIndex()); // 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()); return scope_info; } Handle<ScopeInfo> ScopeInfo::CreateForWithScope( Isolate* isolate, MaybeHandle<ScopeInfo> outer_scope) { const bool has_outer_scope_info = !outer_scope.is_null(); const int length = kVariablePartIndex + 1 + (has_outer_scope_info ? 1 : 0); Factory* factory = isolate->factory(); Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length); // Encode the flags. int flags = ScopeTypeField::encode(WITH_SCOPE) | CallsEvalField::encode(false) | LanguageModeField::encode(SLOPPY) | DeclarationScopeField::encode(false) | ReceiverVariableField::encode(NONE) | HasNewTargetField::encode(false) | FunctionVariableField::encode(NONE) | AsmModuleField::encode(false) | AsmFunctionField::encode(false) | HasSimpleParametersField::encode(true) | FunctionKindField::encode(kNormalFunction) | HasOuterScopeInfoField::encode(has_outer_scope_info) | IsDebugEvaluateScopeField::encode(false); scope_info->SetFlags(flags); scope_info->SetParameterCount(0); scope_info->SetStackLocalCount(0); scope_info->SetContextLocalCount(0); int index = kVariablePartIndex; DCHECK_EQ(index, scope_info->ParameterNamesIndex()); DCHECK_EQ(index, scope_info->StackLocalFirstSlotIndex()); scope_info->set(index++, Smi::kZero); DCHECK_EQ(index, scope_info->StackLocalNamesIndex()); DCHECK_EQ(index, scope_info->ReceiverInfoIndex()); DCHECK_EQ(index, scope_info->FunctionNameInfoIndex()); DCHECK(index == scope_info->OuterScopeInfoIndex()); if (has_outer_scope_info) { scope_info->set(index++, *outer_scope.ToHandleChecked()); } DCHECK_EQ(index, scope_info->length()); DCHECK_EQ(0, scope_info->ParameterCount()); DCHECK_EQ(Context::MIN_CONTEXT_SLOTS, scope_info->ContextLength()); return scope_info; } Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) { DCHECK(isolate->bootstrapper()->IsActive()); const int stack_local_count = 0; const int context_local_count = 1; const bool has_simple_parameters = true; const VariableAllocationInfo receiver_info = CONTEXT; const VariableAllocationInfo function_name_info = NONE; const bool has_function_name = false; const bool has_receiver = true; const bool has_outer_scope_info = false; const int parameter_count = 0; const int length = kVariablePartIndex + parameter_count + (1 + stack_local_count) + 2 * context_local_count + (has_receiver ? 1 : 0) + (has_function_name ? 2 : 0) + (has_outer_scope_info ? 1 : 0); Factory* factory = isolate->factory(); Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length); // Encode the flags. int flags = ScopeTypeField::encode(SCRIPT_SCOPE) | CallsEvalField::encode(false) | LanguageModeField::encode(SLOPPY) | DeclarationScopeField::encode(true) | ReceiverVariableField::encode(receiver_info) | FunctionVariableField::encode(function_name_info) | AsmModuleField::encode(false) | AsmFunctionField::encode(false) | HasSimpleParametersField::encode(has_simple_parameters) | FunctionKindField::encode(FunctionKind::kNormalFunction) | HasOuterScopeInfoField::encode(has_outer_scope_info) | IsDebugEvaluateScopeField::encode(false); scope_info->SetFlags(flags); scope_info->SetParameterCount(parameter_count); scope_info->SetStackLocalCount(stack_local_count); scope_info->SetContextLocalCount(context_local_count); int index = kVariablePartIndex; const int first_slot_index = 0; DCHECK_EQ(index, scope_info->StackLocalFirstSlotIndex()); scope_info->set(index++, Smi::FromInt(first_slot_index)); DCHECK_EQ(index, scope_info->StackLocalNamesIndex()); // Here we add info for context-allocated "this". DCHECK_EQ(index, scope_info->ContextLocalNamesIndex()); scope_info->set(index++, isolate->heap()->this_string()); DCHECK_EQ(index, scope_info->ContextLocalInfosIndex()); const uint32_t value = VariableModeField::encode(CONST) | InitFlagField::encode(kCreatedInitialized) | MaybeAssignedFlagField::encode(kNotAssigned); scope_info->set(index++, Smi::FromInt(value)); // And here we record that this scopeinfo binds a receiver. DCHECK_EQ(index, scope_info->ReceiverInfoIndex()); const int receiver_index = Context::MIN_CONTEXT_SLOTS + 0; scope_info->set(index++, Smi::FromInt(receiver_index)); DCHECK_EQ(index, scope_info->FunctionNameInfoIndex()); DCHECK_EQ(index, scope_info->OuterScopeInfoIndex()); DCHECK_EQ(index, scope_info->length()); DCHECK_EQ(scope_info->ParameterCount(), 0); DCHECK_EQ(scope_info->ContextLength(), Context::MIN_CONTEXT_SLOTS + 1); return scope_info; } ScopeInfo* ScopeInfo::Empty(Isolate* isolate) { return isolate->heap()->empty_scope_info(); } ScopeType ScopeInfo::scope_type() { DCHECK_LT(0, length()); return ScopeTypeField::decode(Flags()); } bool ScopeInfo::CallsEval() { return length() > 0 && CallsEvalField::decode(Flags()); } LanguageMode ScopeInfo::language_mode() { return length() > 0 ? LanguageModeField::decode(Flags()) : SLOPPY; } bool ScopeInfo::is_declaration_scope() { return DeclarationScopeField::decode(Flags()); } int ScopeInfo::LocalCount() { return StackLocalCount() + ContextLocalCount(); } int ScopeInfo::StackSlotCount() { if (length() > 0) { bool function_name_stack_slot = FunctionVariableField::decode(Flags()) == STACK; return StackLocalCount() + (function_name_stack_slot ? 1 : 0); } return 0; } int ScopeInfo::ContextLength() { if (length() > 0) { int context_locals = ContextLocalCount(); bool function_name_context_slot = FunctionVariableField::decode(Flags()) == CONTEXT; bool has_context = context_locals > 0 || function_name_context_slot || scope_type() == WITH_SCOPE || (scope_type() == BLOCK_SCOPE && CallsSloppyEval() && is_declaration_scope()) || (scope_type() == FUNCTION_SCOPE && CallsSloppyEval()) || (scope_type() == FUNCTION_SCOPE && IsAsmModule()) || scope_type() == MODULE_SCOPE; if (has_context) { return Context::MIN_CONTEXT_SLOTS + context_locals + (function_name_context_slot ? 1 : 0); } } return 0; } bool ScopeInfo::HasReceiver() { if (length() > 0) { return NONE != ReceiverVariableField::decode(Flags()); } else { return false; } } bool ScopeInfo::HasAllocatedReceiver() { if (length() > 0) { VariableAllocationInfo allocation = ReceiverVariableField::decode(Flags()); return allocation == STACK || allocation == CONTEXT; } else { return false; } } bool ScopeInfo::HasNewTarget() { return HasNewTargetField::decode(Flags()); } bool ScopeInfo::HasFunctionName() { if (length() > 0) { return NONE != FunctionVariableField::decode(Flags()); } else { return false; } } bool ScopeInfo::HasOuterScopeInfo() { if (length() > 0) { return HasOuterScopeInfoField::decode(Flags()); } else { return false; } } bool ScopeInfo::IsDebugEvaluateScope() { if (length() > 0) { return IsDebugEvaluateScopeField::decode(Flags()); } else { return false; } } void ScopeInfo::SetIsDebugEvaluateScope() { if (length() > 0) { DCHECK_EQ(scope_type(), WITH_SCOPE); SetFlags(Flags() | IsDebugEvaluateScopeField::encode(true)); } else { UNREACHABLE(); } } bool ScopeInfo::HasHeapAllocatedLocals() { if (length() > 0) { return ContextLocalCount() > 0; } else { return false; } } bool ScopeInfo::HasContext() { return ContextLength() > 0; } String* ScopeInfo::FunctionName() { DCHECK(HasFunctionName()); return String::cast(get(FunctionNameInfoIndex())); } ScopeInfo* ScopeInfo::OuterScopeInfo() { DCHECK(HasOuterScopeInfo()); return ScopeInfo::cast(get(OuterScopeInfoIndex())); } ModuleInfo* ScopeInfo::ModuleDescriptorInfo() { DCHECK(scope_type() == MODULE_SCOPE); return ModuleInfo::cast(get(ModuleInfoIndex())); } String* ScopeInfo::ParameterName(int var) { DCHECK_LE(0, var); DCHECK_LT(var, ParameterCount()); int info_index = ParameterNamesIndex() + var; return String::cast(get(info_index)); } String* ScopeInfo::LocalName(int var) { DCHECK_LE(0, var); DCHECK_LT(var, LocalCount()); DCHECK(StackLocalNamesIndex() + StackLocalCount() == ContextLocalNamesIndex()); int info_index = StackLocalNamesIndex() + var; return String::cast(get(info_index)); } String* ScopeInfo::StackLocalName(int var) { DCHECK_LE(0, var); DCHECK_LT(var, StackLocalCount()); int info_index = StackLocalNamesIndex() + var; return String::cast(get(info_index)); } int ScopeInfo::StackLocalIndex(int var) { DCHECK_LE(0, var); DCHECK_LT(var, StackLocalCount()); int first_slot_index = Smi::cast(get(StackLocalFirstSlotIndex()))->value(); return first_slot_index + var; } String* ScopeInfo::ContextLocalName(int var) { DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); int info_index = ContextLocalNamesIndex() + var; return String::cast(get(info_index)); } VariableMode ScopeInfo::ContextLocalMode(int var) { DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); int info_index = ContextLocalInfosIndex() + var; int value = Smi::cast(get(info_index))->value(); return VariableModeField::decode(value); } InitializationFlag ScopeInfo::ContextLocalInitFlag(int var) { DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); int info_index = ContextLocalInfosIndex() + var; int value = Smi::cast(get(info_index))->value(); return InitFlagField::decode(value); } MaybeAssignedFlag ScopeInfo::ContextLocalMaybeAssignedFlag(int var) { DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); int info_index = ContextLocalInfosIndex() + var; int value = Smi::cast(get(info_index))->value(); return MaybeAssignedFlagField::decode(value); } bool ScopeInfo::VariableIsSynthetic(String* name) { // There's currently no flag stored on the ScopeInfo to indicate that a // variable is a compiler-introduced temporary. However, to avoid conflict // with user declarations, the current temporaries like .generator_object and // .result start with a dot, so we can use that as a flag. It's a hack! return name->length() == 0 || name->Get(0) == '.' || name->Equals(name->GetHeap()->this_string()); } int ScopeInfo::StackSlotIndex(String* name) { DCHECK(name->IsInternalizedString()); if (length() > 0) { int first_slot_index = Smi::cast(get(StackLocalFirstSlotIndex()))->value(); int start = StackLocalNamesIndex(); int end = start + StackLocalCount(); for (int i = start; i < end; ++i) { if (name == get(i)) { return i - start + first_slot_index; } } } 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 = ModuleVariablesIndex(); for (int i = 0; i < module_vars_count; ++i) { if (*name == get(entry + kModuleVariableNameOffset)) { int index; ModuleVariable(i, nullptr, &index, mode, init_flag, maybe_assigned_flag); return index; } entry += kModuleVariableEntryLength; } return 0; } int ScopeInfo::ContextSlotIndex(Handle<ScopeInfo> scope_info, Handle<String> name, VariableMode* mode, InitializationFlag* init_flag, MaybeAssignedFlag* maybe_assigned_flag) { DCHECK(name->IsInternalizedString()); DCHECK_NOT_NULL(mode); DCHECK_NOT_NULL(init_flag); DCHECK_NOT_NULL(maybe_assigned_flag); if (scope_info->length() > 0) { ContextSlotCache* context_slot_cache = scope_info->GetIsolate()->context_slot_cache(); int result = context_slot_cache->Lookup(*scope_info, *name, mode, init_flag, maybe_assigned_flag); if (result != ContextSlotCache::kNotFound) { DCHECK_LT(result, scope_info->ContextLength()); return result; } int start = scope_info->ContextLocalNamesIndex(); int end = start + scope_info->ContextLocalCount(); for (int i = start; i < end; ++i) { if (*name == scope_info->get(i)) { int var = i - start; *mode = scope_info->ContextLocalMode(var); *init_flag = scope_info->ContextLocalInitFlag(var); *maybe_assigned_flag = scope_info->ContextLocalMaybeAssignedFlag(var); result = Context::MIN_CONTEXT_SLOTS + var; context_slot_cache->Update(scope_info, name, *mode, *init_flag, *maybe_assigned_flag, result); DCHECK_LT(result, scope_info->ContextLength()); return result; } } // Cache as not found. Mode, init flag and maybe assigned flag don't matter. context_slot_cache->Update(scope_info, name, TEMPORARY, kNeedsInitialization, kNotAssigned, -1); } return -1; } String* ScopeInfo::ContextSlotName(int slot_index) { int const var = slot_index - Context::MIN_CONTEXT_SLOTS; DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); return ContextLocalName(var); } int ScopeInfo::ParameterIndex(String* name) { DCHECK(name->IsInternalizedString()); if (length() > 0) { // We must read parameters from the end since for // multiply declared parameters the value of the // last declaration of that parameter is used // inside a function (and thus we need to look // at the last index). Was bug# 1110337. int start = ParameterNamesIndex(); int end = start + ParameterCount(); for (int i = end - 1; i >= start; --i) { if (name == get(i)) { return i - start; } } } return -1; } int ScopeInfo::ReceiverContextSlotIndex() { if (length() > 0 && ReceiverVariableField::decode(Flags()) == CONTEXT) return Smi::cast(get(ReceiverInfoIndex()))->value(); return -1; } int ScopeInfo::FunctionContextSlotIndex(String* name) { DCHECK(name->IsInternalizedString()); if (length() > 0) { if (FunctionVariableField::decode(Flags()) == CONTEXT && FunctionName() == name) { return Smi::cast(get(FunctionNameInfoIndex() + 1))->value(); } } return -1; } FunctionKind ScopeInfo::function_kind() { return FunctionKindField::decode(Flags()); } int ScopeInfo::ParameterNamesIndex() { DCHECK_LT(0, length()); return kVariablePartIndex; } int ScopeInfo::StackLocalFirstSlotIndex() { return ParameterNamesIndex() + ParameterCount(); } int ScopeInfo::StackLocalNamesIndex() { return StackLocalFirstSlotIndex() + 1; } int ScopeInfo::ContextLocalNamesIndex() { return StackLocalNamesIndex() + StackLocalCount(); } int ScopeInfo::ContextLocalInfosIndex() { return ContextLocalNamesIndex() + ContextLocalCount(); } int ScopeInfo::ReceiverInfoIndex() { return ContextLocalInfosIndex() + ContextLocalCount(); } int ScopeInfo::FunctionNameInfoIndex() { return ReceiverInfoIndex() + (HasAllocatedReceiver() ? 1 : 0); } int ScopeInfo::OuterScopeInfoIndex() { return FunctionNameInfoIndex() + (HasFunctionName() ? 2 : 0); } int ScopeInfo::ModuleInfoIndex() { return OuterScopeInfoIndex() + (HasOuterScopeInfo() ? 1 : 0); } int ScopeInfo::ModuleVariableCountIndex() { return ModuleInfoIndex() + 1; } int ScopeInfo::ModuleVariablesIndex() { return ModuleVariableCountIndex() + 1; } void ScopeInfo::ModuleVariable(int i, String** name, int* index, VariableMode* mode, InitializationFlag* init_flag, MaybeAssignedFlag* maybe_assigned_flag) { DCHECK_LE(0, i); DCHECK_LT(i, Smi::cast(get(ModuleVariableCountIndex()))->value()); int entry = ModuleVariablesIndex() + i * kModuleVariableEntryLength; int properties = Smi::cast(get(entry + kModuleVariablePropertiesOffset))->value(); if (name != nullptr) { *name = String::cast(get(entry + kModuleVariableNameOffset)); } if (index != nullptr) { *index = Smi::cast(get(entry + kModuleVariableIndexOffset))->value(); DCHECK_NE(*index, 0); } if (mode != nullptr) { *mode = VariableModeField::decode(properties); } if (init_flag != nullptr) { *init_flag = InitFlagField::decode(properties); } if (maybe_assigned_flag != nullptr) { *maybe_assigned_flag = MaybeAssignedFlagField::decode(properties); } } #ifdef DEBUG static void PrintList(const char* list_name, int nof_internal_slots, int start, int end, ScopeInfo* scope_info) { if (start < end) { PrintF("\n // %s\n", list_name); if (nof_internal_slots > 0) { PrintF(" %2d - %2d [internal slots]\n", 0, nof_internal_slots - 1); } for (int i = nof_internal_slots; start < end; ++i, ++start) { PrintF(" %2d ", i); String::cast(scope_info->get(start))->ShortPrint(); PrintF("\n"); } } } void ScopeInfo::Print() { PrintF("ScopeInfo "); if (HasFunctionName()) { FunctionName()->ShortPrint(); } else { PrintF("/* no function name */"); } PrintF("{"); if (length() > 0) { PrintList("parameters", 0, ParameterNamesIndex(), ParameterNamesIndex() + ParameterCount(), this); PrintList("stack slots", 0, StackLocalNamesIndex(), StackLocalNamesIndex() + StackLocalCount(), this); PrintList("context slots", Context::MIN_CONTEXT_SLOTS, ContextLocalNamesIndex(), ContextLocalNamesIndex() + ContextLocalCount(), this); // TODO(neis): Print module stuff if present. } PrintF("}\n"); } #endif // DEBUG Handle<ModuleInfoEntry> ModuleInfoEntry::New(Isolate* isolate, Handle<Object> export_name, Handle<Object> local_name, Handle<Object> import_name, int module_request, int cell_index, int beg_pos, int end_pos) { Handle<ModuleInfoEntry> result = Handle<ModuleInfoEntry>::cast( isolate->factory()->NewStruct(MODULE_INFO_ENTRY_TYPE)); result->set_export_name(*export_name); result->set_local_name(*local_name); result->set_import_name(*import_name); result->set_module_request(module_request); result->set_cell_index(cell_index); result->set_beg_pos(beg_pos); result->set_end_pos(end_pos); return result; } Handle<ModuleInfo> ModuleInfo::New(Isolate* isolate, Zone* zone, ModuleDescriptor* descr) { // Serialize module requests. Handle<FixedArray> module_requests = isolate->factory()->NewFixedArray( static_cast<int>(descr->module_requests().size())); for (const auto& elem : descr->module_requests()) { module_requests->set(elem.second, *elem.first->string()); } // Serialize special exports. Handle<FixedArray> special_exports = isolate->factory()->NewFixedArray(descr->special_exports().length()); { int i = 0; for (auto entry : descr->special_exports()) { Handle<ModuleInfoEntry> serialized_entry = entry->Serialize(isolate); special_exports->set(i++, *serialized_entry); } } // Serialize namespace imports. Handle<FixedArray> namespace_imports = isolate->factory()->NewFixedArray(descr->namespace_imports().length()); { int i = 0; for (auto entry : descr->namespace_imports()) { Handle<ModuleInfoEntry> serialized_entry = entry->Serialize(isolate); namespace_imports->set(i++, *serialized_entry); } } // Serialize regular exports. Handle<FixedArray> regular_exports = descr->SerializeRegularExports(isolate, zone); // Serialize regular imports. Handle<FixedArray> regular_imports = isolate->factory()->NewFixedArray( static_cast<int>(descr->regular_imports().size())); { int i = 0; for (const auto& elem : descr->regular_imports()) { Handle<ModuleInfoEntry> serialized_entry = elem.second->Serialize(isolate); regular_imports->set(i++, *serialized_entry); } } Handle<ModuleInfo> result = isolate->factory()->NewModuleInfo(); result->set(kModuleRequestsIndex, *module_requests); result->set(kSpecialExportsIndex, *special_exports); result->set(kRegularExportsIndex, *regular_exports); result->set(kNamespaceImportsIndex, *namespace_imports); result->set(kRegularImportsIndex, *regular_imports); return result; } int ModuleInfo::RegularExportCount() const { DCHECK_EQ(regular_exports()->length() % kRegularExportLength, 0); return regular_exports()->length() / kRegularExportLength; } String* ModuleInfo::RegularExportLocalName(int i) const { return String::cast(regular_exports()->get(i * kRegularExportLength + kRegularExportLocalNameOffset)); } int ModuleInfo::RegularExportCellIndex(int i) const { return Smi::cast(regular_exports()->get(i * kRegularExportLength + kRegularExportCellIndexOffset)) ->value(); } FixedArray* ModuleInfo::RegularExportExportNames(int i) const { return FixedArray::cast(regular_exports()->get( i * kRegularExportLength + kRegularExportExportNamesOffset)); } Handle<ModuleInfoEntry> ModuleInfo::LookupRegularImport( Handle<ModuleInfo> info, Handle<String> local_name) { Isolate* isolate = info->GetIsolate(); Handle<FixedArray> regular_imports(info->regular_imports(), isolate); for (int i = 0, n = regular_imports->length(); i < n; ++i) { Handle<ModuleInfoEntry> entry( ModuleInfoEntry::cast(regular_imports->get(i)), isolate); if (String::cast(entry->local_name())->Equals(*local_name)) { return entry; } } UNREACHABLE(); return Handle<ModuleInfoEntry>(); } } // namespace internal } // namespace v8