// 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 #include "src/objects/scope-info.h" #include "src/ast/scopes.h" #include "src/ast/variables.h" #include "src/init/bootstrapper.h" #include "src/objects/module-inl.h" #include "src/objects/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.IsSourceTextModuleInfo()) { if (!SourceTextModuleInfo::cast(entry).Equals( SourceTextModuleInfo::cast(other_entry))) { return false; } } else { UNREACHABLE(); } } } return true; } #endif // static Handle ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope, MaybeHandle outer_scope) { // Collect variables. 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. for (Variable* var : *scope->locals()) { switch (var->location()) { case VariableLocation::CONTEXT: context_local_count++; break; case VariableLocation::MODULE: module_vars_count++; break; default: break; } } // 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; context_local_count++; } else { DCHECK(var->IsParameter()); receiver_info = STACK; } } else { receiver_info = NONE; } DCHECK(module_vars_count == 0 || scope->is_module_scope()); // Make sure we allocate the correct amount. DCHECK_EQ(scope->ContextLocalCount(), context_local_count); const bool has_new_target = scope->is_declaration_scope() && scope->AsDeclarationScope()->new_target_var() != nullptr; // TODO(cbruni): Don't always waste a field for the inferred name. const bool has_inferred_function_name = scope->is_function_scope(); // Determine use and location of the function variable if it is present. VariableAllocationInfo function_name_info; if (scope->is_function_scope()) { if (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 { // Always reserve space for the debug name in the scope info. function_name_info = UNUSED; } } else if (scope->is_module_scope() || scope->is_script_scope() || scope->is_eval_scope()) { // Always reserve space for the debug name in the scope info. function_name_info = UNUSED; } else { function_name_info = NONE; } const bool has_brand = scope->is_class_scope() ? scope->AsClassScope()->brand() != nullptr : false; const bool has_function_name = function_name_info != NONE; const bool has_position_info = NeedsPositionInfo(scope->scope_type()); const bool has_receiver = receiver_info == STACK || receiver_info == CONTEXT; const int parameter_count = scope->is_declaration_scope() ? scope->AsDeclarationScope()->num_parameters() : 0; const bool has_outer_scope_info = !outer_scope.is_null(); const int length = kVariablePartIndex + 2 * context_local_count + (has_receiver ? 1 : 0) + (has_function_name ? kFunctionNameEntries : 0) + (has_inferred_function_name ? 1 : 0) + (has_position_info ? kPositionInfoEntries : 0) + (has_outer_scope_info ? 1 : 0) + (scope->is_module_scope() ? 2 + kModuleVariableEntryLength * module_vars_count : 0); Handle scope_info_handle = isolate->factory()->NewScopeInfo(length); int index = kVariablePartIndex; { DisallowHeapAllocation no_gc; ScopeInfo scope_info = *scope_info_handle; WriteBarrierMode mode = scope_info.GetWriteBarrierMode(no_gc); bool has_simple_parameters = false; bool is_asm_module = false; bool calls_sloppy_eval = false; if (scope->is_function_scope()) { DeclarationScope* function_scope = scope->AsDeclarationScope(); has_simple_parameters = function_scope->has_simple_parameters(); is_asm_module = function_scope->is_asm_module(); } FunctionKind function_kind = kNormalFunction; if (scope->is_declaration_scope()) { function_kind = scope->AsDeclarationScope()->function_kind(); calls_sloppy_eval = scope->AsDeclarationScope()->calls_sloppy_eval(); } // Encode the flags. int flags = ScopeTypeField::encode(scope->scope_type()) | CallsSloppyEvalField::encode(calls_sloppy_eval) | LanguageModeField::encode(scope->language_mode()) | DeclarationScopeField::encode(scope->is_declaration_scope()) | ReceiverVariableField::encode(receiver_info) | HasClassBrandField::encode(has_brand) | HasNewTargetField::encode(has_new_target) | FunctionVariableField::encode(function_name_info) | HasInferredFunctionNameField::encode(has_inferred_function_name) | IsAsmModuleField::encode(is_asm_module) | HasSimpleParametersField::encode(has_simple_parameters) | FunctionKindField::encode(function_kind) | HasOuterScopeInfoField::encode(has_outer_scope_info) | IsDebugEvaluateScopeField::encode(scope->is_debug_evaluate_scope()) | ForceContextAllocationField::encode( scope->ForceContextForLanguageMode()); scope_info.SetFlags(flags); scope_info.SetParameterCount(parameter_count); scope_info.SetContextLocalCount(context_local_count); // Add context locals' names and info, module variables' names and info. // Context locals are added using their index. int context_local_base = index; 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::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()) | RequiresBrandCheckField::encode( var->get_requires_brand_check_flag()) | MaybeAssignedFlagField::encode(var->maybe_assigned()) | ParameterNumberField::encode(ParameterNumberField::kMax); scope_info.set(context_local_base + local_index, *var->name(), mode); 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(), mode); 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()) | RequiresBrandCheckField::encode( var->get_requires_brand_check_flag()) | ParameterNumberField::encode(ParameterNumberField::kMax); scope_info.set(module_var_entry + kModuleVariablePropertiesOffset, Smi::FromInt(properties)); module_var_entry += kModuleVariableEntryLength; break; } default: break; } } if (scope->is_declaration_scope()) { // Mark contexts slots with the parameter number they represent. We walk // the list of parameters. That can include duplicate entries if a // parameter name is repeated. By walking upwards, we'll automatically // mark the context slot with the highest parameter number that uses this // variable. That will be the parameter number that is represented by the // context slot. All lower parameters will only be available on the stack // through the arguments object. for (int i = 0; i < parameter_count; i++) { Variable* parameter = scope->AsDeclarationScope()->parameter(i); if (parameter->location() != VariableLocation::CONTEXT) continue; int index = parameter->index() - Context::MIN_CONTEXT_SLOTS; int info_index = context_local_info_base + index; int info = Smi::ToInt(scope_info.get(info_index)); info = ParameterNumberField::update(info, i); scope_info.set(info_index, Smi::FromInt(info)); } // TODO(verwaest): Remove this unnecessary entry. if (scope->AsDeclarationScope()->has_this_declaration()) { Variable* var = scope->AsDeclarationScope()->receiver(); if (var->location() == VariableLocation::CONTEXT) { int local_index = var->index() - Context::MIN_CONTEXT_SLOTS; uint32_t info = VariableModeField::encode(var->mode()) | InitFlagField::encode(var->initialization_flag()) | MaybeAssignedFlagField::encode(var->maybe_assigned()) | RequiresBrandCheckField::encode( var->get_requires_brand_check_flag()) | ParameterNumberField::encode(ParameterNumberField::kMax); scope_info.set(context_local_base + local_index, *var->name(), mode); scope_info.set(context_local_info_base + local_index, Smi::FromInt(info)); } } } index += 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) { Variable* var = scope->AsDeclarationScope()->function_var(); int var_index = -1; Object name = Smi::kZero; if (var != nullptr) { var_index = var->index(); name = *var->name(); } scope_info.set(index++, name, mode); scope_info.set(index++, Smi::FromInt(var_index)); DCHECK(function_name_info != CONTEXT || var_index == scope_info.ContextLength() - 1); } DCHECK_EQ(index, scope_info.InferredFunctionNameIndex()); if (has_inferred_function_name) { // The inferred function name is taken from the SFI. index++; } DCHECK_EQ(index, scope_info.PositionInfoIndex()); if (has_position_info) { scope_info.set(index++, Smi::FromInt(scope->start_position())); scope_info.set(index++, Smi::FromInt(scope->end_position())); } // If present, add the outer scope info. DCHECK(index == scope_info.OuterScopeInfoIndex()); if (has_outer_scope_info) { scope_info.set(index++, *outer_scope.ToHandleChecked(), mode); } } // Module-specific information (only for module scopes). if (scope->is_module_scope()) { Handle module_info = SourceTextModuleInfo::New( isolate, zone, scope->AsModuleScope()->module()); DCHECK_EQ(index, scope_info_handle->ModuleInfoIndex()); scope_info_handle->set(index++, *module_info); DCHECK_EQ(index, scope_info_handle->ModuleVariableCountIndex()); scope_info_handle->set(index++, Smi::FromInt(module_vars_count)); DCHECK_EQ(index, scope_info_handle->ModuleVariablesIndex()); // The variable entries themselves have already been written above. index += kModuleVariableEntryLength * module_vars_count; } DCHECK_EQ(index, scope_info_handle->length()); DCHECK_EQ(parameter_count, scope_info_handle->ParameterCount()); DCHECK_EQ(scope->num_heap_slots(), scope_info_handle->ContextLength()); return scope_info_handle; } // static Handle ScopeInfo::CreateForWithScope( Isolate* isolate, MaybeHandle outer_scope) { const bool has_outer_scope_info = !outer_scope.is_null(); const int length = kVariablePartIndex + (has_outer_scope_info ? 1 : 0); Factory* factory = isolate->factory(); Handle scope_info = factory->NewScopeInfo(length); // Encode the flags. int flags = ScopeTypeField::encode(WITH_SCOPE) | CallsSloppyEvalField::encode(false) | LanguageModeField::encode(LanguageMode::kSloppy) | DeclarationScopeField::encode(false) | ReceiverVariableField::encode(NONE) | HasClassBrandField::encode(false) | HasNewTargetField::encode(false) | FunctionVariableField::encode(NONE) | IsAsmModuleField::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->SetContextLocalCount(0); int index = kVariablePartIndex; DCHECK_EQ(index, scope_info->ReceiverInfoIndex()); DCHECK_EQ(index, scope_info->FunctionNameInfoIndex()); DCHECK_EQ(index, scope_info->InferredFunctionNameIndex()); DCHECK_EQ(index, scope_info->PositionInfoIndex()); 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; } // static Handle ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) { return CreateForBootstrapping(isolate, SCRIPT_SCOPE); } // static Handle ScopeInfo::CreateForEmptyFunction(Isolate* isolate) { return CreateForBootstrapping(isolate, FUNCTION_SCOPE); } // static Handle ScopeInfo::CreateForBootstrapping(Isolate* isolate, ScopeType type) { DCHECK(type == SCRIPT_SCOPE || type == FUNCTION_SCOPE); const int parameter_count = 0; const bool is_empty_function = type == FUNCTION_SCOPE; const int context_local_count = is_empty_function ? 0 : 1; const bool has_receiver = !is_empty_function; const bool has_inferred_function_name = is_empty_function; const bool has_position_info = true; const int length = kVariablePartIndex + 2 * context_local_count + (has_receiver ? 1 : 0) + (is_empty_function ? kFunctionNameEntries : 0) + (has_inferred_function_name ? 1 : 0) + (has_position_info ? kPositionInfoEntries : 0); Factory* factory = isolate->factory(); Handle scope_info = factory->NewScopeInfo(length); // Encode the flags. int flags = ScopeTypeField::encode(type) | CallsSloppyEvalField::encode(false) | LanguageModeField::encode(LanguageMode::kSloppy) | DeclarationScopeField::encode(true) | ReceiverVariableField::encode(is_empty_function ? UNUSED : CONTEXT) | HasClassBrandField::encode(false) | HasNewTargetField::encode(false) | FunctionVariableField::encode(is_empty_function ? UNUSED : NONE) | HasInferredFunctionNameField::encode(has_inferred_function_name) | IsAsmModuleField::encode(false) | HasSimpleParametersField::encode(true) | FunctionKindField::encode(FunctionKind::kNormalFunction) | HasOuterScopeInfoField::encode(false) | IsDebugEvaluateScopeField::encode(false); scope_info->SetFlags(flags); scope_info->SetParameterCount(parameter_count); scope_info->SetContextLocalCount(context_local_count); int index = kVariablePartIndex; // Here we add info for context-allocated "this". DCHECK_EQ(index, scope_info->ContextLocalNamesIndex()); if (context_local_count) { scope_info->set(index++, ReadOnlyRoots(isolate).this_string()); } DCHECK_EQ(index, scope_info->ContextLocalInfosIndex()); if (context_local_count) { const uint32_t value = VariableModeField::encode(VariableMode::kConst) | InitFlagField::encode(kCreatedInitialized) | MaybeAssignedFlagField::encode(kNotAssigned) | RequiresBrandCheckField::encode(kNoBrandCheck) | ParameterNumberField::encode(ParameterNumberField::kMax); 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; if (!is_empty_function) { scope_info->set(index++, Smi::FromInt(receiver_index)); } DCHECK_EQ(index, scope_info->FunctionNameInfoIndex()); if (is_empty_function) { scope_info->set(index++, *isolate->factory()->empty_string()); scope_info->set(index++, Smi::kZero); } DCHECK_EQ(index, scope_info->InferredFunctionNameIndex()); if (has_inferred_function_name) { scope_info->set(index++, *isolate->factory()->empty_string()); } DCHECK_EQ(index, scope_info->PositionInfoIndex()); // Store dummy position to be in sync with the {scope_type}. scope_info->set(index++, Smi::kZero); scope_info->set(index++, Smi::kZero); DCHECK_EQ(index, scope_info->OuterScopeInfoIndex()); DCHECK_EQ(index, scope_info->length()); DCHECK_EQ(scope_info->ParameterCount(), parameter_count); if (type == FUNCTION_SCOPE) { DCHECK_EQ(scope_info->ContextLength(), 0); } else { DCHECK_EQ(scope_info->ContextLength(), Context::MIN_CONTEXT_SLOTS + 1); } return scope_info; } ScopeInfo ScopeInfo::Empty(Isolate* isolate) { return ReadOnlyRoots(isolate).empty_scope_info(); } ScopeType ScopeInfo::scope_type() const { DCHECK_LT(0, length()); return ScopeTypeField::decode(Flags()); } bool ScopeInfo::CallsSloppyEval() const { bool calls_sloppy_eval = length() > 0 && CallsSloppyEvalField::decode(Flags()); DCHECK_IMPLIES(calls_sloppy_eval, is_sloppy(language_mode())); DCHECK_IMPLIES(calls_sloppy_eval, is_declaration_scope()); return calls_sloppy_eval; } LanguageMode ScopeInfo::language_mode() const { return length() > 0 ? LanguageModeField::decode(Flags()) : LanguageMode::kSloppy; } bool ScopeInfo::is_declaration_scope() const { return DeclarationScopeField::decode(Flags()); } int ScopeInfo::ContextLength() const { if (length() > 0) { int context_locals = ContextLocalCount(); bool function_name_context_slot = FunctionVariableField::decode(Flags()) == CONTEXT; bool force_context = ForceContextAllocationField::decode(Flags()); bool has_context = context_locals > 0 || force_context || function_name_context_slot || scope_type() == WITH_SCOPE || scope_type() == CLASS_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() const { if (length() == 0) return false; return NONE != ReceiverVariableField::decode(Flags()); } bool ScopeInfo::HasAllocatedReceiver() const { if (length() == 0) return false; VariableAllocationInfo allocation = ReceiverVariableField::decode(Flags()); return allocation == STACK || allocation == CONTEXT; } bool ScopeInfo::HasClassBrand() const { return HasClassBrandField::decode(Flags()); } bool ScopeInfo::HasNewTarget() const { return HasNewTargetField::decode(Flags()); } bool ScopeInfo::HasFunctionName() const { if (length() == 0) return false; return NONE != FunctionVariableField::decode(Flags()); } bool ScopeInfo::HasInferredFunctionName() const { if (length() == 0) return false; return HasInferredFunctionNameField::decode(Flags()); } bool ScopeInfo::HasPositionInfo() const { if (length() == 0) return false; return NeedsPositionInfo(scope_type()); } // static bool ScopeInfo::NeedsPositionInfo(ScopeType type) { return type == FUNCTION_SCOPE || type == SCRIPT_SCOPE || type == EVAL_SCOPE || type == MODULE_SCOPE; } bool ScopeInfo::HasSharedFunctionName() const { return FunctionName() != SharedFunctionInfo::kNoSharedNameSentinel; } void ScopeInfo::SetFunctionName(Object name) { DCHECK(HasFunctionName()); DCHECK(name.IsString() || name == SharedFunctionInfo::kNoSharedNameSentinel); set(FunctionNameInfoIndex(), name); } void ScopeInfo::SetInferredFunctionName(String name) { DCHECK(HasInferredFunctionName()); set(InferredFunctionNameIndex(), name); } bool ScopeInfo::HasOuterScopeInfo() const { if (length() == 0) return false; return HasOuterScopeInfoField::decode(Flags()); } bool ScopeInfo::IsDebugEvaluateScope() const { if (length() == 0) return false; return IsDebugEvaluateScopeField::decode(Flags()); } void ScopeInfo::SetIsDebugEvaluateScope() { if (length() > 0) { DCHECK_EQ(scope_type(), WITH_SCOPE); SetFlags(Flags() | IsDebugEvaluateScopeField::encode(true)); } else { UNREACHABLE(); } } bool ScopeInfo::HasContext() const { return ContextLength() > 0; } Object ScopeInfo::FunctionName() const { DCHECK(HasFunctionName()); return get(FunctionNameInfoIndex()); } Object ScopeInfo::InferredFunctionName() const { DCHECK(HasInferredFunctionName()); return get(InferredFunctionNameIndex()); } String ScopeInfo::FunctionDebugName() const { Object name = FunctionName(); if (name.IsString() && String::cast(name).length() > 0) { return String::cast(name); } if (HasInferredFunctionName()) { name = InferredFunctionName(); if (name.IsString()) return String::cast(name); } return GetReadOnlyRoots().empty_string(); } int ScopeInfo::StartPosition() const { DCHECK(HasPositionInfo()); return Smi::ToInt(get(PositionInfoIndex())); } int ScopeInfo::EndPosition() const { DCHECK(HasPositionInfo()); return Smi::ToInt(get(PositionInfoIndex() + 1)); } void ScopeInfo::SetPositionInfo(int start, int end) { DCHECK(HasPositionInfo()); DCHECK_LE(start, end); set(PositionInfoIndex(), Smi::FromInt(start)); set(PositionInfoIndex() + 1, Smi::FromInt(end)); } ScopeInfo ScopeInfo::OuterScopeInfo() const { DCHECK(HasOuterScopeInfo()); return ScopeInfo::cast(get(OuterScopeInfoIndex())); } SourceTextModuleInfo ScopeInfo::ModuleDescriptorInfo() const { DCHECK(scope_type() == MODULE_SCOPE); return SourceTextModuleInfo::cast(get(ModuleInfoIndex())); } String ScopeInfo::ContextLocalName(int var) const { DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); int info_index = ContextLocalNamesIndex() + var; return String::cast(get(info_index)); } VariableMode ScopeInfo::ContextLocalMode(int var) const { DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); int info_index = ContextLocalInfosIndex() + var; int value = Smi::ToInt(get(info_index)); return VariableModeField::decode(value); } InitializationFlag ScopeInfo::ContextLocalInitFlag(int var) const { DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); int info_index = ContextLocalInfosIndex() + var; int value = Smi::ToInt(get(info_index)); return InitFlagField::decode(value); } bool ScopeInfo::ContextLocalIsParameter(int var) const { DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); int info_index = ContextLocalInfosIndex() + var; int value = Smi::ToInt(get(info_index)); return ParameterNumberField::decode(value) != ParameterNumberField::kMax; } uint32_t ScopeInfo::ContextLocalParameterNumber(int var) const { DCHECK(ContextLocalIsParameter(var)); int info_index = ContextLocalInfosIndex() + var; int value = Smi::ToInt(get(info_index)); return ParameterNumberField::decode(value); } MaybeAssignedFlag ScopeInfo::ContextLocalMaybeAssignedFlag(int var) const { DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); int info_index = ContextLocalInfosIndex() + var; int value = Smi::ToInt(get(info_index)); return MaybeAssignedFlagField::decode(value); } RequiresBrandCheckFlag ScopeInfo::RequiresBrandCheck(int var) const { DCHECK_LE(0, var); DCHECK_LT(var, ContextLocalCount()); int info_index = ContextLocalInfosIndex() + var; int value = Smi::ToInt(get(info_index)); return RequiresBrandCheckField::decode(value); } // static 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.GetReadOnlyRoots().this_string()); } int ScopeInfo::ModuleIndex(String name, VariableMode* mode, InitializationFlag* init_flag, MaybeAssignedFlag* maybe_assigned_flag) { DisallowHeapAllocation no_gc; DCHECK(name.IsInternalizedString()); DCHECK_EQ(scope_type(), MODULE_SCOPE); DCHECK_NOT_NULL(mode); DCHECK_NOT_NULL(init_flag); DCHECK_NOT_NULL(maybe_assigned_flag); int module_vars_count = Smi::ToInt(get(ModuleVariableCountIndex())); int entry = ModuleVariablesIndex(); for (int i = 0; i < module_vars_count; ++i) { String var_name = String::cast(get(entry + kModuleVariableNameOffset)); if (name.Equals(var_name)) { int index; ModuleVariable(i, nullptr, &index, mode, init_flag, maybe_assigned_flag); return index; } entry += kModuleVariableEntryLength; } return 0; } // static int ScopeInfo::ContextSlotIndex(ScopeInfo scope_info, String name, VariableMode* mode, InitializationFlag* init_flag, MaybeAssignedFlag* maybe_assigned_flag, RequiresBrandCheckFlag* requires_brand_check) { DisallowHeapAllocation no_gc; DCHECK(name.IsInternalizedString()); DCHECK_NOT_NULL(mode); DCHECK_NOT_NULL(init_flag); DCHECK_NOT_NULL(maybe_assigned_flag); if (scope_info.length() == 0) return -1; int start = scope_info.ContextLocalNamesIndex(); int end = start + scope_info.ContextLocalCount(); for (int i = start; i < end; ++i) { if (name != scope_info.get(i)) continue; int var = i - start; *mode = scope_info.ContextLocalMode(var); *init_flag = scope_info.ContextLocalInitFlag(var); *maybe_assigned_flag = scope_info.ContextLocalMaybeAssignedFlag(var); *requires_brand_check = scope_info.RequiresBrandCheck(var); int result = Context::MIN_CONTEXT_SLOTS + var; DCHECK_LT(result, scope_info.ContextLength()); return result; } return -1; } int ScopeInfo::ReceiverContextSlotIndex() const { if (length() > 0 && ReceiverVariableField::decode(Flags()) == CONTEXT) { return Smi::ToInt(get(ReceiverInfoIndex())); } return -1; } int ScopeInfo::FunctionContextSlotIndex(String name) const { DCHECK(name.IsInternalizedString()); if (length() > 0) { if (FunctionVariableField::decode(Flags()) == CONTEXT && FunctionName() == name) { return Smi::ToInt(get(FunctionNameInfoIndex() + 1)); } } return -1; } FunctionKind ScopeInfo::function_kind() const { return FunctionKindField::decode(Flags()); } int ScopeInfo::ContextLocalNamesIndex() const { DCHECK_LT(0, length()); return kVariablePartIndex; } int ScopeInfo::ContextLocalInfosIndex() const { return ContextLocalNamesIndex() + ContextLocalCount(); } int ScopeInfo::ReceiverInfoIndex() const { return ContextLocalInfosIndex() + ContextLocalCount(); } int ScopeInfo::FunctionNameInfoIndex() const { return ReceiverInfoIndex() + (HasAllocatedReceiver() ? 1 : 0); } int ScopeInfo::InferredFunctionNameIndex() const { return FunctionNameInfoIndex() + (HasFunctionName() ? kFunctionNameEntries : 0); } int ScopeInfo::PositionInfoIndex() const { return InferredFunctionNameIndex() + (HasInferredFunctionName() ? 1 : 0); } int ScopeInfo::OuterScopeInfoIndex() const { return PositionInfoIndex() + (HasPositionInfo() ? kPositionInfoEntries : 0); } int ScopeInfo::ModuleInfoIndex() const { return OuterScopeInfoIndex() + (HasOuterScopeInfo() ? 1 : 0); } int ScopeInfo::ModuleVariableCountIndex() const { return ModuleInfoIndex() + 1; } int ScopeInfo::ModuleVariablesIndex() const { 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::ToInt(get(ModuleVariableCountIndex()))); int entry = ModuleVariablesIndex() + i * kModuleVariableEntryLength; int properties = Smi::ToInt(get(entry + kModuleVariablePropertiesOffset)); if (name != nullptr) { *name = String::cast(get(entry + kModuleVariableNameOffset)); } if (index != nullptr) { *index = Smi::ToInt(get(entry + kModuleVariableIndexOffset)); 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); } } std::ostream& operator<<(std::ostream& os, ScopeInfo::VariableAllocationInfo var_info) { switch (var_info) { case ScopeInfo::VariableAllocationInfo::NONE: return os << "NONE"; case ScopeInfo::VariableAllocationInfo::STACK: return os << "STACK"; case ScopeInfo::VariableAllocationInfo::CONTEXT: return os << "CONTEXT"; case ScopeInfo::VariableAllocationInfo::UNUSED: return os << "UNUSED"; } UNREACHABLE(); return os; } Handle SourceTextModuleInfoEntry::New( Isolate* isolate, Handle export_name, Handle local_name, Handle import_name, int module_request, int cell_index, int beg_pos, int end_pos) { Handle result = Handle::cast(isolate->factory()->NewStruct( SOURCE_TEXT_MODULE_INFO_ENTRY_TYPE, AllocationType::kOld)); 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 SourceTextModuleInfo::New( Isolate* isolate, Zone* zone, SourceTextModuleDescriptor* descr) { // Serialize module requests. int size = static_cast(descr->module_requests().size()); Handle module_requests = isolate->factory()->NewFixedArray(size); Handle module_request_positions = isolate->factory()->NewFixedArray(size); for (const auto& elem : descr->module_requests()) { module_requests->set(elem.second.index, *elem.first->string()); module_request_positions->set(elem.second.index, Smi::FromInt(elem.second.position)); } // Serialize special exports. Handle special_exports = isolate->factory()->NewFixedArray( static_cast(descr->special_exports().size())); { int i = 0; for (auto entry : descr->special_exports()) { Handle serialized_entry = entry->Serialize(isolate); special_exports->set(i++, *serialized_entry); } } // Serialize namespace imports. Handle namespace_imports = isolate->factory()->NewFixedArray( static_cast(descr->namespace_imports().size())); { int i = 0; for (auto entry : descr->namespace_imports()) { Handle serialized_entry = entry->Serialize(isolate); namespace_imports->set(i++, *serialized_entry); } } // Serialize regular exports. Handle regular_exports = descr->SerializeRegularExports(isolate, zone); // Serialize regular imports. Handle regular_imports = isolate->factory()->NewFixedArray( static_cast(descr->regular_imports().size())); { int i = 0; for (const auto& elem : descr->regular_imports()) { Handle serialized_entry = elem.second->Serialize(isolate); regular_imports->set(i++, *serialized_entry); } } Handle result = isolate->factory()->NewSourceTextModuleInfo(); 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); result->set(kModuleRequestPositionsIndex, *module_request_positions); return result; } int SourceTextModuleInfo::RegularExportCount() const { DCHECK_EQ(regular_exports().length() % kRegularExportLength, 0); return regular_exports().length() / kRegularExportLength; } String SourceTextModuleInfo::RegularExportLocalName(int i) const { return String::cast(regular_exports().get(i * kRegularExportLength + kRegularExportLocalNameOffset)); } int SourceTextModuleInfo::RegularExportCellIndex(int i) const { return Smi::ToInt(regular_exports().get(i * kRegularExportLength + kRegularExportCellIndexOffset)); } FixedArray SourceTextModuleInfo::RegularExportExportNames(int i) const { return FixedArray::cast(regular_exports().get( i * kRegularExportLength + kRegularExportExportNamesOffset)); } } // namespace internal } // namespace v8