blob: 72300727a94c0d7349b952d3e5075ea01f1754ff [file] [log] [blame]
// 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(Tagged<ScopeInfo> other,
bool is_live_edit_compare) const {
if (length() != other->length()) return false;
for (int index = 0; index < length(); ++index) {
if (is_live_edit_compare && HasPositionInfo() &&
index >= PositionInfoIndex() && index <= PositionInfoIndex() + 1) {
continue;
}
Tagged<Object> entry = get(index);
Tagged<Object> other_entry = other->get(index);
if (IsSmi(entry)) {
if (entry != other_entry) return false;
} else {
if (HeapObject::cast(entry)->map()->instance_type() !=
HeapObject::cast(other_entry)->map()->instance_type()) {
return false;
}
if (IsString(entry)) {
if (!String::cast(entry)->Equals(String::cast(other_entry))) {
return false;
}
} else if (IsScopeInfo(entry)) {
if (!is_live_edit_compare && !ScopeInfo::cast(entry)->Equals(
ScopeInfo::cast(other_entry), false)) {
return false;
}
} else if (IsSourceTextModuleInfo(entry)) {
if (!is_live_edit_compare &&
!SourceTextModuleInfo::cast(entry)->Equals(
SourceTextModuleInfo::cast(other_entry))) {
return false;
}
} else if (IsOddball(entry)) {
if (Oddball::cast(entry)->kind() !=
Oddball::cast(other_entry)->kind()) {
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;
Tagged<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);
if (scope->is_module_scope()) {
scope_info->set_module_variable_count(module_vars_count);
++index;
}
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->ModuleVariablesIndex();
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;
Tagged<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->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::CreateForShadowRealmNativeContext(
Isolate* isolate) {
return CreateForBootstrapping(isolate, BootstrappingType::kShadowRealm);
}
// 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) ||
(type == BootstrappingType::kShadowRealm);
const bool is_script = type == BootstrappingType::kScript;
const bool is_shadow_realm = type == BootstrappingType::kShadowRealm;
const bool has_const_tracking_let_side_data = is_script;
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);
DisallowGarbageCollection _nogc;
// Encode the flags.
DCHECK_IMPLIES(is_shadow_realm || is_script, !is_empty_function);
int flags =
ScopeTypeBits::encode(
is_empty_function
? FUNCTION_SCOPE
: (is_shadow_realm ? SHADOW_REALM_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 ||
has_const_tracking_let_side_data) |
IsReplModeScopeBit::encode(false) | HasLocalsBlockListBit::encode(false);
Tagged<ScopeInfo> raw_scope_info = *scope_info;
raw_scope_info->set_flags(flags);
raw_scope_info->set_parameter_count(parameter_count);
raw_scope_info->set_context_local_count(context_local_count);
int index = kVariablePartIndex;
// Here we add info for context-allocated "this".
DCHECK_EQ(index, raw_scope_info->ContextLocalNamesIndex());
ReadOnlyRoots roots(isolate);
if (context_local_count) {
raw_scope_info->set(index++, roots.this_string());
}
DCHECK_EQ(index, raw_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);
raw_scope_info->set(index++, Smi::FromInt(value));
}
DCHECK_EQ(index, raw_scope_info->FunctionVariableInfoIndex());
if (is_empty_function) {
raw_scope_info->set(index++, roots.empty_string());
raw_scope_info->set(index++, Smi::zero());
}
DCHECK_EQ(index, raw_scope_info->InferredFunctionNameIndex());
if (has_inferred_function_name) {
raw_scope_info->set(index++, roots.empty_string());
}
DCHECK_EQ(index, raw_scope_info->PositionInfoIndex());
// Store dummy position to be in sync with the {scope_type}.
raw_scope_info->set(index++, Smi::zero());
raw_scope_info->set(index++, Smi::zero());
DCHECK_EQ(index, raw_scope_info->OuterScopeInfoIndex());
DCHECK_EQ(index, raw_scope_info->length());
DCHECK_EQ(raw_scope_info->ParameterCount(), parameter_count);
if (is_empty_function || is_native_context) {
DCHECK_EQ(raw_scope_info->ContextLength(), 0);
} else {
DCHECK_EQ(raw_scope_info->ContextLength(),
raw_scope_info->ContextHeaderLength() + 1);
}
return scope_info;
}
Tagged<Object> ScopeInfo::get(int index) const {
PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
return get(cage_base, index);
}
Tagged<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, Tagged<Smi> value) {
DCHECK_LT(static_cast<unsigned>(index), static_cast<unsigned>(length()));
DCHECK(IsSmi(Tagged<Object>(value)));
int offset = OffsetOfElementAt(index);
RELAXED_WRITE_FIELD(*this, offset, value);
}
void ScopeInfo::set(int index, Tagged<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,
Tagged<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;
}
Tagged<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(!this->IsEmpty());
return ScopeTypeBits::decode(Flags());
}
bool ScopeInfo::is_script_scope() const {
return !this->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 (this->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 (this->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(Tagged<Object> name) {
DCHECK(HasFunctionName());
DCHECK(IsString(name) || name == SharedFunctionInfo::kNoSharedNameSentinel);
DCHECK_IMPLIES(HasContextAllocatedFunctionName(), IsInternalizedString(name));
set_function_variable_info_name(name);
}
void ScopeInfo::SetInferredFunctionName(Tagged<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(!this->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());
}
Tagged<StringSet> ScopeInfo::LocalsBlockList() const {
DCHECK(HasLocalsBlockList());
return StringSet::cast(locals_block_list());
}
bool ScopeInfo::HasContext() const { return ContextLength() > 0; }
Tagged<Object> ScopeInfo::FunctionName() const {
DCHECK(HasFunctionName());
return function_variable_info_name();
}
Tagged<Object> ScopeInfo::InferredFunctionName() const {
DCHECK(HasInferredFunctionName());
return inferred_function_name();
}
Tagged<String> ScopeInfo::FunctionDebugName() const {
if (!HasFunctionName()) return GetReadOnlyRoots().empty_string();
Tagged<Object> name = FunctionName();
if (IsString(name) && String::cast(name)->length() > 0) {
return String::cast(name);
}
if (HasInferredFunctionName()) {
name = InferredFunctionName();
if (IsString(name)) 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);
}
Tagged<ScopeInfo> ScopeInfo::OuterScopeInfo() const {
DCHECK(HasOuterScopeInfo());
return ScopeInfo::cast(outer_scope_info());
}
Tagged<SourceTextModuleInfo> ScopeInfo::ModuleDescriptorInfo() const {
DCHECK(scope_type() == MODULE_SCOPE);
return SourceTextModuleInfo::cast(module_info());
}
Tagged<String> ScopeInfo::ContextInlinedLocalName(int var) const {
DCHECK(HasInlinedLocalNames());
return context_local_names(var);
}
Tagged<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(Tagged<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(Tagged<String> name, VariableMode* mode,
InitializationFlag* init_flag,
MaybeAssignedFlag* maybe_assigned_flag) {
DisallowGarbageCollection no_gc;
DCHECK(IsInternalizedString(name));
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) {
Tagged<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(Tagged<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(IsInternalizedString(*name));
DCHECK_NOT_NULL(lookup_result);
if (this->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<Tagged<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());
Tagged<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());
Tagged<NameToIndexHashTable> table = context_local_names_hashtable();
Tagged<Object> name = table->KeyAt(entry);
DCHECK(IsString(name));
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(Tagged<String> name) const {
DCHECK(IsInternalizedString(name));
if (HasContextAllocatedFunctionName()) {
DCHECK_IMPLIES(HasFunctionName(), IsInternalizedString(FunctionName()));
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(ContextLocalNamesOffset());
}
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(kModuleVariableCountOffset);
}
int ScopeInfo::ModuleVariablesIndex() const {
return ConvertOffsetToIndex(ModuleVariablesOffset());
}
void ScopeInfo::ModuleVariable(int i, Tagged<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);
}
}
uint32_t ScopeInfo::Hash() {
// Hash ScopeInfo based on its start and end position.
// Note: Ideally we'd also have the script ID. But since we only use the
// hash in a debug-evaluate cache, we don't worry too much about collisions.
if (HasPositionInfo()) {
return static_cast<uint32_t>(
base::hash_combine(flags(), StartPosition(), EndPosition()));
}
return static_cast<uint32_t>(
base::hash_combine(flags(), context_local_count()));
}
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_attributes,
int position) {
Handle<ModuleRequest> result = Handle<ModuleRequest>::cast(
isolate->factory()->NewStruct(MODULE_REQUEST_TYPE, AllocationType::kOld));
DisallowGarbageCollection no_gc;
Tagged<ModuleRequest> raw = *result;
raw->set_specifier(*specifier);
raw->set_import_attributes(*import_attributes);
raw->set_position(position);
return result;
}
template Handle<ModuleRequest> ModuleRequest::New(
Isolate* isolate, Handle<String> specifier,
Handle<FixedArray> import_attributes, int position);
template Handle<ModuleRequest> ModuleRequest::New(
LocalIsolate* isolate, Handle<String> specifier,
Handle<FixedArray> import_attributes, 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));
DisallowGarbageCollection no_gc;
Tagged<SourceTextModuleInfoEntry> raw = *result;
raw->set_export_name(*export_name);
raw->set_local_name(*local_name);
raw->set_import_name(*import_name);
raw->set_module_request(module_request);
raw->set_cell_index(cell_index);
raw->set_beg_pos(beg_pos);
raw->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;
}
Tagged<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));
}
Tagged<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"