blob: 31c56ad199a17486f05940ac81052c5b3fdd371b [file]
// Copyright 2017 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.
#ifndef V8_OBJECTS_SHARED_FUNCTION_INFO_INL_H_
#define V8_OBJECTS_SHARED_FUNCTION_INFO_INL_H_
#include "src/objects/shared-function-info.h"
// Include the non-inl header before the rest of the headers.
#include <optional>
#include "src/base/macros.h"
#include "src/base/platform/mutex.h"
#include "src/base/strong-alias.h"
#include "src/builtins/builtins.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/common/globals.h"
#include "src/common/synchronization-point-support.h"
#include "src/handles/handles-inl.h"
#include "src/heap/heap-write-barrier-inl.h"
#include "src/objects/abstract-code.h"
#include "src/objects/contexts.h"
#include "src/objects/debug-objects-inl.h"
#include "src/objects/feedback-vector-inl.h"
#include "src/objects/function-kind.h"
#include "src/objects/heap-object-inl.h"
#include "src/objects/heap-object-set-map-inl.h"
#include "src/objects/hole.h"
#include "src/objects/instance-type-inl.h"
#include "src/objects/oddball-predicates-inl.h"
#include "src/objects/scope-info-inl.h"
#include "src/objects/script-inl.h"
#include "src/objects/string.h"
#include "src/objects/templates-inl.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-objects.h"
#endif // V8_ENABLE_WEBASSEMBLY
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8::internal {
// static
int PreparseData::SizeFor(int data_length, int children_length) {
return OFFSET_OF_DATA_START(PreparseData) +
ChildrenOffsetInData(data_length) +
children_length * sizeof(TaggedMember<PreparseData>);
}
int PreparseData::children_start_offset() const {
return OFFSET_OF_DATA_START(PreparseData) +
ChildrenOffsetInData(data_length());
}
void PreparseData::clear_padding() {
int data_end_offset = data_length() * sizeof(uint8_t);
int padding_size = ChildrenOffsetInData(data_length()) - data_end_offset;
DCHECK_LE(0, padding_size);
if (padding_size == 0) return;
memset(&data_and_children()[data_end_offset], 0, padding_size);
}
uint8_t PreparseData::get(int index) const {
DCHECK_LE(0, index);
DCHECK_LT(index, data_length());
return data()[index];
}
void PreparseData::set(int index, uint8_t value) {
DCHECK_LE(0, index);
DCHECK_LT(index, data_length());
data()[index] = value;
}
void PreparseData::copy_in(int index, const uint8_t* buffer, int length) {
DCHECK(index >= 0 && length >= 0 && length <= kMaxInt - index &&
index + length <= this->data_length());
memcpy(&data()[index], buffer, length);
}
Tagged<PreparseData> PreparseData::get_child(int index) const {
DCHECK_LE(0, index);
DCHECK_LT(index, children_length());
return children()[index].Relaxed_Load();
}
void PreparseData::set_child(int index, Tagged<PreparseData> value,
WriteBarrierMode mode) {
DCHECK_LE(0, index);
DCHECK_LT(index, children_length());
children()[index].Relaxed_Store(this, value, mode);
}
Tagged<String> UncompiledData::inferred_name() const {
return inferred_name_.load();
}
void UncompiledData::set_inferred_name(Tagged<String> value,
WriteBarrierMode mode) {
inferred_name_.store(this, value, mode);
}
Tagged<PreparseData> UncompiledDataWithPreparseData::preparse_data() const {
return preparse_data_.load();
}
void UncompiledDataWithPreparseData::set_preparse_data(
Tagged<PreparseData> value, WriteBarrierMode mode) {
preparse_data_.store(this, value, mode);
}
Tagged<BytecodeArray> InterpreterData::bytecode_array() const {
DCHECK(has_bytecode_array());
return bytecode_array_.load();
}
void InterpreterData::set_bytecode_array(Tagged<BytecodeArray> value,
WriteBarrierMode mode) {
DCHECK(TrustedHeapLayout::IsOwnedByAnyHeap(this));
bytecode_array_.store(this, value, mode);
}
bool InterpreterData::has_bytecode_array() const {
return !bytecode_array_.load().is_null();
}
void InterpreterData::clear_bytecode_array() {
bytecode_array_.store(this, {}, SKIP_WRITE_BARRIER);
}
Tagged<Code> InterpreterData::interpreter_trampoline() const {
DCHECK(has_interpreter_trampoline());
return interpreter_trampoline_.load();
}
void InterpreterData::set_interpreter_trampoline(Tagged<Code> value,
WriteBarrierMode mode) {
DCHECK(TrustedHeapLayout::IsOwnedByAnyHeap(this));
interpreter_trampoline_.store(this, value, mode);
}
bool InterpreterData::has_interpreter_trampoline() const {
return !interpreter_trampoline_.load().is_null();
}
void InterpreterData::clear_interpreter_trampoline() {
interpreter_trampoline_.store(this, {}, SKIP_WRITE_BARRIER);
}
Tagged<NameOrScopeInfoT> SharedFunctionInfo::name_or_scope_info(
AcquireLoadTag) const {
return name_or_scope_info_.Acquire_Load();
}
void SharedFunctionInfo::set_name_or_scope_info(Tagged<NameOrScopeInfoT> value,
ReleaseStoreTag,
WriteBarrierMode mode) {
name_or_scope_info_.Release_Store(this, value, mode);
}
Tagged<HeapObject> SharedFunctionInfo::script(AcquireLoadTag) const {
return script_.Acquire_Load();
}
void SharedFunctionInfo::set_script(Tagged<HeapObject> value, ReleaseStoreTag,
WriteBarrierMode mode) {
script_.Release_Store(this, value, mode);
}
Tagged<Object> SharedFunctionInfo::raw_script(AcquireLoadTag) const {
return script_.Acquire_Load();
}
void SharedFunctionInfo::set_raw_script(Tagged<Object> value, ReleaseStoreTag,
WriteBarrierMode mode) {
script_.Release_Store(this, Cast<HeapObject>(value), mode);
}
void SharedFunctionInfo::SetTrustedData(Tagged<ExposedTrustedObject> value,
WriteBarrierMode mode) {
trusted_function_data_.Release_Store(this, value, mode);
// Only one of trusted_function_data and untrusted_function_data can be in
// use, so clear the untrusted data field. Using -1 here as cleared data
// value allows HasBuiltinId to become quite simple, as it can just check if
// the untrusted data is a Smi containing a valid builtin ID.
constexpr int kClearedUntrustedFunctionDataValue = -1;
static_assert(!Builtins::IsBuiltinId(kClearedUntrustedFunctionDataValue));
untrusted_function_data_.Release_Store(
this, Smi::FromInt(kClearedUntrustedFunctionDataValue),
SKIP_WRITE_BARRIER);
}
void SharedFunctionInfo::SetUntrustedData(Tagged<Object> value,
WriteBarrierMode mode) {
untrusted_function_data_.Release_Store(this, value, mode);
// Only one of trusted_function_data and untrusted_function_data can be in
// use, so clear the trusted data field.
trusted_function_data_.clear(this);
}
bool SharedFunctionInfo::HasTrustedData() const {
return !trusted_function_data_.is_empty();
}
bool SharedFunctionInfo::HasUnpublishedTrustedData(
IsolateForSandbox isolate) const {
return trusted_function_data_.is_unpublished(isolate);
}
bool SharedFunctionInfo::HasUntrustedData() const { return !HasTrustedData(); }
template <typename T, IndirectPointerTagRange tag_range>
Tagged<T> SharedFunctionInfo::GetTrustedData(IsolateForSandbox isolate) const {
static_assert(tag_range != kAllIndirectPointerTags);
return Cast<T>(TrustedPointerField::ReadTrustedPointerField<tag_range>(
Tagged<HeapObject>(this),
offsetof(SharedFunctionInfo, trusted_function_data_), isolate,
kAcquireLoad));
}
Tagged<Object> SharedFunctionInfo::GetUntrustedData() const {
return untrusted_function_data_.Acquire_Load();
}
DEF_GETTER(SharedFunctionInfo, script, Tagged<HeapObject>) {
return script(kAcquireLoad);
}
bool SharedFunctionInfo::has_script(AcquireLoadTag tag) const {
return IsScript(script(tag));
}
Tagged<UnionOf<ScopeInfo, FeedbackMetadata, TheHole>>
SharedFunctionInfo::outer_scope_info_or_feedback_metadata() const {
return outer_scope_info_or_feedback_metadata_.load();
}
void SharedFunctionInfo::set_outer_scope_info_or_feedback_metadata(
Tagged<UnionOf<ScopeInfo, FeedbackMetadata, TheHole>> value,
WriteBarrierMode mode) {
outer_scope_info_or_feedback_metadata_.store(this, value, mode);
}
Tagged<UnionOf<ScopeInfo, FeedbackMetadata, TheHole>>
SharedFunctionInfo::raw_outer_scope_info_or_feedback_metadata() const {
return outer_scope_info_or_feedback_metadata();
}
void SharedFunctionInfo::set_raw_outer_scope_info_or_feedback_metadata(
Tagged<UnionOf<ScopeInfo, FeedbackMetadata, TheHole>> value,
WriteBarrierMode mode) {
set_outer_scope_info_or_feedback_metadata(value, mode);
}
Tagged<UnionOf<ScopeInfo, FeedbackMetadata, TheHole>>
SharedFunctionInfo::raw_outer_scope_info_or_feedback_metadata(
AcquireLoadTag) const {
return outer_scope_info_or_feedback_metadata_.Acquire_Load();
}
Tagged<Object> SharedFunctionInfo::untrusted_function_data() const {
return untrusted_function_data_.load();
}
void SharedFunctionInfo::set_untrusted_function_data(Tagged<Object> value,
WriteBarrierMode mode) {
untrusted_function_data_.store(this, value, mode);
}
// Primitive header accessors.
uint16_t SharedFunctionInfo::length() const { return length_; }
void SharedFunctionInfo::set_length(uint16_t value) { length_ = value; }
uint16_t SharedFunctionInfo::formal_parameter_count() const {
return formal_parameter_count_;
}
void SharedFunctionInfo::set_formal_parameter_count(uint16_t value) {
formal_parameter_count_ = value;
}
uint16_t SharedFunctionInfo::function_token_offset() const {
return function_token_offset_;
}
void SharedFunctionInfo::set_function_token_offset(uint16_t value) {
function_token_offset_ = value;
}
uint8_t SharedFunctionInfo::expected_nof_properties() const {
return expected_nof_properties_;
}
void SharedFunctionInfo::set_expected_nof_properties(uint8_t value) {
expected_nof_properties_ = value;
}
int32_t SharedFunctionInfo::unique_id() const { return unique_id_; }
void SharedFunctionInfo::set_unique_id(int32_t value) { unique_id_ = value; }
uint16_t SharedFunctionInfo::feedback_slot() const {
return feedback_slot_.load(std::memory_order_relaxed);
}
void SharedFunctionInfo::set_feedback_slot(uint16_t value) {
feedback_slot_.store(value, std::memory_order_relaxed);
}
uint16_t SharedFunctionInfo::internal_formal_parameter_count_with_receiver()
const {
const uint16_t param_count = formal_parameter_count();
return param_count;
}
bool SharedFunctionInfo::IsSloppyNormalJSFunction() const {
// TODO(dcarney): Fix the empty scope and push this down into
// ScopeInfo::IsSloppyNormalJSFunction.
return kind() == FunctionKind::kNormalFunction && is_sloppy(language_mode());
}
uint32_t SharedFunctionInfo::unused_parameter_bits() const {
DCHECK_EQ(scope_info(kAcquireLoad)->scope_type(), ScopeType::FUNCTION_SCOPE);
return scope_info(kAcquireLoad)->unused_parameter_bits();
}
bool SharedFunctionInfo::CanOnlyAccessFixedFormalParameters() const {
return scope_info(kAcquireLoad)->CanOnlyAccessFixedFormalParameters();
}
uint16_t SharedFunctionInfo::internal_formal_parameter_count_without_receiver()
const {
const uint16_t param_count = formal_parameter_count();
if (param_count == kDontAdaptArgumentsSentinel) return param_count;
return param_count - kJSArgcReceiverSlots;
}
void SharedFunctionInfo::set_internal_formal_parameter_count(int value) {
DCHECK_EQ(value, static_cast<uint16_t>(value));
DCHECK_GE(value, kJSArgcReceiverSlots);
set_formal_parameter_count(value);
}
uint16_t SharedFunctionInfo::raw_function_token_offset() const {
return function_token_offset();
}
void SharedFunctionInfo::set_raw_function_token_offset(uint16_t value) {
set_function_token_offset(value);
}
int32_t SharedFunctionInfo::flags(RelaxedLoadTag) const {
return static_cast<int32_t>(flags_.load(std::memory_order_relaxed));
}
void SharedFunctionInfo::set_flags(int32_t value, RelaxedStoreTag) {
flags_.store(static_cast<uint32_t>(value), std::memory_order_relaxed);
}
int32_t SharedFunctionInfo::function_literal_id(RelaxedLoadTag) const {
return function_literal_id_.load(std::memory_order_relaxed);
}
void SharedFunctionInfo::set_function_literal_id(int32_t value,
RelaxedStoreTag) {
function_literal_id_.store(value, std::memory_order_relaxed);
}
int32_t SharedFunctionInfo::relaxed_flags() const {
return flags(kRelaxedLoad);
}
void SharedFunctionInfo::set_relaxed_flags(int32_t flags) {
return set_flags(flags, kRelaxedStore);
}
uint8_t SharedFunctionInfo::flags2() const { return flags2_; }
void SharedFunctionInfo::set_flags2(uint8_t value) { flags2_ = value; }
bool SharedFunctionInfo::HasSharedName() const {
Tagged<Object> value = name_or_scope_info(kAcquireLoad);
if (IsScopeInfo(value)) {
return Cast<ScopeInfo>(value)->HasSharedFunctionName();
}
return value != kNoSharedNameSentinel;
}
Tagged<String> SharedFunctionInfo::Name() const {
if (!HasSharedName()) return GetReadOnlyRoots().empty_string();
Tagged<Object> value = name_or_scope_info(kAcquireLoad);
if (IsScopeInfo(value)) {
if (Cast<ScopeInfo>(value)->HasFunctionName()) {
return Cast<String>(Cast<ScopeInfo>(value)->FunctionName());
}
return GetReadOnlyRoots().empty_string();
}
return Cast<String>(value);
}
void SharedFunctionInfo::SetName(Tagged<String> name) {
Tagged<Object> maybe_scope_info = name_or_scope_info(kAcquireLoad);
if (IsScopeInfo(maybe_scope_info)) {
Cast<ScopeInfo>(maybe_scope_info)->SetFunctionName(name);
} else {
DCHECK(IsString(maybe_scope_info) ||
maybe_scope_info == kNoSharedNameSentinel);
set_name_or_scope_info(name, kReleaseStore);
}
UpdateFunctionMapIndex();
}
bool SharedFunctionInfo::is_script() const {
if (!is_toplevel()) return false;
bool result = scope_info(kAcquireLoad)->is_script_scope();
DCHECK_IMPLIES(result, Cast<Script>(script())->is_host());
return result;
}
bool SharedFunctionInfo::needs_script_context() const {
return is_script() && scope_info(kAcquireLoad)->ContextLocalCount() > 0;
}
Tagged<AbstractCode> SharedFunctionInfo::abstract_code(Isolate* isolate) {
// TODO(v8:11429): Decide if this return bytecode or baseline code, when the
// latter is present.
if (HasBytecodeArray()) {
return Cast<AbstractCode>(GetBytecodeArray(isolate));
} else {
return Cast<AbstractCode>(GetCode(isolate));
}
}
int SharedFunctionInfo::function_token_position() const {
int offset = raw_function_token_offset();
if (offset == kFunctionTokenOutOfRange) {
return kNoSourcePosition;
} else {
return StartPosition() - offset;
}
}
template <typename IsolateT>
bool SharedFunctionInfo::AreSourcePositionsAvailable(IsolateT* isolate) const {
if (v8_flags.enable_lazy_source_positions) {
return !HasBytecodeArray() ||
GetBytecodeArray(isolate)->HasSourcePositionTable();
}
return true;
}
template <typename IsolateT>
SharedFunctionInfo::Inlineability SharedFunctionInfo::GetInlineability(
CodeKind code_kind, IsolateT* isolate) const {
if (!IsScript(script())) return kHasNoScript;
if (isolate->is_precise_binary_code_coverage() &&
!has_reported_binary_coverage()) {
// We may miss invocations if this function is inlined.
return kNeedsBinaryCoverage;
}
// Built-in functions are handled by the JSCallReducer.
if (HasBuiltinId()) return kIsBuiltin;
if (!IsUserJavaScript()) return kIsNotUserCode;
// If there is no bytecode array, the function is not compiled, so we don't
// want to inline.
if (!HasBytecodeArray()) return kHasNoBytecode;
if (GetBytecodeArray(isolate)->length() >
v8_flags.max_inlined_bytecode_size) {
return kExceedsBytecodeLimit;
}
{
MutexGuardIfOffThread<IsolateT> mutex_guard(
isolate->shared_function_info_access(), isolate);
if (HasBreakInfo(isolate->GetMainThreadIsolateUnsafe())) {
return kMayContainBreakPoints;
}
}
if (optimization_disabled(code_kind)) return kHasOptimizationDisabled;
return kIsInlineable;
}
BIT_FIELD_ACCESSORS(SharedFunctionInfo, flags2, class_scope_has_private_brand,
SharedFunctionInfo::ClassScopeHasPrivateBrandBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, flags2,
has_static_private_methods_or_accessors,
SharedFunctionInfo::HasStaticPrivateMethodsOrAccessorsBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, flags2, is_sparkplug_compiling,
SharedFunctionInfo::IsSparkplugCompilingBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, flags2, maglev_compilation_failed,
SharedFunctionInfo::MaglevCompilationFailedBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, flags2,
function_context_independent_compiled,
SharedFunctionInfo::FunctionContextIndependentCompiledBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags, syntax_kind,
SharedFunctionInfo::FunctionSyntaxKindBits)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags, allows_lazy_compilation,
SharedFunctionInfo::AllowLazyCompilationBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags, has_duplicate_parameters,
SharedFunctionInfo::HasDuplicateParametersBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags, native,
SharedFunctionInfo::IsNativeBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags,
requires_instance_members_initializer,
SharedFunctionInfo::RequiresInstanceMembersInitializerBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags,
name_should_print_as_anonymous,
SharedFunctionInfo::NameShouldPrintAsAnonymousBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags,
has_reported_binary_coverage,
SharedFunctionInfo::HasReportedBinaryCoverageBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags, is_toplevel,
SharedFunctionInfo::IsTopLevelBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags, properties_are_final,
SharedFunctionInfo::PropertiesAreFinalBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags,
private_name_lookup_skips_outer_class,
SharedFunctionInfo::PrivateNameLookupSkipsOuterClassBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags, is_hoisted_in_context,
SharedFunctionInfo::IsHoistedInContextBit)
bool SharedFunctionInfo::optimization_disabled(CodeKind kind) const {
switch (kind) {
case CodeKind::MAGLEV:
return IsTerminalBailoutReasonForMaglev(disabled_optimization_reason());
case CodeKind::TURBOFAN_JS:
return IsTerminalBailoutReasonForTurbofan(disabled_optimization_reason());
default:
UNREACHABLE();
}
}
bool SharedFunctionInfo::all_optimization_disabled() const {
return IsTerminalBailoutReason(disabled_optimization_reason());
}
BailoutReason SharedFunctionInfo::disabled_optimization_reason() const {
return DisabledOptimizationReasonBits::decode(flags(kRelaxedLoad));
}
LanguageMode SharedFunctionInfo::language_mode() const {
static_assert(LanguageModeSize == 2);
return construct_language_mode(IsStrictBit::decode(flags(kRelaxedLoad)));
}
void SharedFunctionInfo::set_language_mode(LanguageMode language_mode) {
static_assert(LanguageModeSize == 2);
// We only allow language mode transitions that set the same language mode
// again or go up in the chain:
DCHECK(is_sloppy(this->language_mode()) || is_strict(language_mode));
int hints = flags(kRelaxedLoad);
hints = IsStrictBit::update(hints, is_strict(language_mode));
set_flags(hints, kRelaxedStore);
UpdateFunctionMapIndex();
}
FunctionKind SharedFunctionInfo::kind() const {
static_assert(FunctionKindBits::kSize == kFunctionKindBitSize);
return FunctionKindBits::decode(flags(kRelaxedLoad));
}
void SharedFunctionInfo::set_kind(FunctionKind kind) {
int hints = flags(kRelaxedLoad);
hints = FunctionKindBits::update(hints, kind);
hints = IsClassConstructorBit::update(hints, IsClassConstructor(kind));
set_flags(hints, kRelaxedStore);
UpdateFunctionMapIndex();
}
bool SharedFunctionInfo::is_wrapped() const {
return syntax_kind() == FunctionSyntaxKind::kWrapped;
}
bool SharedFunctionInfo::construct_as_builtin() const {
return ConstructAsBuiltinBit::decode(flags(kRelaxedLoad));
}
void SharedFunctionInfo::CalculateConstructAsBuiltin() {
bool uses_builtins_construct_stub = false;
if (HasBuiltinId()) {
Builtin id = builtin_id();
if (id != Builtin::kCompileLazy &&
#if V8_ENABLE_WEBASSEMBLY
id != Builtin::kWasmMethodWrapper &&
#endif
id != Builtin::kEmptyFunction) {
uses_builtins_construct_stub = true;
}
} else if (IsApiFunction()) {
uses_builtins_construct_stub = true;
}
int f = flags(kRelaxedLoad);
f = ConstructAsBuiltinBit::update(f, uses_builtins_construct_stub);
set_flags(f, kRelaxedStore);
}
uint16_t SharedFunctionInfo::age() const {
return age_.load(std::memory_order_relaxed);
}
void SharedFunctionInfo::set_age(uint16_t value) {
age_.store(value, std::memory_order_relaxed);
}
uint16_t SharedFunctionInfo::CompareExchangeAge(uint16_t expected_age,
uint16_t new_age) {
age_.compare_exchange_strong(expected_age, new_age,
std::memory_order_relaxed);
return expected_age;
}
int SharedFunctionInfo::function_map_index() const {
// Note: Must be kept in sync with the FastNewClosure builtin.
int index = Context::FIRST_FUNCTION_MAP_INDEX +
FunctionMapIndexBits::decode(flags(kRelaxedLoad));
DCHECK_LE(index, Context::LAST_FUNCTION_MAP_INDEX);
return index;
}
void SharedFunctionInfo::set_function_map_index(int index) {
static_assert(Context::LAST_FUNCTION_MAP_INDEX <=
Context::FIRST_FUNCTION_MAP_INDEX + FunctionMapIndexBits::kMax);
DCHECK_LE(Context::FIRST_FUNCTION_MAP_INDEX, index);
DCHECK_LE(index, Context::LAST_FUNCTION_MAP_INDEX);
index -= Context::FIRST_FUNCTION_MAP_INDEX;
set_flags(FunctionMapIndexBits::update(flags(kRelaxedLoad), index),
kRelaxedStore);
}
void SharedFunctionInfo::UpdateFunctionMapIndex() {
int map_index =
Context::FunctionMapIndex(language_mode(), kind(), HasSharedName());
set_function_map_index(map_index);
}
void SharedFunctionInfo::DontAdaptArguments() {
#if V8_ENABLE_WEBASSEMBLY
// TODO(leszeks): Revise this DCHECK now that the code field is gone.
DCHECK(!HasWasmExportedFunctionData(GetCurrentIsolateForSandbox()));
#endif // V8_ENABLE_WEBASSEMBLY
if (HasBuiltinId()) {
Builtin builtin = builtin_id();
if (Builtins::KindOf(builtin) == Builtins::TFJ) {
const int formal_parameter_count =
Builtins::GetStackParameterCount(builtin);
// If we have `kDontAdaptArgumentsSentinel` or no arguments, then we are
// good. Otherwise this is a mismatch.
if (formal_parameter_count != kDontAdaptArgumentsSentinel &&
formal_parameter_count != JSParameterCount(0)) {
FATAL(
"Conflicting argument adaptation configuration (SFI vs call "
"descriptor) for builtin: %s (%d)",
Builtins::name(builtin), static_cast<int>(builtin));
}
}
}
set_formal_parameter_count(kDontAdaptArgumentsSentinel);
}
DEF_ACQUIRE_GETTER(SharedFunctionInfo, scope_info, Tagged<ScopeInfo>) {
Tagged<Object> maybe_scope_info = name_or_scope_info(tag);
if (IsScopeInfo(maybe_scope_info)) {
return Cast<ScopeInfo>(maybe_scope_info);
}
return GetReadOnlyRoots().empty_scope_info();
}
DEF_GETTER(SharedFunctionInfo, scope_info, Tagged<ScopeInfo>) {
return scope_info(kAcquireLoad);
}
Tagged<ScopeInfo> SharedFunctionInfo::EarlyScopeInfo(AcquireLoadTag tag) {
// Keep in sync with the scope_info getter above.
Tagged<Object> maybe_scope_info = name_or_scope_info(tag);
if (IsScopeInfo(maybe_scope_info)) {
return Cast<ScopeInfo>(maybe_scope_info);
}
return ReadOnlyHeap::EarlyGetReadOnlyRoots(this).empty_scope_info();
}
void SharedFunctionInfo::SetScopeInfo(Tagged<ScopeInfo> scope_info,
WriteBarrierMode mode) {
// Move the existing name onto the ScopeInfo.
Tagged<NameOrScopeInfoT> name_or_scope_info =
this->name_or_scope_info(kAcquireLoad);
Tagged<UnionOf<Smi, String>> name;
if (IsScopeInfo(name_or_scope_info)) {
name = Cast<ScopeInfo>(name_or_scope_info)->FunctionName();
} else {
name = Cast<UnionOf<Smi, String>>(name_or_scope_info);
}
DCHECK(IsString(name) || name == kNoSharedNameSentinel);
// ScopeInfo can get promoted to read-only space. Now that we reuse them after
// flushing bytecode, we'll actually reinstall read-only scopeinfos on
// SharedFunctionInfos if they required a context. The read-only scopeinfos
// should already be fully initialized though, and hence will already have the
// right FunctionName (and InferredName if relevant).
if (scope_info->FunctionName() != name) {
scope_info->SetFunctionName(name);
}
if (HasInferredName() && inferred_name()->length() != 0 &&
scope_info->InferredFunctionName() != inferred_name()) {
scope_info->SetInferredFunctionName(inferred_name());
}
set_name_or_scope_info(scope_info, kReleaseStore, mode);
}
void SharedFunctionInfo::set_raw_scope_info(Tagged<ScopeInfo> scope_info,
WriteBarrierMode mode) {
name_or_scope_info_.store(this, scope_info, mode);
}
DEF_GETTER(SharedFunctionInfo, outer_scope_info,
Tagged<UnionOf<ScopeInfo, TheHole>>) {
DCHECK(!is_compiled());
DCHECK(!HasFeedbackMetadata());
return Cast<UnionOf<ScopeInfo, TheHole>>(
raw_outer_scope_info_or_feedback_metadata());
}
bool SharedFunctionInfo::HasOuterScopeInfo() const {
Tagged<ScopeInfo> info = scope_info(kAcquireLoad);
if (info->IsEmpty()) {
if (is_compiled()) return false;
Tagged<UnionOf<ScopeInfo, TheHole>> maybe_outer_info = outer_scope_info();
if (IsTheHole(maybe_outer_info)) return false;
DCHECK(!Cast<ScopeInfo>(maybe_outer_info)->IsEmpty());
return true;
}
DCHECK_IMPLIES(info->HasOuterScopeInfo(), !info->OuterScopeInfo()->IsEmpty());
return info->HasOuterScopeInfo();
}
Tagged<ScopeInfo> SharedFunctionInfo::GetOuterScopeInfo() const {
DCHECK(HasOuterScopeInfo());
Tagged<ScopeInfo> info = scope_info(kAcquireLoad);
if (info->IsEmpty()) return Cast<ScopeInfo>(outer_scope_info());
return info->OuterScopeInfo();
}
Tagged<ScopeInfo> SharedFunctionInfo::TryGetScopeInfoForMerge() const {
Tagged<Object> maybe_scope_info = name_or_scope_info(kAcquireLoad);
if (IsScopeInfo(maybe_scope_info)) {
return Cast<ScopeInfo>(maybe_scope_info);
}
Tagged<Object> maybe_outer_scope_info_or_feedback =
raw_outer_scope_info_or_feedback_metadata(kAcquireLoad);
if (IsScopeInfo(maybe_outer_scope_info_or_feedback)) {
return Cast<ScopeInfo>(maybe_outer_scope_info_or_feedback);
}
return GetReadOnlyRoots().empty_scope_info();
}
Tagged<ScopeInfo> SharedFunctionInfo::TryGetOuterScopeInfo() const {
if (Tagged<ScopeInfo> scope_info;
TryCast(name_or_scope_info(kAcquireLoad), &scope_info)) {
if (scope_info->HasOuterScopeInfo()) {
return scope_info->OuterScopeInfo();
}
return GetReadOnlyRoots().empty_scope_info();
}
SYNCHRONIZATION_POINT("BeforeGetOuterScopeInfo");
if (Tagged<ScopeInfo> outer_scope_info;
TryCast(raw_outer_scope_info_or_feedback_metadata(kAcquireLoad),
&outer_scope_info)) {
return outer_scope_info;
}
return GetReadOnlyRoots().empty_scope_info();
}
void SharedFunctionInfo::set_outer_scope_info(
Tagged<UnionOf<ScopeInfo, TheHole>> value, WriteBarrierMode mode) {
DCHECK(!is_compiled());
DCHECK(IsTheHole(raw_outer_scope_info_or_feedback_metadata()));
DCHECK(IsTheHole(value) || IsScopeInfo(value));
DCHECK(scope_info()->IsEmpty());
set_raw_outer_scope_info_or_feedback_metadata(value, mode);
}
bool SharedFunctionInfo::HasFeedbackMetadata() const {
Tagged<UnionOf<ScopeInfo, FeedbackMetadata, TheHole>> raw =
raw_outer_scope_info_or_feedback_metadata();
return IsFeedbackMetadata(raw);
}
bool SharedFunctionInfo::HasFeedbackMetadata(AcquireLoadTag tag) const {
Tagged<UnionOf<ScopeInfo, FeedbackMetadata, TheHole>> raw =
raw_outer_scope_info_or_feedback_metadata(tag);
return IsFeedbackMetadata(raw);
}
DEF_GETTER(SharedFunctionInfo, feedback_metadata, Tagged<FeedbackMetadata>) {
DCHECK(HasFeedbackMetadata());
return Cast<FeedbackMetadata>(raw_outer_scope_info_or_feedback_metadata());
}
Tagged<FeedbackMetadata> SharedFunctionInfo::feedback_metadata(
AcquireLoadTag) const {
Tagged<FeedbackMetadata> value = Cast<FeedbackMetadata>(
outer_scope_info_or_feedback_metadata_.Acquire_Load());
DCHECK(HasFeedbackMetadata(kAcquireLoad));
return value;
}
void SharedFunctionInfo::set_feedback_metadata(Tagged<FeedbackMetadata> value,
ReleaseStoreTag,
WriteBarrierMode mode) {
DCHECK(!HasFeedbackMetadata(kAcquireLoad) && IsFeedbackMetadata(value));
outer_scope_info_or_feedback_metadata_.Release_Store(this, value, mode);
}
bool SharedFunctionInfo::is_compiled() const {
return GetUntrustedData() != Smi::FromEnum(Builtin::kCompileLazy) &&
!HasUncompiledData(GetCurrentIsolateForSandbox());
}
template <typename IsolateT>
IsCompiledScope SharedFunctionInfo::is_compiled_scope(IsolateT* isolate) const {
return IsCompiledScope(this, isolate);
}
IsCompiledScope::IsCompiledScope(const Tagged<SharedFunctionInfo> shared,
Isolate* isolate) {
Tagged<Union<Smi, TrustedObject>> data_obj = shared->GetTrustedData(isolate);
if (Tagged<Code> code; TryCast(data_obj, &code)) {
DCHECK_EQ(code->kind(), CodeKind::BASELINE);
data_obj = code->bytecode_or_interpreter_data();
}
// Unlike GetBytecodeArray, we don't bother checking for DebugInfo here. If
// there is DebugInfo, then it will hold both the debug and original
// BytecodeArray strongly, so it doesn't matter which of those we hold.
if (Tagged<BytecodeArray> bytecode; TryCast(data_obj, &bytecode)) {
retain_code_ = handle(bytecode, isolate);
is_compiled_ = true;
} else if (Tagged<InterpreterData> interpreter_data;
TryCast(data_obj, &interpreter_data)) {
retain_code_ = handle(interpreter_data->bytecode_array(), isolate);
is_compiled_ = true;
} else if (IsUncompiledData(data_obj)) {
retain_code_ = {};
is_compiled_ = false;
} else {
retain_code_ = {};
is_compiled_ = shared->is_compiled();
}
DCHECK_IMPLIES(!retain_code_.is_null(), is_compiled());
DCHECK_EQ(shared->is_compiled(), is_compiled());
}
IsCompiledScope::IsCompiledScope(const Tagged<SharedFunctionInfo> shared,
LocalIsolate* isolate) {
Tagged<Union<Smi, TrustedObject>> data_obj = shared->GetTrustedData(isolate);
auto Default = [&]() {
retain_code_ = {};
is_compiled_ = shared->is_compiled();
};
if (Tagged<TrustedObject> data; TryCast<TrustedObject>(data_obj, &data)) {
if (Tagged<Code> code; TryCast(data, &code)) {
DCHECK(code->kind() == CodeKind::BASELINE);
data_obj = code->bytecode_or_interpreter_data();
}
// Unlike GetBytecodeArray, we don't bother checking for DebugInfo here. If
// there is DebugInfo, then it will hold both the debug and original
// BytecodeArray strongly, so it doesn't matter which of those we hold.
if (Tagged<BytecodeArray> bytecode; TryCast(data, &bytecode)) {
retain_code_ = isolate->heap()->NewPersistentHandle(bytecode);
is_compiled_ = true;
} else if (Tagged<InterpreterData> interpreter_data;
TryCast(data, &interpreter_data)) {
retain_code_ = isolate->heap()->NewPersistentHandle(
interpreter_data->bytecode_array());
is_compiled_ = true;
} else if (Is<UncompiledData>(data)) {
retain_code_ = {};
is_compiled_ = false;
} else {
Default();
}
} else {
Default();
}
DCHECK_IMPLIES(!retain_code_.is_null(), is_compiled());
DCHECK_EQ(shared->is_compiled(), is_compiled());
}
IsBaselineCompiledScope::IsBaselineCompiledScope(
const Tagged<SharedFunctionInfo> shared, Isolate* isolate) {
Tagged<Union<Smi, TrustedObject>> data_obj = shared->GetTrustedData(isolate);
if (Tagged<Code> code; TryCast(data_obj, &code)) {
DCHECK_EQ(code->kind(), CodeKind::BASELINE);
retain_code_ = handle(code, isolate);
is_compiled_ = true;
}
}
bool SharedFunctionInfo::has_simple_parameters() const {
return scope_info(kAcquireLoad)->HasSimpleParameters();
}
bool SharedFunctionInfo::CanCollectSourcePosition(Isolate* isolate) {
// This function is called during heap iteration and so might see
// dead-but-inconsistent SFIs, e.g. those referencing an unpublished trusted
// object, so we need to check for that here.
return v8_flags.enable_lazy_source_positions &&
!HasUnpublishedTrustedData(isolate) && HasBytecodeArray() &&
!GetBytecodeArray(isolate)->HasSourcePositionTable();
}
bool SharedFunctionInfo::IsApiFunction() const {
return IsFunctionTemplateInfo(GetUntrustedData());
}
DEF_GETTER(SharedFunctionInfo, api_func_data, Tagged<FunctionTemplateInfo>) {
DCHECK(IsApiFunction());
return Cast<FunctionTemplateInfo>(GetUntrustedData());
}
DEF_GETTER(SharedFunctionInfo, HasBytecodeArray, bool) {
Tagged<Union<Smi, TrustedObject>> data =
GetTrustedData(GetCurrentIsolateForSandbox());
// If the SFI has no trusted data, GetTrustedData() will return Smi::zero().
if (IsSmi(data)) return false;
return IsBytecodeArray(data) || IsInterpreterData(data) || IsCode(data);
}
template <typename IsolateT>
Tagged<BytecodeArray> SharedFunctionInfo::GetBytecodeArray(
IsolateT* isolate) const {
MutexGuardIfOffThread<IsolateT> mutex_guard(
isolate->shared_function_info_access(), isolate);
Isolate* main_isolate = isolate->GetMainThreadIsolateUnsafe();
return GetBytecodeArrayInternal(main_isolate);
}
Tagged<BytecodeArray> SharedFunctionInfo::GetBytecodeArrayForGC(
Isolate* isolate) const {
// Can only be used during GC when all threads are halted.
DCHECK_EQ(Isolate::Current()->heap()->gc_state(), Heap::MARK_COMPACT);
return GetBytecodeArrayInternal(isolate);
}
Tagged<BytecodeArray> SharedFunctionInfo::GetBytecodeArrayInternal(
Isolate* isolate) const {
DCHECK(HasBytecodeArray());
std::optional<Tagged<DebugInfo>> debug_info = TryGetDebugInfo(isolate);
if (debug_info.has_value() &&
debug_info.value()->HasInstrumentedBytecodeArray()) {
return debug_info.value()->OriginalBytecodeArray(isolate);
}
return GetActiveBytecodeArray(isolate);
}
Tagged<BytecodeArray> SharedFunctionInfo::GetActiveBytecodeArray(
Isolate* isolate) const {
auto data = GetTrustedData(isolate);
if (Tagged<Code> baseline_code; TryCast(data, &baseline_code)) {
data = baseline_code->bytecode_or_interpreter_data();
}
if (Tagged<BytecodeArray> bytecode_array; TryCast(data, &bytecode_array)) {
return bytecode_array;
}
return SbxCast<InterpreterData>(data)->bytecode_array();
}
void SharedFunctionInfo::SetActiveBytecodeArray(Tagged<BytecodeArray> bytecode,
IsolateForSandbox isolate) {
// We don't allow setting the active bytecode array on baseline-optimized
// functions. They should have been flushed earlier.
DCHECK(!HasBaselineCode());
if (HasInterpreterData(isolate)) {
interpreter_data(isolate)->set_bytecode_array(bytecode);
} else {
DCHECK(HasBytecodeArray());
overwrite_bytecode_array(bytecode);
}
}
void SharedFunctionInfo::set_bytecode_array(Tagged<BytecodeArray> bytecode) {
DCHECK(GetUntrustedData() == Smi::FromEnum(Builtin::kCompileLazy) ||
HasUncompiledData(GetCurrentIsolateForSandbox()));
SetTrustedData(bytecode);
}
void SharedFunctionInfo::overwrite_bytecode_array(
Tagged<BytecodeArray> bytecode) {
DCHECK(HasBytecodeArray());
SetTrustedData(bytecode);
}
Tagged<Code> SharedFunctionInfo::InterpreterTrampoline(
IsolateForSandbox isolate) const {
DCHECK(HasInterpreterData(isolate));
return interpreter_data(isolate)->interpreter_trampoline();
}
bool SharedFunctionInfo::HasInterpreterData(IsolateForSandbox isolate) const {
auto data = GetTrustedData(isolate);
if (Tagged<Code> baseline_code; TryCast(data, &baseline_code)) {
DCHECK_EQ(baseline_code->kind(), CodeKind::BASELINE);
data = baseline_code->bytecode_or_interpreter_data();
}
return IsInterpreterData(data);
}
Tagged<InterpreterData> SharedFunctionInfo::interpreter_data(
IsolateForSandbox isolate) const {
DCHECK(HasInterpreterData(isolate));
auto data = GetTrustedData(isolate);
if (Tagged<Code> baseline_code; TryCast(data, &baseline_code)) {
DCHECK_EQ(baseline_code->kind(), CodeKind::BASELINE);
data = baseline_code->bytecode_or_interpreter_data();
}
return SbxCast<InterpreterData>(data);
}
void SharedFunctionInfo::set_interpreter_data(
Isolate* isolate, Tagged<InterpreterData> interpreter_data,
WriteBarrierMode mode) {
DCHECK(isolate->interpreted_frames_native_stack());
DCHECK(!HasBaselineCode());
SetTrustedData(interpreter_data, mode);
}
DEF_GETTER(SharedFunctionInfo, HasBaselineCode, bool) {
auto data = GetTrustedData(GetCurrentIsolateForSandbox());
if (Tagged<Code> code; TryCast(data, &code)) {
DCHECK_EQ(code->kind(), CodeKind::BASELINE);
return true;
}
return false;
}
DEF_ACQUIRE_GETTER(SharedFunctionInfo, baseline_code, Tagged<Code>) {
DCHECK(HasBaselineCode());
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
auto code = GetTrustedData<Code, kCodeIndirectPointerTag>(isolate);
SBXCHECK_EQ(code->kind(), CodeKind::BASELINE);
return code;
}
void SharedFunctionInfo::set_baseline_code(Tagged<Code> baseline_code,
ReleaseStoreTag tag,
WriteBarrierMode mode) {
DCHECK_EQ(baseline_code->kind(), CodeKind::BASELINE);
SetTrustedData(baseline_code, mode);
}
void SharedFunctionInfo::FlushBaselineCode() {
DCHECK(HasBaselineCode());
Tagged<TrustedObject> new_data =
baseline_code(kAcquireLoad)->bytecode_or_interpreter_data();
DCHECK(IsBytecodeArray(new_data) || IsInterpreterData(new_data));
SetTrustedData(TrustedCast<ExposedTrustedObject>(new_data));
}
#if V8_ENABLE_WEBASSEMBLY
bool SharedFunctionInfo::HasWasmFunctionData(IsolateForSandbox isolate) const {
return IsWasmFunctionData(GetTrustedData(isolate));
}
bool SharedFunctionInfo::HasWasmExportedFunctionData(
IsolateForSandbox isolate) const {
return IsWasmExportedFunctionData(GetTrustedData(isolate));
}
bool SharedFunctionInfo::HasWasmCapiFunctionData(
IsolateForSandbox isolate) const {
return IsWasmCapiFunctionData(GetTrustedData(isolate));
}
bool SharedFunctionInfo::HasWasmResumeData() const {
return IsWasmResumeData(GetUntrustedData());
}
DEF_GETTER(SharedFunctionInfo, wasm_function_data, Tagged<WasmFunctionData>) {
// TODO(saelo): It would be nicer if the caller provided an
// IsolateForSandbox.
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
DCHECK(HasWasmFunctionData(isolate));
return GetTrustedData<WasmFunctionData,
kWasmFunctionDataIndirectPointerTagRange>(isolate);
}
DEF_GETTER(SharedFunctionInfo, wasm_exported_function_data,
Tagged<WasmExportedFunctionData>) {
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
DCHECK(HasWasmExportedFunctionData(isolate));
return GetTrustedData<WasmExportedFunctionData,
kWasmExportedFunctionDataIndirectPointerTag>(isolate);
}
DEF_GETTER(SharedFunctionInfo, wasm_capi_function_data,
Tagged<WasmCapiFunctionData>) {
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
DCHECK(HasWasmCapiFunctionData(isolate));
return GetTrustedData<WasmCapiFunctionData,
kWasmCapiFunctionDataIndirectPointerTag>(isolate);
}
DEF_GETTER(SharedFunctionInfo, wasm_resume_data, Tagged<WasmResumeData>) {
DCHECK(HasWasmResumeData());
return Cast<WasmResumeData>(GetUntrustedData());
}
#endif // V8_ENABLE_WEBASSEMBLY
bool SharedFunctionInfo::HasBuiltinId() const {
Tagged<Object> data = GetUntrustedData();
return IsSmi(data) && Builtins::IsBuiltinId(Smi::ToInt(data));
}
Builtin SharedFunctionInfo::builtin_id() const {
DCHECK(HasBuiltinId());
int id = Smi::ToInt(GetUntrustedData());
// The builtin id is read from the heap and so must be assumed to be
// untrusted in the sandbox attacker model. As it is considered trusted by
// e.g. `GetCode` (when fetching the code for this SFI), we validate it here.
SBXCHECK(Builtins::IsBuiltinId(id));
return Builtins::FromInt(id);
}
void SharedFunctionInfo::set_builtin_id(Builtin builtin) {
DCHECK(Builtins::IsBuiltinId(builtin));
SetUntrustedData(Smi::FromInt(static_cast<int>(builtin)), SKIP_WRITE_BARRIER);
}
bool SharedFunctionInfo::HasUncompiledData(IsolateForSandbox isolate) const {
return IsUncompiledData(GetTrustedData(isolate));
}
Tagged<UncompiledData> SharedFunctionInfo::uncompiled_data(
IsolateForSandbox isolate) const {
DCHECK(HasUncompiledData(isolate));
return GetTrustedData<UncompiledData, kUncompiledDataIndirectPointerTag>(
isolate);
}
void SharedFunctionInfo::set_uncompiled_data(
Tagged<UncompiledData> uncompiled_data, WriteBarrierMode mode) {
DCHECK(IsUncompiledData(uncompiled_data));
SetTrustedData(uncompiled_data, mode);
}
bool SharedFunctionInfo::HasUncompiledDataWithPreparseData(
IsolateForSandbox isolate) const {
return IsUncompiledDataWithPreparseData(GetTrustedData(isolate));
}
Tagged<UncompiledDataWithPreparseData>
SharedFunctionInfo::uncompiled_data_with_preparse_data(
IsolateForSandbox isolate) const {
DCHECK(HasUncompiledDataWithPreparseData(isolate));
Tagged<UncompiledData> data = uncompiled_data(isolate);
// TODO(saelo): this SBXCHECK is needed because our type tags don't currently
// support type hierarchies.
return SbxCast<UncompiledDataWithPreparseData>(data);
}
void SharedFunctionInfo::set_uncompiled_data_with_preparse_data(
Tagged<UncompiledDataWithPreparseData> uncompiled_data_with_preparse_data,
WriteBarrierMode mode) {
DCHECK_EQ(GetUntrustedData(), Smi::FromEnum(Builtin::kCompileLazy));
DCHECK(IsUncompiledDataWithPreparseData(uncompiled_data_with_preparse_data));
SetTrustedData(uncompiled_data_with_preparse_data, mode);
}
bool SharedFunctionInfo::HasUncompiledDataWithoutPreparseData(
IsolateForSandbox isolate) const {
return IsUncompiledDataWithoutPreparseData(GetTrustedData(isolate));
}
void SharedFunctionInfo::ClearPreparseData(IsolateForSandbox isolate) {
DCHECK(HasUncompiledDataWithPreparseData(isolate));
Tagged<UncompiledDataWithPreparseData> data =
uncompiled_data_with_preparse_data(isolate);
// Trim off the pre-parsed scope data from the uncompiled data by swapping the
// map, leaving only an uncompiled data without pre-parsed scope.
DisallowGarbageCollection no_gc;
Heap* heap = Isolate::Current()->heap();
// We are basically trimming that object to its supertype, so recorded slots
// within the object don't need to be invalidated.
heap->NotifyObjectLayoutChange(data, no_gc, InvalidateRecordedSlots{false},
InvalidateExternalPointerSlots{false});
static_assert(sizeof(UncompiledDataWithoutPreparseData) <
sizeof(UncompiledDataWithPreparseData));
static_assert(sizeof(UncompiledDataWithoutPreparseData) ==
sizeof(UncompiledData));
// Fill the remaining space with filler and clear slots in the trimmed area.
int old_size = data->Size();
DCHECK_LE(sizeof(UncompiledDataWithPreparseData), old_size);
heap->NotifyObjectSizeChange(data, old_size,
sizeof(UncompiledDataWithoutPreparseData),
ClearRecordedSlots{true});
// Swap the map.
data->set_map(heap->isolate(),
GetReadOnlyRoots().uncompiled_data_without_preparse_data_map(),
kReleaseStore);
// Ensure that the clear was successful.
DCHECK(HasUncompiledDataWithoutPreparseData(isolate));
}
void UncompiledData::InitAfterBytecodeFlush(
Isolate* isolate, Tagged<String> inferred_name, int start_position,
int end_position,
std::function<void(Tagged<HeapObject> object, ObjectSlot slot,
Tagged<HeapObject> target)>
gc_notify_updated_slot) {
set_inferred_name(inferred_name);
gc_notify_updated_slot(this, ObjectSlot(&inferred_name_), inferred_name);
set_start_position(start_position);
set_end_position(end_position);
#ifdef V8_ENABLE_SANDBOX
InitAndPublish(isolate);
#endif
}
bool SharedFunctionInfo::is_repl_mode() const {
return IsScript(script()) && Cast<Script>(script())->is_repl_mode();
}
bool SharedFunctionInfo::HasInferredName() {
Tagged<Object> scope_info = name_or_scope_info(kAcquireLoad);
if (IsScopeInfo(scope_info)) {
return Cast<ScopeInfo>(scope_info)->HasInferredFunctionName();
}
return HasUncompiledData(GetCurrentIsolateForSandbox());
}
DEF_GETTER(SharedFunctionInfo, inferred_name, Tagged<String>) {
Tagged<Object> maybe_scope_info = name_or_scope_info(kAcquireLoad);
if (IsScopeInfo(maybe_scope_info)) {
Tagged<ScopeInfo> scope_info = Cast<ScopeInfo>(maybe_scope_info);
if (scope_info->HasInferredFunctionName()) {
Tagged<Object> name = scope_info->InferredFunctionName();
if (IsString(name)) return Cast<String>(name);
}
} else {
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
if (HasUncompiledData(isolate)) {
return uncompiled_data(isolate)->inferred_name();
}
}
return GetReadOnlyRoots().empty_string();
}
bool SharedFunctionInfo::IsUserJavaScript() const {
Tagged<Object> script_obj = script();
if (IsUndefined(script_obj)) return false;
Tagged<Script> script = Cast<Script>(script_obj);
return script->IsUserJavaScript();
}
bool SharedFunctionInfo::IsSubjectToDebugging() const {
#if V8_ENABLE_WEBASSEMBLY
if (HasWasmExportedFunctionData(GetCurrentIsolateForSandbox())) return false;
#endif // V8_ENABLE_WEBASSEMBLY
return IsUserJavaScript();
}
bool SharedFunctionInfo::CanDiscardCompiled(
Tagged<DiscardableData>* out_data) const {
Tagged<Union<Smi, TrustedObject>> data =
GetTrustedData(GetCurrentIsolateForSandbox());
// If the SFI has no trusted data, GetTrustedData() will return Smi::zero().
if (IsSmi(data)) {
return false;
}
Tagged<DiscardableData> discardable_data;
if (!TryCast(data, &discardable_data)) return false;
DCHECK_IMPLIES(
IsCode(discardable_data),
TrustedCast<Code>(discardable_data)->kind() == CodeKind::BASELINE);
if (out_data != nullptr) {
*out_data = discardable_data;
}
return true;
}
bool SharedFunctionInfo::is_class_constructor() const {
return IsClassConstructorBit::decode(flags(kRelaxedLoad));
}
void SharedFunctionInfo::set_are_properties_final(bool value) {
if (is_class_constructor()) {
set_properties_are_final(value);
}
}
bool SharedFunctionInfo::are_properties_final() const {
bool bit = properties_are_final();
return bit && is_class_constructor();
}
Tagged<SharedFunctionInfo> SharedFunctionInfoWrapper::shared_info() const {
return shared_info_.load();
}
void SharedFunctionInfoWrapper::set_shared_info(
Tagged<SharedFunctionInfo> value, WriteBarrierMode mode) {
shared_info_.store(this, value, mode);
}
Tagged<ByteArray> OnHeapBasicBlockProfilerData::block_ids() const {
return block_ids_.load();
}
void OnHeapBasicBlockProfilerData::set_block_ids(Tagged<ByteArray> value,
WriteBarrierMode mode) {
block_ids_.store(this, value, mode);
}
Tagged<ByteArray> OnHeapBasicBlockProfilerData::counts() const {
return counts_.load();
}
void OnHeapBasicBlockProfilerData::set_counts(Tagged<ByteArray> value,
WriteBarrierMode mode) {
counts_.store(this, value, mode);
}
Tagged<ByteArray> OnHeapBasicBlockProfilerData::branches() const {
return branches_.load();
}
void OnHeapBasicBlockProfilerData::set_branches(Tagged<ByteArray> value,
WriteBarrierMode mode) {
branches_.store(this, value, mode);
}
Tagged<String> OnHeapBasicBlockProfilerData::name() const {
return name_.load();
}
void OnHeapBasicBlockProfilerData::set_name(Tagged<String> value,
WriteBarrierMode mode) {
name_.store(this, value, mode);
}
Tagged<String> OnHeapBasicBlockProfilerData::schedule() const {
return schedule_.load();
}
void OnHeapBasicBlockProfilerData::set_schedule(Tagged<String> value,
WriteBarrierMode mode) {
schedule_.store(this, value, mode);
}
Tagged<String> OnHeapBasicBlockProfilerData::code() const {
return code_.load();
}
void OnHeapBasicBlockProfilerData::set_code(Tagged<String> value,
WriteBarrierMode mode) {
code_.store(this, value, mode);
}
Tagged<Smi> OnHeapBasicBlockProfilerData::hash() const { return hash_.load(); }
void OnHeapBasicBlockProfilerData::set_hash(Tagged<Smi> value) {
hash_.store(this, value, SKIP_WRITE_BARRIER);
}
} // namespace v8::internal
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_SHARED_FUNCTION_INFO_INL_H_