| // Copyright 2011 the V8 project authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <stdlib.h> | |
| #include "src/objects/scope-info.h" | |
| #include "src/ast/context-slot-cache.h" | |
| #include "src/ast/scopes.h" | |
| #include "src/ast/variables.h" | |
| #include "src/bootstrapper.h" | |
| #include "src/heap/heap-inl.h" | |
| #include "src/objects-inl.h" | |
| #include "src/objects/module-inl.h" | |
| namespace v8 { | |
| namespace internal { | |
| // An entry in ModuleVariableEntries consists of several slots: | |
| enum ModuleVariableEntryOffset { | |
| kModuleVariableNameOffset, | |
| kModuleVariableIndexOffset, | |
| kModuleVariablePropertiesOffset, | |
| kModuleVariableEntryLength // Sentinel value. | |
| }; | |
| #ifdef DEBUG | |
| bool ScopeInfo::Equals(ScopeInfo* other) const { | |
| if (length() != other->length()) return false; | |
| for (int index = 0; index < length(); ++index) { | |
| Object* entry = get(index); | |
| Object* other_entry = other->get(index); | |
| if (entry->IsSmi()) { | |
| if (entry != other_entry) return false; | |
| } else { | |
| if (HeapObject::cast(entry)->map()->instance_type() != | |
| HeapObject::cast(other_entry)->map()->instance_type()) { | |
| return false; | |
| } | |
| if (entry->IsString()) { | |
| if (!String::cast(entry)->Equals(String::cast(other_entry))) { | |
| return false; | |
| } | |
| } else if (entry->IsScopeInfo()) { | |
| if (!ScopeInfo::cast(entry)->Equals(ScopeInfo::cast(other_entry))) { | |
| return false; | |
| } | |
| } else if (entry->IsModuleInfo()) { | |
| if (!ModuleInfo::cast(entry)->Equals(ModuleInfo::cast(other_entry))) { | |
| return false; | |
| } | |
| } else { | |
| UNREACHABLE(); | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| #endif | |
| // static | |
| Handle<ScopeInfo> ScopeInfo::Create(Isolate* 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: | |
| context_local_count++; | |
| break; | |
| case VariableLocation::MODULE: | |
| module_vars_count++; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| DCHECK(module_vars_count == 0 || scope->is_module_scope()); | |
| // Make sure we allocate the correct amount. | |
| DCHECK_EQ(scope->ContextLocalCount(), context_local_count); | |
| // Determine use and location of the "this" binding if it is present. | |
| VariableAllocationInfo receiver_info; | |
| if (scope->is_declaration_scope() && | |
| scope->AsDeclarationScope()->has_this_declaration()) { | |
| Variable* var = scope->AsDeclarationScope()->receiver(); | |
| if (!var->is_used()) { | |
| receiver_info = UNUSED; | |
| } else if (var->IsContextSlot()) { | |
| receiver_info = CONTEXT; | |
| } else { | |
| DCHECK(var->IsParameter()); | |
| receiver_info = STACK; | |
| } | |
| } else { | |
| receiver_info = NONE; | |
| } | |
| 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_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->num_parameters(); | |
| 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); | |
| Factory* factory = isolate->factory(); | |
| Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length); | |
| bool has_simple_parameters = false; | |
| bool 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(); | |
| asm_module = function_scope->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) | | |
| HasNewTargetField::encode(has_new_target) | | |
| FunctionVariableField::encode(function_name_info) | | |
| HasInferredFunctionNameField::encode(has_inferred_function_name) | | |
| AsmModuleField::encode(asm_module) | | |
| HasSimpleParametersField::encode(has_simple_parameters) | | |
| FunctionKindField::encode(function_kind) | | |
| HasOuterScopeInfoField::encode(has_outer_scope_info) | | |
| IsDebugEvaluateScopeField::encode(scope->is_debug_evaluate_scope()); | |
| scope_info->SetFlags(flags); | |
| scope_info->SetParameterCount(parameter_count); | |
| scope_info->SetContextLocalCount(context_local_count); | |
| int index = kVariablePartIndex; | |
| // 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()) | | |
| MaybeAssignedFlagField::encode(var->maybe_assigned()) | | |
| ParameterNumberField::encode(ParameterNumberField::kMax); | |
| scope_info->set(context_local_base + local_index, *var->name()); | |
| scope_info->set(context_local_info_base + local_index, | |
| Smi::FromInt(info)); | |
| break; | |
| } | |
| case VariableLocation::MODULE: { | |
| scope_info->set(module_var_entry + kModuleVariableNameOffset, | |
| *var->name()); | |
| scope_info->set(module_var_entry + kModuleVariableIndexOffset, | |
| Smi::FromInt(var->index())); | |
| uint32_t properties = | |
| VariableModeField::encode(var->mode()) | | |
| InitFlagField::encode(var->initialization_flag()) | | |
| MaybeAssignedFlagField::encode(var->maybe_assigned()) | | |
| 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)); | |
| } | |
| } | |
| 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) { | |
| DisallowHeapAllocation no_gc; | |
| 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); | |
| 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()); | |
| } | |
| // Module-specific information (only for module scopes). | |
| if (scope->is_module_scope()) { | |
| Handle<ModuleInfo> module_info = | |
| ModuleInfo::New(isolate, zone, scope->AsModuleScope()->module()); | |
| DCHECK_EQ(index, scope_info->ModuleInfoIndex()); | |
| scope_info->set(index++, *module_info); | |
| DCHECK_EQ(index, scope_info->ModuleVariableCountIndex()); | |
| scope_info->set(index++, Smi::FromInt(module_vars_count)); | |
| DCHECK_EQ(index, scope_info->ModuleVariablesIndex()); | |
| // The variable entries themselves have already been written above. | |
| index += kModuleVariableEntryLength * module_vars_count; | |
| } | |
| DCHECK_EQ(index, scope_info->length()); | |
| DCHECK_EQ(scope->num_parameters(), scope_info->ParameterCount()); | |
| DCHECK_EQ(scope->num_heap_slots(), scope_info->ContextLength()); | |
| return scope_info; | |
| } | |
| // 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 = | |
| ScopeTypeField::encode(WITH_SCOPE) | CallsSloppyEvalField::encode(false) | | |
| LanguageModeField::encode(LanguageMode::kSloppy) | | |
| DeclarationScopeField::encode(false) | | |
| ReceiverVariableField::encode(NONE) | HasNewTargetField::encode(false) | | |
| FunctionVariableField::encode(NONE) | AsmModuleField::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> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) { | |
| return CreateForBootstrapping(isolate, SCRIPT_SCOPE); | |
| } | |
| // static | |
| Handle<ScopeInfo> ScopeInfo::CreateForEmptyFunction(Isolate* isolate) { | |
| return CreateForBootstrapping(isolate, FUNCTION_SCOPE); | |
| } | |
| // static | |
| Handle<ScopeInfo> ScopeInfo::CreateForBootstrapping(Isolate* isolate, | |
| ScopeType type) { | |
| DCHECK(isolate->bootstrapper()->IsActive()); | |
| 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<ScopeInfo> 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) | | |
| HasNewTargetField::encode(false) | | |
| FunctionVariableField::encode(is_empty_function ? UNUSED : NONE) | | |
| HasInferredFunctionNameField::encode(has_inferred_function_name) | | |
| AsmModuleField::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) | | |
| 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 has_context = context_locals > 0 || function_name_context_slot || | |
| scope_type() == WITH_SCOPE || | |
| (scope_type() == BLOCK_SCOPE && CallsSloppyEval() && | |
| is_declaration_scope()) || | |
| (scope_type() == FUNCTION_SCOPE && CallsSloppyEval()) || | |
| (scope_type() == FUNCTION_SCOPE && IsAsmModule()) || | |
| scope_type() == MODULE_SCOPE; | |
| if (has_context) { | |
| return Context::MIN_CONTEXT_SLOTS + context_locals + | |
| (function_name_context_slot ? 1 : 0); | |
| } | |
| } | |
| return 0; | |
| } | |
| bool ScopeInfo::HasReceiver() 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::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())); | |
| } | |
| ModuleInfo* ScopeInfo::ModuleDescriptorInfo() const { | |
| DCHECK(scope_type() == MODULE_SCOPE); | |
| return ModuleInfo::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); | |
| } | |
| // 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(Handle<String> name, VariableMode* mode, | |
| InitializationFlag* init_flag, | |
| MaybeAssignedFlag* maybe_assigned_flag) { | |
| 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(Handle<ScopeInfo> scope_info, | |
| Handle<String> name, VariableMode* mode, | |
| InitializationFlag* init_flag, | |
| MaybeAssignedFlag* maybe_assigned_flag) { | |
| DCHECK(name->IsInternalizedString()); | |
| DCHECK_NOT_NULL(mode); | |
| DCHECK_NOT_NULL(init_flag); | |
| DCHECK_NOT_NULL(maybe_assigned_flag); | |
| if (scope_info->length() == 0) return -1; | |
| // Get the Isolate via the heap. | |
| // | |
| // Ideally we'd pass Isolate* through to this function, however this is mostly | |
| // called from the parser, which is otherwise isolate independent. We can't | |
| // assume that all scope infos are never RO space (like we can with JSReceiver | |
| // or Context), but we can assume that *non-empty* scope infos are. | |
| // | |
| // So, we take the least-ugly approach of manually getting the isolate to be | |
| // able to remove GetIsolate from ScopeInfo in the general case, while | |
| // allowing it in this one particular case. | |
| Isolate* isolate = Heap::FromWritableHeapObject(*scope_info)->isolate(); | |
| ContextSlotCache* context_slot_cache = isolate->context_slot_cache(); | |
| int result = context_slot_cache->Lookup(*scope_info, *name, mode, init_flag, | |
| maybe_assigned_flag); | |
| if (result != ContextSlotCache::kNotFound) { | |
| DCHECK_LT(result, scope_info->ContextLength()); | |
| return result; | |
| } | |
| int start = scope_info->ContextLocalNamesIndex(); | |
| int end = start + scope_info->ContextLocalCount(); | |
| for (int i = start; i < end; ++i) { | |
| if (*name == scope_info->get(i)) { | |
| int var = i - start; | |
| *mode = scope_info->ContextLocalMode(var); | |
| *init_flag = scope_info->ContextLocalInitFlag(var); | |
| *maybe_assigned_flag = scope_info->ContextLocalMaybeAssignedFlag(var); | |
| result = Context::MIN_CONTEXT_SLOTS + var; | |
| context_slot_cache->Update(scope_info, name, *mode, *init_flag, | |
| *maybe_assigned_flag, result); | |
| DCHECK_LT(result, scope_info->ContextLength()); | |
| return result; | |
| } | |
| } | |
| // Cache as not found. Mode, init flag and maybe assigned flag don't matter. | |
| context_slot_cache->Update(scope_info, name, VariableMode::kTemporary, | |
| kNeedsInitialization, kNotAssigned, -1); | |
| 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<ModuleInfoEntry> ModuleInfoEntry::New(Isolate* isolate, | |
| Handle<Object> export_name, | |
| Handle<Object> local_name, | |
| Handle<Object> import_name, | |
| int module_request, int cell_index, | |
| int beg_pos, int end_pos) { | |
| Handle<ModuleInfoEntry> result = Handle<ModuleInfoEntry>::cast( | |
| isolate->factory()->NewStruct(MODULE_INFO_ENTRY_TYPE, TENURED)); | |
| result->set_export_name(*export_name); | |
| result->set_local_name(*local_name); | |
| result->set_import_name(*import_name); | |
| result->set_module_request(module_request); | |
| result->set_cell_index(cell_index); | |
| result->set_beg_pos(beg_pos); | |
| result->set_end_pos(end_pos); | |
| return result; | |
| } | |
| Handle<ModuleInfo> ModuleInfo::New(Isolate* isolate, Zone* zone, | |
| ModuleDescriptor* descr) { | |
| // Serialize module requests. | |
| int size = static_cast<int>(descr->module_requests().size()); | |
| Handle<FixedArray> module_requests = isolate->factory()->NewFixedArray(size); | |
| Handle<FixedArray> 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<FixedArray> special_exports = isolate->factory()->NewFixedArray( | |
| static_cast<int>(descr->special_exports().size())); | |
| { | |
| int i = 0; | |
| for (auto entry : descr->special_exports()) { | |
| Handle<ModuleInfoEntry> serialized_entry = entry->Serialize(isolate); | |
| special_exports->set(i++, *serialized_entry); | |
| } | |
| } | |
| // Serialize namespace imports. | |
| Handle<FixedArray> namespace_imports = isolate->factory()->NewFixedArray( | |
| static_cast<int>(descr->namespace_imports().size())); | |
| { | |
| int i = 0; | |
| for (auto entry : descr->namespace_imports()) { | |
| Handle<ModuleInfoEntry> serialized_entry = entry->Serialize(isolate); | |
| namespace_imports->set(i++, *serialized_entry); | |
| } | |
| } | |
| // Serialize regular exports. | |
| Handle<FixedArray> regular_exports = | |
| descr->SerializeRegularExports(isolate, zone); | |
| // Serialize regular imports. | |
| Handle<FixedArray> regular_imports = isolate->factory()->NewFixedArray( | |
| static_cast<int>(descr->regular_imports().size())); | |
| { | |
| int i = 0; | |
| for (const auto& elem : descr->regular_imports()) { | |
| Handle<ModuleInfoEntry> serialized_entry = | |
| elem.second->Serialize(isolate); | |
| regular_imports->set(i++, *serialized_entry); | |
| } | |
| } | |
| Handle<ModuleInfo> result = isolate->factory()->NewModuleInfo(); | |
| result->set(kModuleRequestsIndex, *module_requests); | |
| result->set(kSpecialExportsIndex, *special_exports); | |
| result->set(kRegularExportsIndex, *regular_exports); | |
| result->set(kNamespaceImportsIndex, *namespace_imports); | |
| result->set(kRegularImportsIndex, *regular_imports); | |
| result->set(kModuleRequestPositionsIndex, *module_request_positions); | |
| return result; | |
| } | |
| int ModuleInfo::RegularExportCount() const { | |
| DCHECK_EQ(regular_exports()->length() % kRegularExportLength, 0); | |
| return regular_exports()->length() / kRegularExportLength; | |
| } | |
| String* ModuleInfo::RegularExportLocalName(int i) const { | |
| return String::cast(regular_exports()->get(i * kRegularExportLength + | |
| kRegularExportLocalNameOffset)); | |
| } | |
| int ModuleInfo::RegularExportCellIndex(int i) const { | |
| return Smi::ToInt(regular_exports()->get(i * kRegularExportLength + | |
| kRegularExportCellIndexOffset)); | |
| } | |
| FixedArray* ModuleInfo::RegularExportExportNames(int i) const { | |
| return FixedArray::cast(regular_exports()->get( | |
| i * kRegularExportLength + kRegularExportExportNamesOffset)); | |
| } | |
| } // namespace internal | |
| } // namespace v8 |