// 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 "src/objects/scope-info.h" #include <stdlib.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" #include "src/objects/scope-info-inl.h" #include "src/objects/string-set-inl.h" #include "src/roots/roots.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" namespace v8 { namespace internal { #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 template <typename IsolateT> Handle<ScopeInfo> ScopeInfo::Create(IsolateT* isolate, Zone* zone, Scope* scope, MaybeHandle<ScopeInfo> 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: case VariableLocation::REPL_GLOBAL: 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 = VariableAllocationInfo::UNUSED; } else if (var->IsContextSlot()) { receiver_info = VariableAllocationInfo::CONTEXT; } else { DCHECK(var->IsParameter()); receiver_info = VariableAllocationInfo::STACK; } } else { receiver_info = VariableAllocationInfo::NONE; } DCHECK(module_vars_count == 0 || scope->is_module_scope()); // Make sure we allocate the correct amount. DCHECK_EQ(scope->ContextLocalCount(), context_local_count); // If the number of locals is small, we inline directly // in the scope info object. bool has_inlined_local_names = context_local_count < kScopeInfoMaxInlinedLocalNamesSize; 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 = VariableAllocationInfo::UNUSED; } else if (var->IsContextSlot()) { function_name_info = VariableAllocationInfo::CONTEXT; } else { DCHECK(var->IsStackLocal()); function_name_info = VariableAllocationInfo::STACK; } } else { // Always reserve space for the debug name in the scope info. function_name_info = VariableAllocationInfo::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 = VariableAllocationInfo::UNUSED; } else { function_name_info = VariableAllocationInfo::NONE; } const bool has_brand = scope->is_class_scope() ? scope->AsClassScope()->brand() != nullptr : scope->IsConstructorScope() && scope->AsDeclarationScope()->class_scope_has_private_brand(); const bool should_save_class_variable_index = scope->is_class_scope() ? scope->AsClassScope()->should_save_class_variable_index() : false; const bool has_function_name = function_name_info != VariableAllocationInfo::NONE; const bool has_position_info = NeedsPositionInfo(scope->scope_type()); const int parameter_count = scope->is_declaration_scope() ? scope->AsDeclarationScope()->num_parameters() : 0; const bool has_outer_scope_info = !outer_scope.is_null(); Handle<SourceTextModuleInfo> module_info; if (scope->is_module_scope()) { module_info = SourceTextModuleInfo::New(isolate, zone, scope->AsModuleScope()->module()); } // Make sure the Fields enum agrees with Torque-generated offsets. #define ASSERT_MATCHED_FIELD(name) \ STATIC_ASSERT(OffsetOfElementAt(k##name) == k##name##Offset); FOR_EACH_SCOPE_INFO_NUMERIC_FIELD(ASSERT_MATCHED_FIELD) #undef ASSERT_MATCHED_FIELD const int local_names_container_size = has_inlined_local_names ? context_local_count : 1; const int length = kVariablePartIndex + local_names_container_size + context_local_count + (should_save_class_variable_index ? 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); // Create hash table if local names are not inlined. Handle<NameToIndexHashTable> local_names_hashtable; if (!has_inlined_local_names) { local_names_hashtable = NameToIndexHashTable::New( isolate, context_local_count, AllocationType::kOld); } Handle<ScopeInfo> scope_info_handle = isolate->factory()->NewScopeInfo(length); int index = kVariablePartIndex; { DisallowGarbageCollection 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 sloppy_eval_can_extend_vars = false; if (scope->is_function_scope()) { DeclarationScope* function_scope = scope->AsDeclarationScope(); has_simple_parameters = function_scope->has_simple_parameters(); #if V8_ENABLE_WEBASSEMBLY is_asm_module = function_scope->is_asm_module(); #endif // V8_ENABLE_WEBASSEMBLY } FunctionKind function_kind = FunctionKind::kNormalFunction; if (scope->is_declaration_scope()) { function_kind = scope->AsDeclarationScope()->function_kind(); sloppy_eval_can_extend_vars = scope->AsDeclarationScope()->sloppy_eval_can_extend_vars(); } // Encode the flags. int flags = ScopeTypeBits::encode(scope->scope_type()) | SloppyEvalCanExtendVarsBit::encode(sloppy_eval_can_extend_vars) | LanguageModeBit::encode(scope->language_mode()) | DeclarationScopeBit::encode(scope->is_declaration_scope()) | ReceiverVariableBits::encode(receiver_info) | ClassScopeHasPrivateBrandBit::encode(has_brand) | HasSavedClassVariableBit::encode(should_save_class_variable_index) | HasNewTargetBit::encode(has_new_target) | FunctionVariableBits::encode(function_name_info) | HasInferredFunctionNameBit::encode(has_inferred_function_name) | IsAsmModuleBit::encode(is_asm_module) | HasSimpleParametersBit::encode(has_simple_parameters) | FunctionKindBits::encode(function_kind) | HasOuterScopeInfoBit::encode(has_outer_scope_info) | IsDebugEvaluateScopeBit::encode(scope->is_debug_evaluate_scope()) | ForceContextAllocationBit::encode( scope->ForceContextForLanguageMode()) | PrivateNameLookupSkipsOuterClassBit::encode( scope->private_name_lookup_skips_outer_class()) | HasContextExtensionSlotBit::encode(scope->HasContextExtensionSlot()) | IsReplModeScopeBit::encode(scope->is_repl_mode_scope()) | HasLocalsBlockListBit::encode(false); scope_info.set_flags(flags); scope_info.set_parameter_count(parameter_count); scope_info.set_context_local_count(context_local_count); // Jump ahead to set the number of module variables so that we can use range // DCHECKs in future steps. if (scope->is_module_scope()) { scope_info.set_module_variable_count(module_vars_count); } if (!has_inlined_local_names) { scope_info.set_context_local_names_hashtable(*local_names_hashtable); } // 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 + local_names_container_size; int module_var_entry = scope_info.ModuleVariableCountIndex() + 1; for (Variable* var : *scope->locals()) { switch (var->location()) { case VariableLocation::CONTEXT: case VariableLocation::REPL_GLOBAL: { // Due to duplicate parameters, context locals aren't guaranteed to // come in order. int local_index = var->index() - scope->ContextHeaderLength(); DCHECK_LE(0, local_index); DCHECK_LT(local_index, context_local_count); uint32_t info = VariableModeBits::encode(var->mode()) | InitFlagBit::encode(var->initialization_flag()) | MaybeAssignedFlagBit::encode(var->maybe_assigned()) | ParameterNumberBits::encode(ParameterNumberBits::kMax) | IsStaticFlagBit::encode(var->is_static_flag()); if (has_inlined_local_names) { scope_info.set(context_local_base + local_index, *var->name(), mode); } else { Handle<NameToIndexHashTable> new_table = NameToIndexHashTable::Add( isolate, local_names_hashtable, var->name(), local_index); DCHECK_EQ(*new_table, *local_names_hashtable); USE(new_table); } scope_info.set(context_local_info_base + local_index, Smi::FromInt(info)); break; } case VariableLocation::MODULE: { scope_info.set(module_var_entry + TorqueGeneratedModuleVariableOffsets::kNameOffset / kTaggedSize, *var->name(), mode); scope_info.set( module_var_entry + TorqueGeneratedModuleVariableOffsets::kIndexOffset / kTaggedSize, Smi::FromInt(var->index())); uint32_t properties = VariableModeBits::encode(var->mode()) | InitFlagBit::encode(var->initialization_flag()) | MaybeAssignedFlagBit::encode(var->maybe_assigned()) | ParameterNumberBits::encode(ParameterNumberBits::kMax) | IsStaticFlagBit::encode(var->is_static_flag()); scope_info.set( module_var_entry + TorqueGeneratedModuleVariableOffsets::kPropertiesOffset / kTaggedSize, 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 param_index = parameter->index() - scope->ContextHeaderLength(); int info_index = context_local_info_base + param_index; int info = Smi::ToInt(scope_info.get(info_index)); info = ParameterNumberBits::update(info, i); scope_info.set(info_index, Smi::FromInt(info)); } } // Advance past local names and local names info. index += local_names_container_size + context_local_count; DCHECK_EQ(index, scope_info.SavedClassVariableInfoIndex()); // If the scope is a class scope and has used static private methods, save // the context slot index of the class variable. // Store the class variable index. if (should_save_class_variable_index) { Variable* class_variable = scope->AsClassScope()->class_variable(); DCHECK_EQ(class_variable->location(), VariableLocation::CONTEXT); int local_index; if (has_inlined_local_names) { local_index = class_variable->index(); } else { Handle<Name> name = class_variable->name(); InternalIndex entry = local_names_hashtable->FindEntry(isolate, name); local_index = entry.as_int(); } scope_info.set(index++, Smi::FromInt(local_index)); } // If present, add the function variable name and its index. DCHECK_EQ(index, scope_info.FunctionVariableInfoIndex()); if (has_function_name) { Variable* var = scope->AsDeclarationScope()->function_var(); int var_index = -1; Object name = Smi::zero(); 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 != VariableAllocationInfo::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()) { DCHECK_EQ(index, scope_info.ModuleInfoIndex()); scope_info.set(index++, *module_info); DCHECK_EQ(index, scope_info.ModuleVariableCountIndex()); // Module variable count was already written above. index++; 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_handle->length()); DCHECK_EQ(parameter_count, scope_info_handle->ParameterCount()); DCHECK_EQ(scope->num_heap_slots(), scope_info_handle->ContextLength()); return scope_info_handle; } template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope, MaybeHandle<ScopeInfo> outer_scope); template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle<ScopeInfo> ScopeInfo::Create(LocalIsolate* isolate, Zone* zone, Scope* scope, MaybeHandle<ScopeInfo> outer_scope); // static Handle<ScopeInfo> ScopeInfo::CreateForWithScope( Isolate* isolate, MaybeHandle<ScopeInfo> 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<ScopeInfo> scope_info = factory->NewScopeInfo(length); // Encode the flags. int flags = ScopeTypeBits::encode(WITH_SCOPE) | SloppyEvalCanExtendVarsBit::encode(false) | LanguageModeBit::encode(LanguageMode::kSloppy) | DeclarationScopeBit::encode(false) | ReceiverVariableBits::encode(VariableAllocationInfo::NONE) | ClassScopeHasPrivateBrandBit::encode(false) | HasSavedClassVariableBit::encode(false) | HasNewTargetBit::encode(false) | FunctionVariableBits::encode(VariableAllocationInfo::NONE) | IsAsmModuleBit::encode(false) | HasSimpleParametersBit::encode(true) | FunctionKindBits::encode(FunctionKind::kNormalFunction) | HasOuterScopeInfoBit::encode(has_outer_scope_info) | IsDebugEvaluateScopeBit::encode(false) | ForceContextAllocationBit::encode(false) | PrivateNameLookupSkipsOuterClassBit::encode(false) | HasContextExtensionSlotBit::encode(true) | IsReplModeScopeBit::encode(false) | HasLocalsBlockListBit::encode(false); scope_info->set_flags(flags); scope_info->set_parameter_count(0); scope_info->set_context_local_count(0); int index = kVariablePartIndex; DCHECK_EQ(index, scope_info->FunctionVariableInfoIndex()); 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(scope_info->ContextHeaderLength(), scope_info->ContextLength()); return scope_info; } // static Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) { return CreateForBootstrapping(isolate, BootstrappingType::kScript); } // static Handle<ScopeInfo> ScopeInfo::CreateForEmptyFunction(Isolate* isolate) { return CreateForBootstrapping(isolate, BootstrappingType::kFunction); } // static Handle<ScopeInfo> ScopeInfo::CreateForNativeContext(Isolate* isolate) { return CreateForBootstrapping(isolate, BootstrappingType::kNative); } // static Handle<ScopeInfo> ScopeInfo::CreateForBootstrapping(Isolate* isolate, BootstrappingType type) { const int parameter_count = 0; const bool is_empty_function = type == BootstrappingType::kFunction; const bool is_native_context = type == BootstrappingType::kNative; const bool is_script = type == BootstrappingType::kScript; const int context_local_count = is_empty_function || is_native_context ? 0 : 1; const bool has_inferred_function_name = is_empty_function; const bool has_position_info = true; // NOTE: Local names are always inlined here, since context_local_count < 2. DCHECK_LT(context_local_count, kScopeInfoMaxInlinedLocalNamesSize); const int length = kVariablePartIndex + 2 * context_local_count + (is_empty_function ? kFunctionNameEntries : 0) + (has_inferred_function_name ? 1 : 0) + (has_position_info ? kPositionInfoEntries : 0); Factory* factory = isolate->factory(); Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length, AllocationType::kReadOnly); // Encode the flags. int flags = ScopeTypeBits::encode(is_empty_function ? FUNCTION_SCOPE : SCRIPT_SCOPE) | SloppyEvalCanExtendVarsBit::encode(false) | LanguageModeBit::encode(LanguageMode::kSloppy) | DeclarationScopeBit::encode(true) | ReceiverVariableBits::encode(is_script ? VariableAllocationInfo::CONTEXT : VariableAllocationInfo::UNUSED) | ClassScopeHasPrivateBrandBit::encode(false) | HasSavedClassVariableBit::encode(false) | HasNewTargetBit::encode(false) | FunctionVariableBits::encode(is_empty_function ? VariableAllocationInfo::UNUSED : VariableAllocationInfo::NONE) | HasInferredFunctionNameBit::encode(has_inferred_function_name) | IsAsmModuleBit::encode(false) | HasSimpleParametersBit::encode(true) | FunctionKindBits::encode(FunctionKind::kNormalFunction) | HasOuterScopeInfoBit::encode(false) | IsDebugEvaluateScopeBit::encode(false) | ForceContextAllocationBit::encode(false) | PrivateNameLookupSkipsOuterClassBit::encode(false) | HasContextExtensionSlotBit::encode(is_native_context) | IsReplModeScopeBit::encode(false) | HasLocalsBlockListBit::encode(false); scope_info->set_flags(flags); scope_info->set_parameter_count(parameter_count); scope_info->set_context_local_count(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 > 0) { const uint32_t value = VariableModeBits::encode(VariableMode::kConst) | InitFlagBit::encode(kCreatedInitialized) | MaybeAssignedFlagBit::encode(kNotAssigned) | ParameterNumberBits::encode(ParameterNumberBits::kMax) | IsStaticFlagBit::encode(IsStaticFlag::kNotStatic); scope_info->set(index++, Smi::FromInt(value)); } DCHECK_EQ(index, scope_info->FunctionVariableInfoIndex()); if (is_empty_function) { scope_info->set(index++, *isolate->factory()->empty_string()); scope_info->set(index++, Smi::zero()); } 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::zero()); scope_info->set(index++, Smi::zero()); DCHECK_EQ(index, scope_info->OuterScopeInfoIndex()); DCHECK_EQ(index, scope_info->length()); DCHECK_EQ(scope_info->ParameterCount(), parameter_count); if (is_empty_function || is_native_context) { DCHECK_EQ(scope_info->ContextLength(), 0); } else { DCHECK_EQ(scope_info->ContextLength(), scope_info->ContextHeaderLength() + 1); } return scope_info; } Object ScopeInfo::get(int index) const { PtrComprCageBase cage_base = GetPtrComprCageBase(*this); return get(cage_base, index); } Object ScopeInfo::get(PtrComprCageBase cage_base, int index) const { DCHECK_LT(static_cast<unsigned>(index), static_cast<unsigned>(length())); return TaggedField<Object>::Relaxed_Load(cage_base, *this, OffsetOfElementAt(index)); } void ScopeInfo::set(int index, Smi value) { DCHECK_LT(static_cast<unsigned>(index), static_cast<unsigned>(length())); DCHECK(Object(value).IsSmi()); int offset = OffsetOfElementAt(index); RELAXED_WRITE_FIELD(*this, offset, value); } void ScopeInfo::set(int index, Object value, WriteBarrierMode mode) { DCHECK_LT(static_cast<unsigned>(index), static_cast<unsigned>(length())); int offset = OffsetOfElementAt(index); RELAXED_WRITE_FIELD(*this, offset, value); CONDITIONAL_WRITE_BARRIER(*this, offset, value, mode); } void ScopeInfo::CopyElements(Isolate* isolate, int dst_index, ScopeInfo src, int src_index, int len, WriteBarrierMode mode) { if (len == 0) return; DCHECK_LE(src_index + len, src.length()); DisallowGarbageCollection no_gc; ObjectSlot dst_slot(RawFieldOfElementAt(dst_index)); ObjectSlot src_slot(src.RawFieldOfElementAt(src_index)); isolate->heap()->CopyRange(*this, dst_slot, src_slot, len, mode); } ObjectSlot ScopeInfo::RawFieldOfElementAt(int index) { return RawField(OffsetOfElementAt(index)); } int ScopeInfo::length() const { // AllocatedSize() is generated by Torque and represents the size in bytes of // the object, as computed from flags, context_local_count, and possibly // module_variable_count. Convert that size into a number of slots. return (AllocatedSize() - HeapObject::kHeaderSize) / kTaggedSize; } // static Handle<ScopeInfo> ScopeInfo::RecreateWithBlockList( Isolate* isolate, Handle<ScopeInfo> original, Handle<StringSet> blocklist) { DCHECK(!original.is_null()); if (original->HasLocalsBlockList()) return original; int length = original->length() + 1; Handle<ScopeInfo> scope_info = isolate->factory()->NewScopeInfo(length); // Copy the static part first and update the flags to include the // blocklist field, so {LocalsBlockListIndex} returns the correct value. scope_info->CopyElements(isolate, 0, *original, 0, kVariablePartIndex, WriteBarrierMode::UPDATE_WRITE_BARRIER); scope_info->set_flags( HasLocalsBlockListBit::update(scope_info->Flags(), true)); // Copy the dynamic part including the provided blocklist: // 1) copy all the fields up to the blocklist index // 2) add the blocklist // 3) copy the remaining fields scope_info->CopyElements( isolate, kVariablePartIndex, *original, kVariablePartIndex, scope_info->LocalsBlockListIndex() - kVariablePartIndex, WriteBarrierMode::UPDATE_WRITE_BARRIER); scope_info->set_locals_block_list(*blocklist); scope_info->CopyElements(isolate, scope_info->LocalsBlockListIndex() + 1, *original, scope_info->LocalsBlockListIndex(), length - scope_info->LocalsBlockListIndex() - 1, WriteBarrierMode::UPDATE_WRITE_BARRIER); return scope_info; } ScopeInfo ScopeInfo::Empty(Isolate* isolate) { return ReadOnlyRoots(isolate).empty_scope_info(); } bool ScopeInfo::IsEmpty() const { return IsEmptyBit::decode(Flags()); } ScopeType ScopeInfo::scope_type() const { DCHECK(!IsEmpty()); return ScopeTypeBits::decode(Flags()); } bool ScopeInfo::is_script_scope() const { return !IsEmpty() && scope_type() == SCRIPT_SCOPE; } bool ScopeInfo::SloppyEvalCanExtendVars() const { bool sloppy_eval_can_extend_vars = SloppyEvalCanExtendVarsBit::decode(Flags()); DCHECK_IMPLIES(sloppy_eval_can_extend_vars, is_sloppy(language_mode())); DCHECK_IMPLIES(sloppy_eval_can_extend_vars, is_declaration_scope()); return sloppy_eval_can_extend_vars; } LanguageMode ScopeInfo::language_mode() const { return LanguageModeBit::decode(Flags()); } bool ScopeInfo::is_declaration_scope() const { return DeclarationScopeBit::decode(Flags()); } int ScopeInfo::ContextLength() const { if (IsEmpty()) return 0; int context_locals = ContextLocalCount(); bool function_name_context_slot = HasContextAllocatedFunctionName(); bool force_context = ForceContextAllocationBit::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 && SloppyEvalCanExtendVars() && is_declaration_scope()) || (scope_type() == FUNCTION_SCOPE && SloppyEvalCanExtendVars()) || (scope_type() == FUNCTION_SCOPE && IsAsmModule()) || scope_type() == MODULE_SCOPE; if (!has_context) return 0; return ContextHeaderLength() + context_locals + (function_name_context_slot ? 1 : 0); } bool ScopeInfo::HasContextExtensionSlot() const { return HasContextExtensionSlotBit::decode(Flags()); } int ScopeInfo::ContextHeaderLength() const { return HasContextExtensionSlot() ? Context::MIN_CONTEXT_EXTENDED_SLOTS : Context::MIN_CONTEXT_SLOTS; } bool ScopeInfo::HasReceiver() const { return VariableAllocationInfo::NONE != ReceiverVariableBits::decode(Flags()); } bool ScopeInfo::HasAllocatedReceiver() const { VariableAllocationInfo allocation = ReceiverVariableBits::decode(Flags()); return allocation == VariableAllocationInfo::STACK || allocation == VariableAllocationInfo::CONTEXT; } bool ScopeInfo::ClassScopeHasPrivateBrand() const { return ClassScopeHasPrivateBrandBit::decode(Flags()); } bool ScopeInfo::HasSavedClassVariable() const { return HasSavedClassVariableBit::decode(Flags()); } bool ScopeInfo::HasNewTarget() const { return HasNewTargetBit::decode(Flags()); } bool ScopeInfo::HasFunctionName() const { return VariableAllocationInfo::NONE != FunctionVariableBits::decode(Flags()); } bool ScopeInfo::HasContextAllocatedFunctionName() const { return VariableAllocationInfo::CONTEXT == FunctionVariableBits::decode(Flags()); } bool ScopeInfo::HasInferredFunctionName() const { return HasInferredFunctionNameBit::decode(Flags()); } bool ScopeInfo::HasPositionInfo() const { if (IsEmpty()) 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 || type == CLASS_SCOPE; } bool ScopeInfo::HasSharedFunctionName() const { return FunctionName() != SharedFunctionInfo::kNoSharedNameSentinel; } void ScopeInfo::SetFunctionName(Object name) { DCHECK(HasFunctionName()); DCHECK(name.IsString() || name == SharedFunctionInfo::kNoSharedNameSentinel); DCHECK_IMPLIES(HasContextAllocatedFunctionName(), name.IsInternalizedString()); set_function_variable_info_name(name); } void ScopeInfo::SetInferredFunctionName(String name) { DCHECK(HasInferredFunctionName()); set_inferred_function_name(name); } bool ScopeInfo::HasOuterScopeInfo() const { return HasOuterScopeInfoBit::decode(Flags()); } bool ScopeInfo::IsDebugEvaluateScope() const { return IsDebugEvaluateScopeBit::decode(Flags()); } void ScopeInfo::SetIsDebugEvaluateScope() { CHECK(!IsEmpty()); DCHECK_EQ(scope_type(), WITH_SCOPE); set_flags(Flags() | IsDebugEvaluateScopeBit::encode(true)); } bool ScopeInfo::PrivateNameLookupSkipsOuterClass() const { return PrivateNameLookupSkipsOuterClassBit::decode(Flags()); } bool ScopeInfo::IsReplModeScope() const { return IsReplModeScopeBit::decode(Flags()); } bool ScopeInfo::HasLocalsBlockList() const { return HasLocalsBlockListBit::decode(Flags()); } StringSet ScopeInfo::LocalsBlockList() const { DCHECK(HasLocalsBlockList()); return StringSet::cast(locals_block_list()); } bool ScopeInfo::HasContext() const { return ContextLength() > 0; } Object ScopeInfo::FunctionName() const { DCHECK(HasFunctionName()); return function_variable_info_name(); } Object ScopeInfo::InferredFunctionName() const { DCHECK(HasInferredFunctionName()); return inferred_function_name(); } String ScopeInfo::FunctionDebugName() const { if (!HasFunctionName()) return GetReadOnlyRoots().empty_string(); 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 position_info_start(); } int ScopeInfo::EndPosition() const { DCHECK(HasPositionInfo()); return position_info_end(); } void ScopeInfo::SetPositionInfo(int start, int end) { DCHECK(HasPositionInfo()); DCHECK_LE(start, end); set_position_info_start(start); set_position_info_end(end); } ScopeInfo ScopeInfo::OuterScopeInfo() const { DCHECK(HasOuterScopeInfo()); return ScopeInfo::cast(outer_scope_info()); } SourceTextModuleInfo ScopeInfo::ModuleDescriptorInfo() const { DCHECK(scope_type() == MODULE_SCOPE); return SourceTextModuleInfo::cast(module_info()); } String ScopeInfo::ContextInlinedLocalName(int var) const { DCHECK(HasInlinedLocalNames()); return context_local_names(var); } String ScopeInfo::ContextInlinedLocalName(PtrComprCageBase cage_base, int var) const { DCHECK(HasInlinedLocalNames()); return context_local_names(cage_base, var); } VariableMode ScopeInfo::ContextLocalMode(int var) const { int value = context_local_infos(var); return VariableModeBits::decode(value); } IsStaticFlag ScopeInfo::ContextLocalIsStaticFlag(int var) const { int value = context_local_infos(var); return IsStaticFlagBit::decode(value); } InitializationFlag ScopeInfo::ContextLocalInitFlag(int var) const { int value = context_local_infos(var); return InitFlagBit::decode(value); } bool ScopeInfo::ContextLocalIsParameter(int var) const { int value = context_local_infos(var); return ParameterNumberBits::decode(value) != ParameterNumberBits::kMax; } uint32_t ScopeInfo::ContextLocalParameterNumber(int var) const { DCHECK(ContextLocalIsParameter(var)); int value = context_local_infos(var); return ParameterNumberBits::decode(value); } MaybeAssignedFlag ScopeInfo::ContextLocalMaybeAssignedFlag(int var) const { int value = context_local_infos(var); return MaybeAssignedFlagBit::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.Get(0) == '#' || name.Equals(name.GetReadOnlyRoots().this_string()); } int ScopeInfo::ModuleVariableCount() const { DCHECK_EQ(scope_type(), MODULE_SCOPE); return module_variable_count(); } int ScopeInfo::ModuleIndex(String name, VariableMode* mode, InitializationFlag* init_flag, MaybeAssignedFlag* maybe_assigned_flag) { DisallowGarbageCollection 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 = module_variable_count(); for (int i = 0; i < module_vars_count; ++i) { String var_name = module_variables_name(i); if (name.Equals(var_name)) { int index; ModuleVariable(i, nullptr, &index, mode, init_flag, maybe_assigned_flag); return index; } } return 0; } int ScopeInfo::InlinedLocalNamesLookup(String name) { DisallowGarbageCollection no_gc; PtrComprCageBase cage_base = GetPtrComprCageBase(*this); int local_count = context_local_count(); for (int i = 0; i < local_count; ++i) { if (name == ContextInlinedLocalName(cage_base, i)) { return i; } } return -1; } int ScopeInfo::ContextSlotIndex(Handle<String> name, VariableLookupResult* lookup_result) { DisallowGarbageCollection no_gc; DCHECK(name->IsInternalizedString()); DCHECK_NOT_NULL(lookup_result); if (IsEmpty()) return -1; int index = HasInlinedLocalNames() ? InlinedLocalNamesLookup(*name) : context_local_names_hashtable().Lookup(name); if (index != -1) { lookup_result->mode = ContextLocalMode(index); lookup_result->is_static_flag = ContextLocalIsStaticFlag(index); lookup_result->init_flag = ContextLocalInitFlag(index); lookup_result->maybe_assigned_flag = ContextLocalMaybeAssignedFlag(index); lookup_result->is_repl_mode = IsReplModeScope(); int context_slot = ContextHeaderLength() + index; DCHECK_LT(context_slot, ContextLength()); return context_slot; } return -1; } int ScopeInfo::ContextSlotIndex(Handle<String> name) { VariableLookupResult lookup_result; return ContextSlotIndex(name, &lookup_result); } std::pair<String, int> ScopeInfo::SavedClassVariable() const { DCHECK(HasSavedClassVariableBit::decode(Flags())); if (HasInlinedLocalNames()) { // The saved class variable info corresponds to the context slot index. int index = saved_class_variable_info() - Context::MIN_CONTEXT_SLOTS; DCHECK_GE(index, 0); DCHECK_LT(index, ContextLocalCount()); String name = ContextInlinedLocalName(index); return std::make_pair(name, index); } else { // The saved class variable info corresponds to the offset in the hash // table storage. InternalIndex entry(saved_class_variable_info()); NameToIndexHashTable table = context_local_names_hashtable(); Object name = table.KeyAt(entry); DCHECK(name.IsString()); return std::make_pair(String::cast(name), table.IndexAt(entry)); } } int ScopeInfo::ReceiverContextSlotIndex() const { if (ReceiverVariableBits::decode(Flags()) == VariableAllocationInfo::CONTEXT) { return ContextHeaderLength(); } return -1; } int ScopeInfo::ParametersStartIndex() const { if (ReceiverVariableBits::decode(Flags()) == VariableAllocationInfo::CONTEXT) { return ContextHeaderLength() + 1; } return ContextHeaderLength(); } int ScopeInfo::FunctionContextSlotIndex(String name) const { DCHECK(name.IsInternalizedString()); if (HasContextAllocatedFunctionName()) { DCHECK_IMPLIES(HasFunctionName(), FunctionName().IsInternalizedString()); if (FunctionName() == name) { return function_variable_info_context_or_stack_slot_index(); } } return -1; } FunctionKind ScopeInfo::function_kind() const { return FunctionKindBits::decode(Flags()); } int ScopeInfo::ContextLocalNamesIndex() const { return ConvertOffsetToIndex(kContextLocalNamesOffset); } int ScopeInfo::ContextLocalInfosIndex() const { return ConvertOffsetToIndex(ContextLocalInfosOffset()); } int ScopeInfo::SavedClassVariableInfoIndex() const { return ConvertOffsetToIndex(SavedClassVariableInfoOffset()); } int ScopeInfo::FunctionVariableInfoIndex() const { return ConvertOffsetToIndex(FunctionVariableInfoOffset()); } int ScopeInfo::InferredFunctionNameIndex() const { return ConvertOffsetToIndex(InferredFunctionNameOffset()); } int ScopeInfo::PositionInfoIndex() const { return ConvertOffsetToIndex(PositionInfoOffset()); } int ScopeInfo::OuterScopeInfoIndex() const { return ConvertOffsetToIndex(OuterScopeInfoOffset()); } int ScopeInfo::LocalsBlockListIndex() const { return ConvertOffsetToIndex(LocalsBlockListOffset()); } int ScopeInfo::ModuleInfoIndex() const { return ConvertOffsetToIndex(ModuleInfoOffset()); } int ScopeInfo::ModuleVariableCountIndex() const { return ConvertOffsetToIndex(ModuleVariableCountOffset()); } int ScopeInfo::ModuleVariablesIndex() const { return ConvertOffsetToIndex(ModuleVariablesOffset()); } void ScopeInfo::ModuleVariable(int i, String* name, int* index, VariableMode* mode, InitializationFlag* init_flag, MaybeAssignedFlag* maybe_assigned_flag) { int properties = module_variables_properties(i); if (name != nullptr) { *name = module_variables_name(i); } if (index != nullptr) { *index = module_variables_index(i); DCHECK_NE(*index, 0); } if (mode != nullptr) { *mode = VariableModeBits::decode(properties); } if (init_flag != nullptr) { *init_flag = InitFlagBit::decode(properties); } if (maybe_assigned_flag != nullptr) { *maybe_assigned_flag = MaybeAssignedFlagBit::decode(properties); } } std::ostream& operator<<(std::ostream& os, VariableAllocationInfo var_info) { switch (var_info) { case VariableAllocationInfo::NONE: return os << "NONE"; case VariableAllocationInfo::STACK: return os << "STACK"; case VariableAllocationInfo::CONTEXT: return os << "CONTEXT"; case VariableAllocationInfo::UNUSED: return os << "UNUSED"; } UNREACHABLE(); } template <typename IsolateT> Handle<ModuleRequest> ModuleRequest::New(IsolateT* isolate, Handle<String> specifier, Handle<FixedArray> import_assertions, int position) { Handle<ModuleRequest> result = Handle<ModuleRequest>::cast( isolate->factory()->NewStruct(MODULE_REQUEST_TYPE, AllocationType::kOld)); result->set_specifier(*specifier); result->set_import_assertions(*import_assertions); result->set_position(position); return result; } template Handle<ModuleRequest> ModuleRequest::New( Isolate* isolate, Handle<String> specifier, Handle<FixedArray> import_assertions, int position); template Handle<ModuleRequest> ModuleRequest::New( LocalIsolate* isolate, Handle<String> specifier, Handle<FixedArray> import_assertions, int position); template <typename IsolateT> Handle<SourceTextModuleInfoEntry> SourceTextModuleInfoEntry::New( IsolateT* isolate, Handle<PrimitiveHeapObject> export_name, Handle<PrimitiveHeapObject> local_name, Handle<PrimitiveHeapObject> import_name, int module_request, int cell_index, int beg_pos, int end_pos) { Handle<SourceTextModuleInfoEntry> result = Handle<SourceTextModuleInfoEntry>::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; } template Handle<SourceTextModuleInfoEntry> SourceTextModuleInfoEntry::New( Isolate* isolate, Handle<PrimitiveHeapObject> export_name, Handle<PrimitiveHeapObject> local_name, Handle<PrimitiveHeapObject> import_name, int module_request, int cell_index, int beg_pos, int end_pos); template Handle<SourceTextModuleInfoEntry> SourceTextModuleInfoEntry::New( LocalIsolate* isolate, Handle<PrimitiveHeapObject> export_name, Handle<PrimitiveHeapObject> local_name, Handle<PrimitiveHeapObject> import_name, int module_request, int cell_index, int beg_pos, int end_pos); template <typename IsolateT> Handle<SourceTextModuleInfo> SourceTextModuleInfo::New( IsolateT* isolate, Zone* zone, SourceTextModuleDescriptor* descr) { // Serialize module requests. int size = static_cast<int>(descr->module_requests().size()); Handle<FixedArray> module_requests = isolate->factory()->NewFixedArray(size, AllocationType::kOld); for (const auto& elem : descr->module_requests()) { Handle<ModuleRequest> serialized_module_request = elem->Serialize(isolate); module_requests->set(elem->index(), *serialized_module_request); } // Serialize special exports. Handle<FixedArray> special_exports = isolate->factory()->NewFixedArray( static_cast<int>(descr->special_exports().size()), AllocationType::kOld); { int i = 0; for (auto entry : descr->special_exports()) { Handle<SourceTextModuleInfoEntry> serialized_entry = entry->Serialize(isolate); special_exports->set(i++, *serialized_entry); } } // Serialize namespace imports. Handle<FixedArray> namespace_imports = isolate->factory()->NewFixedArray( static_cast<int>(descr->namespace_imports().size()), AllocationType::kOld); { int i = 0; for (auto entry : descr->namespace_imports()) { Handle<SourceTextModuleInfoEntry> 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()), AllocationType::kOld); { int i = 0; for (const auto& elem : descr->regular_imports()) { Handle<SourceTextModuleInfoEntry> serialized_entry = elem.second->Serialize(isolate); regular_imports->set(i++, *serialized_entry); } } Handle<SourceTextModuleInfo> 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); return result; } template Handle<SourceTextModuleInfo> SourceTextModuleInfo::New( Isolate* isolate, Zone* zone, SourceTextModuleDescriptor* descr); template Handle<SourceTextModuleInfo> SourceTextModuleInfo::New( LocalIsolate* isolate, Zone* zone, SourceTextModuleDescriptor* descr); 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 #include "src/objects/object-macros-undef.h"