current commit
261026.12 · deps: patch V8 to 15.6.75.25
// 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 {
  if (Tagged<ScopeInfo> info;
      TryCast(name_or_scope_info(kAcquireLoad), &info)) {
    return info->CanOnlyAccessFixedFormalParameters();
  }
  return false;
}

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 {
  if (!is_toplevel()) return false;
  Tagged<ScopeInfo> info = scope_info(kAcquireLoad);
  return info->is_script_scope() && info->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)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, relaxed_flags,
                    class_initializer_allows_await,
                    SharedFunctionInfo::ClassInitializerAllowsAwaitBit)

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);
}

bool SharedFunctionInfo::HasScopeInfo() const {
  return HasScopeInfo(kAcquireLoad);
}

bool SharedFunctionInfo::HasScopeInfo(AcquireLoadTag tag) const {
  return IsScopeInfo(name_or_scope_info(tag));
}

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 {
  if (Tagged<ScopeInfo> info;
      TryCast(name_or_scope_info(kAcquireLoad), &info)) {
    DCHECK_IMPLIES(info->HasOuterScopeInfo(),
                   !info->OuterScopeInfo()->IsEmpty());
    return info->HasOuterScopeInfo();
  }
  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;
}

Tagged<ScopeInfo> SharedFunctionInfo::GetOuterScopeInfo() const {
  DCHECK(HasOuterScopeInfo());
  if (Tagged<ScopeInfo> info;
      TryCast(name_or_scope_info(kAcquireLoad), &info)) {
    return info->OuterScopeInfo();
  }
  return Cast<ScopeInfo>(outer_scope_info());
}

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(!HasScopeInfo());
  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_