current commit
261015.17 · deps: update V8 to 15.6.75.13
// Copyright 2018 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_JS_OBJECTS_INL_H_
#define V8_OBJECTS_JS_OBJECTS_INL_H_

#include "src/objects/js-objects.h"
// Include the non-inl header before the rest of the headers.

#include <optional>

#include "src/common/globals.h"
#include "src/heap/heap-layout-inl.h"
#include "src/heap/heap-write-barrier.h"
#include "src/objects/cpp-heap-external-object.h"
#include "src/objects/cpp-heap-object-wrapper.h"
#include "src/objects/dictionary.h"
#include "src/objects/elements.h"
#include "src/objects/embedder-data-slot-inl.h"
#include "src/objects/feedback-vector.h"
#include "src/objects/field-index-inl.h"
#include "src/objects/fixed-array-base.h"
#include "src/objects/fixed-array.h"
#include "src/objects/fixed-primitive-array.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/heap-number-inl.h"
#include "src/objects/heap-object-field-inl.h"
#include "src/objects/heap-object-inl.h"
#include "src/objects/heap-object.h"
#include "src/objects/instance-type-inl.h"
#include "src/objects/js-interceptor-map-inl.h"
#include "src/objects/keys.h"
#include "src/objects/lookup-inl.h"
#include "src/objects/primitive-heap-object.h"
#include "src/objects/property-array-inl.h"
#include "src/objects/prototype-inl.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/slots.h"
#include "src/objects/smi-inl.h"
#include "src/objects/string.h"
#include "src/objects/swiss-name-dictionary-inl.h"

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8::internal {

Tagged<FixedArrayBase> JSObject::elements() const { return elements_.load(); }

Tagged<FixedArrayBase> JSObject::elements(RelaxedLoadTag) const {
  return elements_.Relaxed_Load();
}

void JSObject::set_elements(Tagged<FixedArrayBase> value,
                            WriteBarrierMode mode) {
  elements_.Relaxed_Store(this, value, mode);
}

MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate,
                                            DirectHandle<JSReceiver> receiver,
                                            DirectHandle<Name> name) {
  LookupIterator it(isolate, receiver, name, receiver);
  if (!it.IsFound()) return it.factory()->undefined_value();
  return Object::GetProperty(&it);
}

MaybeHandle<Object> JSReceiver::GetElement(Isolate* isolate,
                                           DirectHandle<JSReceiver> receiver,
                                           uint32_t index) {
  LookupIterator it(isolate, receiver, index, receiver);
  if (!it.IsFound()) return it.factory()->undefined_value();
  return Object::GetProperty(&it);
}

Handle<Object> JSReceiver::GetDataProperty(Isolate* isolate,
                                           DirectHandle<JSReceiver> object,
                                           DirectHandle<Name> name,
                                           AllowAllocation allow_allocation) {
  LookupIterator it(isolate, object, name, object,
                    LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
  if (!it.IsFound()) return it.factory()->undefined_value();
  return GetDataProperty(&it, allow_allocation);
}

MaybeDirectHandle<JSPrototype> JSReceiver::GetPrototype(
    Isolate* isolate, DirectHandle<JSReceiver> receiver) {
  // We don't expect access checks to be needed on JSProxy objects.
  DCHECK(!IsAccessCheckNeeded(*receiver) || IsJSObject(*receiver));

  PrototypeIterator iter(isolate, receiver, kStartAtReceiver,
                         PrototypeIterator::END_AT_NON_HIDDEN);
  do {
    if (!iter.AdvanceFollowingProxies()) return {};
  } while (!iter.IsAtEnd());
  return PrototypeIterator::GetCurrent(iter);
}

MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate,
                                            DirectHandle<JSReceiver> receiver,
                                            const char* name) {
  DirectHandle<String> str = isolate->factory()->InternalizeUtf8String(name);
  return GetProperty(isolate, receiver, str);
}

// static
V8_WARN_UNUSED_RESULT MaybeDirectHandle<FixedArray> JSReceiver::OwnPropertyKeys(
    Isolate* isolate, DirectHandle<JSReceiver> object) {
  return KeyAccumulator::GetKeys(isolate, object, KeyCollectionMode::kOwnOnly,
                                 ALL_PROPERTIES,
                                 GetKeysConversion::kConvertToString);
}

bool JSObject::PrototypeHasNoElements(Isolate* isolate,
                                      Tagged<JSObject> object) {
  DisallowGarbageCollection no_gc;
  Tagged<HeapObject> prototype = Cast<HeapObject>(object->map()->prototype());
  ReadOnlyRoots roots(isolate);
  Tagged<HeapObject> null = roots.null_value();
  Tagged<FixedArrayBase> empty_fixed_array = roots.empty_fixed_array();
  Tagged<FixedArrayBase> empty_slow_element_dictionary =
      roots.empty_slow_element_dictionary();
  while (prototype != null) {
    Tagged<Map> map = prototype->map();
    if (IsCustomElementsReceiverMap(map)) return false;
    Tagged<FixedArrayBase> elements = Cast<JSObject>(prototype)->elements();
    if (elements != empty_fixed_array &&
        elements != empty_slow_element_dictionary) {
      return false;
    }
    prototype = Cast<HeapObject>(map->prototype());
  }
  return true;
}

Tagged<JSReceiver::PropertiesOrHash> JSReceiver::raw_properties_or_hash()
    const {
  return properties_or_hash_.load();
}
void JSReceiver::set_raw_properties_or_hash(Tagged<PropertiesOrHash> value,
                                            WriteBarrierMode mode) {
  properties_or_hash_.store(this, value, mode);
}
Tagged<JSReceiver::PropertiesOrHash> JSReceiver::raw_properties_or_hash(
    RelaxedLoadTag tag) const {
  return properties_or_hash_.Relaxed_Load();
}
void JSReceiver::set_raw_properties_or_hash(Tagged<PropertiesOrHash> value,
                                            RelaxedStoreTag,
                                            WriteBarrierMode mode) {
  properties_or_hash_.Relaxed_Store(this, value, mode);
}

void JSObject::EnsureCanContainHeapObjectElements(
    Isolate* isolate, DirectHandle<JSObject> object) {
  JSObject::ValidateElements(isolate, *object);
  ElementsKind elements_kind = object->map()->elements_kind();
  if (!IsObjectElementsKind(elements_kind)) {
    if (IsHoleyElementsKind(elements_kind)) {
      TransitionElementsKind(isolate, object, HOLEY_ELEMENTS);
    } else {
      TransitionElementsKind(isolate, object, PACKED_ELEMENTS);
    }
  }
}

template <typename TSlot>
ElementsKind JSObject::GetTransitionedElementsKind(Isolate* isolate,
                                                   ElementsKind current_kind,
                                                   TSlot elements,
                                                   uint32_t count,
                                                   EnsureElementsMode mode) {
  static_assert(std::is_same_v<TSlot, FullObjectSlot> ||
                    std::is_same_v<TSlot, ObjectSlot>,
                "Only ObjectSlot and FullObjectSlot are expected here");
  DisallowGarbageCollection no_gc;
  if (current_kind == HOLEY_ELEMENTS) return current_kind;
  ElementsKind target_kind = current_kind;

  DCHECK(mode != ALLOW_COPIED_DOUBLE_ELEMENTS);
  bool is_holey = IsHoleyElementsKind(current_kind);
  Tagged<Object> the_hole = GetReadOnlyRoots().the_hole_value();
  TSlot end = elements + count;
  for (; elements < end; ++elements) {
    Tagged<Object> current = *elements;
    if (current == the_hole) {
      is_holey = true;
      target_kind = GetHoleyElementsKind(target_kind);
#ifdef V8_ENABLE_UNDEFINED_DOUBLE
    } else if (IsUndefined(current)) {
      if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS) {
        if (IsSmiElementsKind(target_kind)) {
          target_kind = HOLEY_DOUBLE_ELEMENTS;
        } else if (target_kind == PACKED_DOUBLE_ELEMENTS) {
          target_kind = HOLEY_DOUBLE_ELEMENTS;
        } else {
          DCHECK(target_kind == PACKED_ELEMENTS ||
                 target_kind == HOLEY_ELEMENTS ||
                 target_kind == HOLEY_DOUBLE_ELEMENTS);
        }
      } else if (is_holey) {
        if (IsSmiElementsKind(target_kind)) {
          return HOLEY_ELEMENTS;
        }
      } else {
        target_kind = PACKED_ELEMENTS;
      }
#endif  // V8_ENABLE_UNDEFINED_DOUBLE
    } else if (!IsSmi(current)) {
      if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS && IsNumber(current)) {
        if (IsSmiElementsKind(target_kind)) {
          if (is_holey) {
            target_kind = HOLEY_DOUBLE_ELEMENTS;
          } else {
            target_kind = PACKED_DOUBLE_ELEMENTS;
          }
        }
      } else if (is_holey) {
        return HOLEY_ELEMENTS;
      } else {
        target_kind = PACKED_ELEMENTS;
      }
    }
  }

  return target_kind;
}

template <typename TSlot>
void JSObject::EnsureCanContainElements(Isolate* isolate,
                                        DirectHandle<JSObject> object,
                                        TSlot objects, uint32_t count,
                                        EnsureElementsMode mode) {
  static_assert(std::is_same_v<TSlot, FullObjectSlot> ||
                    std::is_same_v<TSlot, ObjectSlot>,
                "Only ObjectSlot and FullObjectSlot are expected here");
  ElementsKind current_kind = object->GetElementsKind();
  ElementsKind target_kind =
      GetTransitionedElementsKind(isolate, current_kind, objects, count, mode);
  if (target_kind != current_kind) {
    TransitionElementsKind(isolate, object, target_kind);
  }
}

void JSObject::EnsureCanContainElements(Isolate* isolate,
                                        DirectHandle<JSObject> object,
                                        DirectHandle<FixedArrayBase> elements,
                                        uint32_t length,
                                        EnsureElementsMode mode) {
  ReadOnlyRoots roots = GetReadOnlyRoots();
  if (elements->map() != roots.fixed_double_array_map()) {
    DCHECK(elements->map() == roots.fixed_array_map() ||
           elements->map() == roots.fixed_cow_array_map());
    if (mode == ALLOW_COPIED_DOUBLE_ELEMENTS) {
      mode = DONT_ALLOW_DOUBLE_ELEMENTS;
    }
    ObjectSlot objects = Cast<FixedArray>(elements)->RawFieldOfFirstElement();
    EnsureCanContainElements(isolate, object, objects, length, mode);
    return;
  }

  DCHECK(mode == ALLOW_COPIED_DOUBLE_ELEMENTS);
  if (object->GetElementsKind() == HOLEY_SMI_ELEMENTS) {
    TransitionElementsKind(isolate, object, HOLEY_DOUBLE_ELEMENTS);
  } else if (object->GetElementsKind() == PACKED_SMI_ELEMENTS) {
    auto double_array = Cast<FixedDoubleArray>(elements);
    for (uint32_t i = 0; i < length; ++i) {
      if (double_array->is_the_hole(i)) {
        TransitionElementsKind(isolate, object, HOLEY_DOUBLE_ELEMENTS);
        return;
      }
    }
    TransitionElementsKind(isolate, object, PACKED_DOUBLE_ELEMENTS);
  }
}

void JSObject::SetMapAndElements(Isolate* isolate,
                                 DirectHandle<JSObject> object,
                                 DirectHandle<Map> new_map,
                                 DirectHandle<FixedArrayBase> value) {
  JSObject::MigrateToMap(isolate, object, new_map);
  DCHECK((object->map()->has_fast_smi_or_object_elements() ||
          (*value == ReadOnlyRoots(isolate).empty_fixed_array()) ||
          object->map()->has_fast_string_wrapper_elements()) ==
         (value->map() == ReadOnlyRoots(isolate).fixed_array_map() ||
          value->map() == ReadOnlyRoots(isolate).fixed_cow_array_map()));
  DCHECK((*value == ReadOnlyRoots(isolate).empty_fixed_array()) ||
         (object->map()->has_fast_double_elements() ==
          IsFixedDoubleArray(*value)));
  object->set_elements(*value);
}

void JSObject::initialize_elements() {
  Tagged<FixedArrayBase> elements = map()->GetInitialElements();
  set_elements(elements, SKIP_WRITE_BARRIER);
}

DEF_GETTER(JSObject, GetIndexedInterceptor, Tagged<InterceptorInfo>) {
  return map()->GetIndexedInterceptor();
}

DEF_GETTER(JSObject, GetNamedInterceptor, Tagged<InterceptorInfo>) {
  return map()->GetNamedInterceptor();
}

// static
int JSObject::GetHeaderSize(Tagged<Map> map) {
  // Check for the most common kind of JavaScript object before
  // falling into the generic switch. This speeds up the internal
  // field operations considerably on average.
  InstanceType instance_type = map->instance_type();
  return instance_type == JS_OBJECT_TYPE ? JSObject::kHeaderSize
                                         : GetHeaderSize(instance_type);
}

// static
int JSObject::GetEmbedderFieldsStartOffset(Tagged<Map> map) {
  // Embedder fields are located after the object header.
  return GetHeaderSize(map);
}

int JSObject::GetEmbedderFieldsStartOffset() {
  return GetEmbedderFieldsStartOffset(map());
}

// static
bool JSObject::MayHaveEmbedderFields(Tagged<Map> map) {
  InstanceType instance_type = map->instance_type();
  // TODO(v8) It'd be nice if all objects with embedder data slots inherited
  // from JSObjectJSAPIObjectWithEmbedderSlotsWithEmbedderSlots, but this is
  // currently not possible due to instance_type constraints.
  return InstanceTypeChecker::IsJSObjectWithEmbedderSlots(instance_type) ||
         InstanceTypeChecker::IsJSAPIObjectWithEmbedderSlots(instance_type) ||
         InstanceTypeChecker::IsJSSpecialObject(instance_type);
}

bool JSObject::MayHaveEmbedderFields() const {
  return MayHaveEmbedderFields(map());
}

// static
int JSObject::GetEmbedderFieldCount(Tagged<Map> map) {
  // We inline some code from Map::instance_size and Map::GetInObjectProperties
  // here, to avoid reading the map's instance size field twice.
  // See https://crbug.com/355120682.
  int instance_size_in_words = map->instance_size_in_words();
  int instance_size = instance_size_in_words << kTaggedSizeLog2;
  if (instance_size == kVariableSizeSentinel) return 0;
  // Embedder fields are located after the object header, whereas in-object
  // properties are located at the end of the object. We don't have to round up
  // the header size here because division by kEmbedderDataSlotSizeInTaggedSlots
  // will swallow potential padding in case of (kTaggedSize !=
  // kSystemPointerSize) anyway.
  int in_object_properties =
      instance_size_in_words - map->GetInObjectPropertiesStartInWords();
  return (((instance_size - GetEmbedderFieldsStartOffset(map)) >>
           kTaggedSizeLog2) -
          in_object_properties) /
         kEmbedderDataSlotSizeInTaggedSlots;
}

int JSObject::GetEmbedderFieldCount() const {
  return GetEmbedderFieldCount(map());
}

int JSObject::GetEmbedderFieldOffset(int index) {
  DCHECK_LT(static_cast<unsigned>(index),
            static_cast<unsigned>(GetEmbedderFieldCount()));
  return GetEmbedderFieldsStartOffset() + (kEmbedderDataSlotSize * index);
}

Tagged<Object> JSObject::GetEmbedderField(int index) {
  return EmbedderDataSlot(this, index).load_tagged();
}

void JSObject::SetEmbedderField(int index, Tagged<Object> value) {
  EmbedderDataSlot::store_tagged(this, index, value);
}

void JSObject::SetEmbedderField(int index, Tagged<Smi> value) {
  EmbedderDataSlot(this, index).store_smi(value);
}

// static
bool JSObject::IsDroppableApiObject(const Tagged<Map> map) {
  auto instance_type = map->instance_type();
  return InstanceTypeChecker::IsJSApiObject(instance_type) ||
         instance_type == JS_SPECIAL_API_OBJECT_TYPE;
}

bool JSObject::IsDroppableApiObject() const {
  return IsDroppableApiObject(map());
}

// Access fast-case object properties at index. The use of these routines
// is needed to correctly distinguish between properties stored in-object and
// properties stored in the properties array.

// Type system violation: the declared return type Tagged<JSAny> is incorrect.
// Property slots can also hold non-JSAny values such as the Hole sentinel
// (`uninitialized_value` during MigrateFastToFast) and class metadata Structs
// (e.g. ClassPositions, AccessorPair, AccessorInfo). The legacy code
// reinterpret-loaded the slot as JSAny without validation; callers dispatch
// on the descriptor's PropertyKind to know what they actually got.
// TODO(jgruber): Change this to return
// Tagged<UnionOf<JSAny, Hole, ClassPositions, AccessorPair, AccessorInfo>>
// (or Tagged<Object>) and update the ~50 callers to Cast<JSAny> (or
// whatever narrower type they actually expect) at the use site.
Tagged<JSAny> JSObject::RawFastPropertyAt(FieldIndex index) const {
  if (index.is_inobject()) {
    return TaggedField<JSAny>::Relaxed_Load(this, index.offset());
  } else {
    return UncheckedCast<JSAny>(
        property_array()->get(index.outobject_array_index()));
  }
}

// The SeqCst versions of RawFastPropertyAt are used for atomically accessing
// shared struct fields.

// See the TODO(jgruber) on the non-SeqCst overload above.
Tagged<JSAny> JSObject::RawFastPropertyAt(FieldIndex index,
                                          SeqCstAccessTag tag) const {
  if (index.is_inobject()) {
    return TaggedField<JSAny>::SeqCst_Load(this, index.offset());
  } else {
    return UncheckedCast<JSAny>(
        property_array()->get(index.outobject_array_index(), tag));
  }
}

std::optional<Tagged<Object>> JSObject::RawInobjectPropertyAt(
    Tagged<Map> original_map, FieldIndex index) const {
  CHECK(index.is_inobject());

  // This method implements a "snapshot" protocol to protect against reading out
  // of bounds of an object. It's used to access a fast in-object property from
  // a background thread with no locking. That caller does have the guarantee
  // that a garbage collection cannot happen during its query. However, it must
  // contend with the main thread altering the object in heavy ways through
  // object migration. Specifically, the object can get smaller. Initially, this
  // may seem benign, because object migration fills the freed-up space with
  // FillerMap words which, even though they offer wrong values, are at
  // least tagged values.

  // However, there is an additional danger. Sweeper threads may discover the
  // filler words and offer that space to the main thread for allocation. Should
  // a HeapNumber be allocated into that space while we're reading a property at
  // that location (from our out-of-date information), we risk interpreting a
  // double value as a pointer. This must be prevented.
  //
  // We do this by:
  //
  // a) Reading the map first
  // b) Reading the property with acquire semantics (but do not inspect it!)
  // c) Re-read the map with acquire semantics.
  //
  // Only if the maps match can the property be inspected. It may have a "wrong"
  // value, but it will be within the bounds of the objects instance size as
  // given by the map and it will be a valid Smi or object pointer.
  Tagged<Object> maybe_tagged_object =
      TaggedField<Object>::Acquire_Load(this, index.offset());
  if (original_map != map(kAcquireLoad)) return {};
  return maybe_tagged_object;
}

void JSObject::RawFastInobjectPropertyAtPut(FieldIndex index,
                                            Tagged<Object> value,
                                            WriteBarrierMode mode) {
  DCHECK(index.is_inobject());
  int offset = index.offset();
  RELAXED_WRITE_FIELD(this, offset, value);
  CONDITIONAL_WRITE_BARRIER(this, offset, value, mode);
}

void JSObject::RawFastInobjectPropertyAtPut(FieldIndex index,
                                            Tagged<Object> value,
                                            SeqCstAccessTag tag) {
  DCHECK(index.is_inobject());
  DCHECK(IsShared(value));
  SEQ_CST_WRITE_FIELD(this, index.offset(), value);
  CONDITIONAL_WRITE_BARRIER(this, index.offset(), value, UPDATE_WRITE_BARRIER);
}

void JSObject::FastPropertyAtPut(FieldIndex index, Tagged<Object> value,
                                 WriteBarrierMode mode) {
  if (index.is_inobject()) {
    RawFastInobjectPropertyAtPut(index, value, mode);
  } else {
    DCHECK_EQ(UPDATE_WRITE_BARRIER, mode);
    property_array()->set(index.outobject_array_index(), value);
  }
}

void JSObject::FastPropertyAtPut(FieldIndex index, Tagged<Object> value,
                                 SeqCstAccessTag tag) {
  if (index.is_inobject()) {
    RawFastInobjectPropertyAtPut(index, value, tag);
  } else {
    property_array()->set(index.outobject_array_index(), value, tag);
  }
}

void JSObject::WriteToField(InternalIndex descriptor, PropertyDetails details,
                            Tagged<Object> value) {
  DCHECK_EQ(PropertyLocation::kField, details.location());
  DCHECK_EQ(PropertyKind::kData, details.kind());
  DisallowGarbageCollection no_gc;
  FieldIndex index = FieldIndex::ForDetails(map(), details);
  if (details.representation().IsDouble()) {
    // Manipulating the signaling NaN used for the hole and uninitialized
    // double field sentinel in C++, e.g. with base::bit_cast or
    // value()/set_value(), will change its value on ia32 (the x87 stack is used
    // to return values and stores to the stack silently clear the signalling
    // bit).
    uint64_t bits;
    if (IsSmi(value)) {
      bits = base::bit_cast<uint64_t>(static_cast<double>(Smi::ToInt(value)));
    } else if (IsUninitializedHole(value)) {
      bits = kHoleNanInt64;
    } else {
      DCHECK(IsHeapNumber(value));
      bits = Cast<HeapNumber>(value)->value_as_bits();
    }
    auto box = Cast<HeapNumber>(RawFastPropertyAt(index));
    box->set_value_as_bits(bits);
  } else {
    FastPropertyAtPut(index, value);
  }
}

Tagged<Object> JSObject::RawFastInobjectPropertyAtSwap(FieldIndex index,
                                                       Tagged<Object> value,
                                                       SeqCstAccessTag tag) {
  DCHECK(index.is_inobject());
  DCHECK(IsShared(value));
  int offset = index.offset();
  Tagged<Object> old_value = SEQ_CST_SWAP_FIELD(this, offset, value);
  CONDITIONAL_WRITE_BARRIER(this, offset, value, UPDATE_WRITE_BARRIER);
  return old_value;
}

Tagged<Object> JSObject::RawFastPropertyAtSwap(FieldIndex index,
                                               Tagged<Object> value,
                                               SeqCstAccessTag tag) {
  if (index.is_inobject()) {
    return RawFastInobjectPropertyAtSwap(index, value, tag);
  }
  return property_array()->Swap(index.outobject_array_index(), value, tag);
}

Tagged<Object> JSObject::RawFastInobjectPropertyAtCompareAndSwap(
    FieldIndex index, Tagged<Object> expected, Tagged<Object> value,
    SeqCstAccessTag tag) {
  DCHECK(index.is_inobject());
  DCHECK(IsShared(value));
  Tagged<Object> previous_value =
      SEQ_CST_COMPARE_AND_SWAP_FIELD(this, index.offset(), expected, value);
  if (previous_value == expected) {
    CONDITIONAL_WRITE_BARRIER(this, index.offset(), value,
                              UPDATE_WRITE_BARRIER);
  }
  return previous_value;
}

Tagged<Object> JSObject::RawFastPropertyAtCompareAndSwapInternal(
    FieldIndex index, Tagged<Object> expected, Tagged<Object> value,
    SeqCstAccessTag tag) {
  if (index.is_inobject()) {
    return RawFastInobjectPropertyAtCompareAndSwap(index, expected, value, tag);
  }
  return property_array()->CompareAndSwap(index.outobject_array_index(),
                                          expected, value, tag);
}

int JSObject::GetInObjectPropertyOffset(int index) {
  return map()->GetInObjectPropertyOffset(index);
}

Tagged<Object> JSObject::InObjectPropertyAtOffset(int offset) {
  DCHECK_GE(offset, GetInObjectPropertyOffset(0));
  DCHECK_LT(offset, Size());
  return TaggedField<Object>::load(this, offset);
}

Tagged<Object> JSObject::InObjectPropertyPutAtIndex(int index,
                                                    Tagged<Object> value,
                                                    WriteBarrierMode mode) {
  // Adjust for the number of properties stored in the object.
  return InObjectPropertyPutAtOffset(GetInObjectPropertyOffset(index), value,
                                     mode);
}

Tagged<Object> JSObject::InObjectPropertyPutAtOffset(int offset,
                                                     Tagged<Object> value,
                                                     WriteBarrierMode mode) {
  DCHECK_GE(offset, GetInObjectPropertyOffset(0));
  DCHECK_LT(offset, Size());
  WRITE_FIELD(this, offset, value);
  CONDITIONAL_WRITE_BARRIER(this, offset, value, mode);
  return value;
}

void JSObject::InitializeBody(Tagged<Map> map, int start_offset,
                              bool is_slack_tracking_in_progress,
                              NewJSObjectType new_js_object_type) {
  const uint32_t size = map->instance_size();
  uint32_t current_offset = start_offset;

#if V8_STATIC_ROOTS_BOOL
  using FillerType = Tagged_t;
  constexpr Tagged_t undefined_filler = StaticReadOnlyRoot::kUndefinedValue;
  constexpr Tagged_t filler_map = StaticReadOnlyRoot::kOnePointerFillerMap;
#else   // !STATIC_ROOTS_BOOL
  using FillerType = Tagged<HeapObject>;
  Tagged<Undefined> undefined_filler = GetReadOnlyRoots().undefined_value();
  Tagged<Map> filler_map = GetReadOnlyRoots().one_pointer_filler_map();
#endif  // !STATIC_ROOTS_BOOL

  // Fills memory at current offset and moves forward the offset.
  const auto FillMemoryTaggedUntil =
      [this, &current_offset, size](uint32_t end_offset, FillerType filler) {
        DCHECK_LE(kHeaderSize, current_offset);
        DCHECK_LE(current_offset, end_offset);
        DCHECK_LE(end_offset, size);
        USE(size);
        // TODO(442989225): This loop should ideally be just a MemsetTagged().
        // Unfortunately, this currently results in performance regressions.
        while (current_offset < end_offset) {
#if V8_STATIC_ROOTS_BOOL
          RELAXED_WRITE_UINT32_FIELD(this, current_offset, filler);
#else   // !STATIC_ROOTS_BOOL
          WRITE_FIELD(this, current_offset, filler);
#endif  // !STATIC_ROOTS_BOOL
          current_offset += kTaggedSize;
        }
        DCHECK_LE(current_offset, size);
      };

  DCHECK_IMPLIES(
      new_js_object_type == NewJSObjectType::kNoEmbedderFieldsAndNoApiWrapper,
      !MayHaveEmbedderFields(map));
  DCHECK_IMPLIES(!MayHaveEmbedderFields(map), GetEmbedderFieldCount(map) == 0);
  if (new_js_object_type != NewJSObjectType::kNoEmbedderFieldsAndNoApiWrapper &&
      MayHaveEmbedderFields(map)) {
    const uint32_t embedder_field_start = GetEmbedderFieldsStartOffset(map);
    // Fill start with references to the undefined value object.
    FillMemoryTaggedUntil(embedder_field_start, undefined_filler);

    // Initialize embedder data slots.
    DCHECK_EQ(current_offset, embedder_field_start);
    for (int i = 0; i < GetEmbedderFieldCount(map); i++) {
      // TODO(v8): consider initializing embedded data slots with Smi::zero().
      EmbedderDataSlot(this, i).Initialize(
          GetReadOnlyRoots().undefined_value());
      current_offset += kEmbedderDataSlotSize;
    }
  }

  DCHECK_LE(current_offset, size);
  if (is_slack_tracking_in_progress) {
    const uint32_t end_of_pre_allocated_offset =
        size - (map->UnusedPropertyFields() * kTaggedSize);
    // Fill pre allocated slots with references to the undefined value object.
    FillMemoryTaggedUntil(end_of_pre_allocated_offset, undefined_filler);
    // Fill the remainder with one word filler objects (ie just a map word)
    FillMemoryTaggedUntil(size, filler_map);
  } else {
    FillMemoryTaggedUntil(size, undefined_filler);
  }
}

template <typename T, template <typename> typename HandleType>
  requires(std::is_convertible_v<HandleType<T>, DirectHandle<T>>)
inline typename HandleType<Object>::MaybeType
JSObject::DefineOwnPropertyIgnoreAttributes(LookupIterator* it,
                                            HandleType<T> value,
                                            PropertyAttributes attributes,
                                            AccessorInfoHandling handling,
                                            EnforceDefineSemantics semantics) {
  MAYBE_RETURN_NULL(DefineOwnPropertyIgnoreAttributes(
      it, value, attributes, Just(ShouldThrow::kThrowOnError), handling,
      semantics));
  return value;
}

void* JSExternalObject::value(ExternalPointerTagRange tag_range) const {
  i::IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
  return value(isolate, tag_range);
}

void* JSExternalObject::value(i::IsolateForSandbox isolate,
                              ExternalPointerTagRange tag_range) const {
  // Accessors take a runtime tag / tag_range (callers pick it dynamically
  // based on v8::External tag values); ExternalPointerMember's template
  // member variants require compile-time tags, so route through the
  // field_address-based free functions instead.
  Address result = ::v8::internal::ReadExternalPointerField(
      value_.storage_address(), isolate, tag_range);
  return reinterpret_cast<void*>(result);
}

void JSExternalObject::init_value(i::IsolateForSandbox isolate,
                                  ExternalPointerTag tag, void* initial_value) {
  ::v8::internal::InitExternalPointerField(
      reinterpret_cast<Address>(this), value_.storage_address(), isolate, tag,
      reinterpret_cast<Address>(initial_value));
}

void JSExternalObject::set_value(i::IsolateForSandbox isolate,
                                 ExternalPointerTag tag, void* value) {
  ::v8::internal::WriteExternalPointerField(
      value_.storage_address(), isolate, tag, reinterpret_cast<Address>(value));
}

Tagged<String> JSStringIterator::string() const { return string_.load(); }

void JSStringIterator::set_string(Tagged<String> value, WriteBarrierMode mode) {
  string_.store(this, value, mode);
}

int JSStringIterator::index() const { return index_.load().value(); }

void JSStringIterator::set_index(int value) {
  index_.store(this, Smi::FromInt(value));
}

Tagged<JSReceiver> JSAsyncFromSyncIterator::sync_iterator() const {
  return sync_iterator_.load();
}

void JSAsyncFromSyncIterator::set_sync_iterator(Tagged<JSReceiver> value,
                                                WriteBarrierMode mode) {
  sync_iterator_.store(this, value, mode);
}

Tagged<Object> JSAsyncFromSyncIterator::next() const { return next_.load(); }

void JSAsyncFromSyncIterator::set_next(Tagged<Object> value,
                                       WriteBarrierMode mode) {
  next_.store(this, value, mode);
}

Tagged<JSAny> JSPrimitiveWrapper::value() const { return value_.load(); }

void JSPrimitiveWrapper::set_value(Tagged<JSAny> value, WriteBarrierMode mode) {
  value_.store(this, value, mode);
}

Tagged<JSReceiver> JSValidIteratorWrapper::underlying_object() const {
  return underlying_object_.load();
}

void JSValidIteratorWrapper::set_underlying_object(Tagged<JSReceiver> value,
                                                   WriteBarrierMode mode) {
  underlying_object_.store(this, value, mode);
}

Tagged<JSAny> JSValidIteratorWrapper::underlying_next() const {
  return underlying_next_.load();
}

void JSValidIteratorWrapper::set_underlying_next(Tagged<JSAny> value,
                                                 WriteBarrierMode mode) {
  underlying_next_.store(this, value, mode);
}

double JSDate::value() const { return value_.value(); }
void JSDate::set_value(double v) { value_.set_value(v); }

#define DEFINE_JSDATE_CACHED_ACCESSOR(name)                       \
  Tagged<UnionOf<Smi, HeapNumber>> JSDate::name() const {         \
    return name##_.load();                                        \
  }                                                               \
  void JSDate::set_##name(Tagged<UnionOf<Smi, HeapNumber>> value, \
                          WriteBarrierMode mode) {                \
    name##_.store(this, value, mode);                             \
  }
DEFINE_JSDATE_CACHED_ACCESSOR(year)
DEFINE_JSDATE_CACHED_ACCESSOR(month)
DEFINE_JSDATE_CACHED_ACCESSOR(day)
DEFINE_JSDATE_CACHED_ACCESSOR(weekday)
DEFINE_JSDATE_CACHED_ACCESSOR(hour)
DEFINE_JSDATE_CACHED_ACCESSOR(min)
DEFINE_JSDATE_CACHED_ACCESSOR(sec)
DEFINE_JSDATE_CACHED_ACCESSOR(cache_stamp)
#undef DEFINE_JSDATE_CACHED_ACCESSOR

bool JSMessageObject::DidEnsureSourcePositionsAvailable() const {
  return shared_info() == Smi::zero();
}

// static
void JSMessageObject::EnsureSourcePositionsAvailable(
    Isolate* isolate, DirectHandle<JSMessageObject> message) {
  if (message->DidEnsureSourcePositionsAvailable()) {
    DCHECK(message->script()->has_line_ends());
  } else {
    JSMessageObject::InitializeSourcePositions(isolate, message);
  }
}

int JSMessageObject::GetStartPosition() const {
  // TODO(cbruni): make this DCHECK stricter (>= 0).
  DCHECK_LE(-1, start_position());
  return start_position();
}

int JSMessageObject::GetEndPosition() const {
  // TODO(cbruni): make this DCHECK stricter (>= 0).
  DCHECK_LE(-1, end_position());
  return end_position();
}

MessageTemplate JSMessageObject::type() const {
  return MessageTemplateFromInt(raw_type());
}

void JSMessageObject::set_type(MessageTemplate value) {
  set_raw_type(static_cast<int>(value));
}

Tagged<Object> JSMessageObject::shared_info() const {
  return shared_info_.load();
}
void JSMessageObject::set_shared_info(Tagged<Object> value,
                                      WriteBarrierMode mode) {
  shared_info_.store(this, Cast<UnionOf<SharedFunctionInfo, Smi>>(value), mode);
}

Tagged<Smi> JSMessageObject::bytecode_offset() const {
  return bytecode_offset_.load();
}
void JSMessageObject::set_bytecode_offset(Tagged<Smi> value) {
  bytecode_offset_.store(this, value);
}

int JSMessageObject::start_position() const {
  return start_position_.load().value();
}
void JSMessageObject::set_start_position(int value) {
  start_position_.store(this, Smi::FromInt(value));
}

int JSMessageObject::end_position() const {
  return end_position_.load().value();
}
void JSMessageObject::set_end_position(int value) {
  end_position_.store(this, Smi::FromInt(value));
}

int JSMessageObject::error_level() const { return error_level_.load().value(); }
void JSMessageObject::set_error_level(int value) {
  error_level_.store(this, Smi::FromInt(value));
}

int JSMessageObject::raw_type() const { return message_type_.load().value(); }
void JSMessageObject::set_raw_type(int value) {
  message_type_.store(this, Smi::FromInt(value));
}

Tagged<Object> JSMessageObject::argument() const { return argument_.load(); }
void JSMessageObject::set_argument(Tagged<Object> value,
                                   WriteBarrierMode mode) {
  argument_.store(this, value, mode);
}

Tagged<Script> JSMessageObject::script() const { return script_.load(); }
void JSMessageObject::set_script(Tagged<Script> value, WriteBarrierMode mode) {
  script_.store(this, value, mode);
}

Tagged<UnionOf<StackTraceInfo, Hole>> JSMessageObject::stack_trace() const {
  return stack_trace_.load();
}
void JSMessageObject::set_stack_trace(
    Tagged<UnionOf<StackTraceInfo, Hole>> value, WriteBarrierMode mode) {
  stack_trace_.store(this, value, mode);
}

DEF_GETTER(JSObject, GetElementsKind, ElementsKind) {
  ElementsKind kind = map()->elements_kind();
#if VERIFY_HEAP && DEBUG
  Tagged<FixedArrayBase> fixed_array = UncheckedCast<FixedArrayBase>(
      TaggedField<HeapObject, offsetof(JSObject, elements_)>::load(this));

  // If a GC was caused while constructing this object, the elements
  // pointer may point to a one pointer filler map.
  if (ElementsAreSafeToExamine()) {
    Tagged<Map> map = fixed_array->map();
    if (IsSmiOrObjectElementsKind(kind)) {
      CHECK(map == GetReadOnlyRoots().fixed_array_map() ||
            map == GetReadOnlyRoots().fixed_cow_array_map());
    } else if (IsDoubleElementsKind(kind)) {
      CHECK(IsFixedDoubleArray(fixed_array) ||
            fixed_array == GetReadOnlyRoots().empty_fixed_array());
    } else if (kind == DICTIONARY_ELEMENTS) {
      CHECK(IsFixedArray(fixed_array));
      CHECK(IsNumberDictionary(fixed_array));
    } else {
      CHECK(kind > DICTIONARY_ELEMENTS || IsAnyNonextensibleElementsKind(kind));
    }
    CHECK_IMPLIES(IsSloppyArgumentsElementsKind(kind),
                  IsSloppyArgumentsElements(elements()));
  }
#endif
  return kind;
}

DEF_GETTER(JSObject, GetElementsAccessor, ElementsAccessor*) {
  return ElementsAccessor::ForKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasObjectElements, bool) {
  return IsObjectElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasSmiElements, bool) {
  return IsSmiElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasSmiOrObjectElements, bool) {
  return IsSmiOrObjectElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasDoubleElements, bool) {
  return IsDoubleElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasHoleyElements, bool) {
  return IsHoleyElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasFastElements, bool) {
  return IsFastElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasFastPackedElements, bool) {
  return IsFastPackedElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasDictionaryElements, bool) {
  return IsDictionaryElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasPackedElements, bool) {
  return GetElementsKind() == PACKED_ELEMENTS;
}

DEF_GETTER(JSObject, HasAnyNonextensibleElements, bool) {
  return IsAnyNonextensibleElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasSealedElements, bool) {
  return IsSealedElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasSharedArrayElements, bool) {
  return GetElementsKind() == SHARED_ARRAY_ELEMENTS;
}

DEF_GETTER(JSObject, HasNonextensibleElements, bool) {
  return IsNonextensibleElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasFastArgumentsElements, bool) {
  return IsFastArgumentsElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasSlowArgumentsElements, bool) {
  return IsSlowArgumentsElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasSloppyArgumentsElements, bool) {
  return IsSloppyArgumentsElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasStringWrapperElements, bool) {
  return IsStringWrapperElementsKind(GetElementsKind());
}

DEF_GETTER(JSObject, HasFastStringWrapperElements, bool) {
  return GetElementsKind() == FAST_STRING_WRAPPER_ELEMENTS;
}

DEF_GETTER(JSObject, HasSlowStringWrapperElements, bool) {
  return GetElementsKind() == SLOW_STRING_WRAPPER_ELEMENTS;
}

DEF_GETTER(JSObject, HasTypedArrayOrRabGsabTypedArrayElements, bool) {
  DCHECK(!elements().is_null());
  return map()->has_typed_array_or_rab_gsab_typed_array_elements();
}

#define FIXED_TYPED_ELEMENTS_CHECK(Type, type, TYPE, ctype) \
  DEF_GETTER(JSObject, HasFixed##Type##Elements, bool) {    \
    return map()->elements_kind() == TYPE##_ELEMENTS;       \
  }

TYPED_ARRAYS(FIXED_TYPED_ELEMENTS_CHECK)

#undef FIXED_TYPED_ELEMENTS_CHECK

DEF_GETTER(JSObject, HasNamedInterceptor, bool) {
  return map()->has_named_interceptor();
}

DEF_GETTER(JSObject, HasIndexedInterceptor, bool) {
  return map()->has_indexed_interceptor();
}

Tagged<JSGlobalProxy> JSGlobalObject::global_proxy() const {
  return global_proxy_.load();
}
void JSGlobalObject::set_global_proxy(Tagged<JSGlobalProxy> value,
                                      WriteBarrierMode mode) {
  global_proxy_.store(this, value, mode);
}

Tagged<JSGlobalProxy> JSGlobalObject::global_proxy_for_api() const {
  return global_proxy_for_api_.load();
}
void JSGlobalObject::set_global_proxy_for_api(Tagged<JSGlobalProxy> value,
                                              WriteBarrierMode mode) {
  global_proxy_for_api_.store(this, value, mode);
}

Tagged<GlobalDictionary> JSGlobalObject::global_dictionary(
    AcquireLoadTag) const {
  DCHECK(!HasFastProperties());
  return Cast<GlobalDictionary>(properties_or_hash_.Acquire_Load());
}
void JSGlobalObject::set_global_dictionary(Tagged<GlobalDictionary> value,
                                           ReleaseStoreTag,
                                           WriteBarrierMode mode) {
  properties_or_hash_.Release_Store(this, Cast<FixedArrayBase>(value), mode);
}

Tagged<HeapObject> JSGlobalObject::raw_global_proxy() const {
  return Cast<HeapObject>(global_proxy_.load());
}

DEF_GETTER(JSObject, element_dictionary, Tagged<NumberDictionary>) {
  DCHECK(HasDictionaryElements() || HasSlowStringWrapperElements());
  return Cast<NumberDictionary>(elements());
}

void JSReceiver::initialize_properties(Isolate* isolate) {
  ReadOnlyRoots roots(isolate);
  DCHECK(!HeapLayout::InYoungGeneration(roots.empty_fixed_array()));
  DCHECK(!HeapLayout::InYoungGeneration(roots.empty_property_dictionary()));
  DCHECK(!HeapLayout::InYoungGeneration(
      roots.empty_ordered_property_dictionary()));
  if (map()->is_dictionary_map()) {
    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
      properties_or_hash_.store_no_write_barrier(
          roots.empty_swiss_property_dictionary());
    } else {
      properties_or_hash_.store_no_write_barrier(
          roots.empty_property_dictionary());
    }
  } else {
    properties_or_hash_.store_no_write_barrier(roots.empty_fixed_array());
  }
}

DEF_GETTER(JSReceiver, HasFastProperties, bool) {
  Tagged<JSReceiver::PropertiesOrHash> properties_or_hash_obj =
      raw_properties_or_hash(kRelaxedLoad);
  DCHECK(IsSmi(properties_or_hash_obj) ||
         ((IsGlobalDictionary(properties_or_hash_obj) ||
           IsPropertyDictionary(properties_or_hash_obj)) ==
          map()->is_dictionary_map()));
  USE(properties_or_hash_obj);
  return !map()->is_dictionary_map();
}

DEF_GETTER(JSReceiver, property_dictionary, Tagged<NameDictionary>) {
  DCHECK(!Is<JSGlobalObject>(this));
  DCHECK(!HasFastProperties());
  DCHECK(!V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL);

  Tagged<JSReceiver::PropertiesOrHash> prop = raw_properties_or_hash();
  if (IsSmi(prop)) {
    return GetReadOnlyRoots().empty_property_dictionary();
  }
  return Cast<NameDictionary>(prop);
}

DEF_GETTER(JSReceiver, property_dictionary_swiss, Tagged<SwissNameDictionary>) {
  DCHECK(!Is<JSGlobalObject>(this));
  DCHECK(!HasFastProperties());
  DCHECK(V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL);

  Tagged<JSReceiver::PropertiesOrHash> prop = raw_properties_or_hash();
  if (IsSmi(prop)) {
    return GetReadOnlyRoots().empty_swiss_property_dictionary();
  }
  return Cast<SwissNameDictionary>(prop);
}

// TODO(gsathya): Pass isolate directly to this function and access
// the heap from this.
DEF_GETTER(JSReceiver, property_array, Tagged<PropertyArray>) {
  DCHECK(HasFastProperties());
  Tagged<JSReceiver::PropertiesOrHash> prop = raw_properties_or_hash();
  if (IsSmi(prop) || prop == GetReadOnlyRoots().empty_fixed_array()) {
    return GetReadOnlyRoots().empty_property_array();
  }
  return Cast<PropertyArray>(prop);
}

void JSObject::EnsureWritableFastElements(Isolate* isolate,
                                          DirectHandle<JSObject> object) {
  DCHECK(object->HasSmiOrObjectElements() ||
         object->HasFastStringWrapperElements() ||
         object->HasAnyNonextensibleElements());
  Tagged<FixedArray> raw_elems = Cast<FixedArray>(object->elements());
  if (V8_UNLIKELY(raw_elems->map() ==
                  ReadOnlyRoots(isolate).fixed_cow_array_map())) {
    MakeElementsWritable(isolate, object);
  }
}

std::optional<Tagged<NativeContext>> JSReceiver::GetCreationContext() const {
  DisallowGarbageCollection no_gc;
  Tagged<Map> meta_map = map()->map();
  DCHECK(IsMapMap(meta_map));
  Tagged<Object> maybe_native_context = meta_map->native_context_or_null();
  if (V8_UNLIKELY(IsNull(maybe_native_context))) return {};
  DCHECK(IsNativeContext(maybe_native_context));
  return Cast<NativeContext>(maybe_native_context);
}

MaybeDirectHandle<NativeContext> JSReceiver::GetCreationContext(
    Isolate* isolate) const {
  DisallowGarbageCollection no_gc;
  std::optional<Tagged<NativeContext>> maybe_context = GetCreationContext();
  if (!maybe_context.has_value()) return {};
  return direct_handle(maybe_context.value(), isolate);
}

Maybe<bool> JSReceiver::HasProperty(Isolate* isolate,
                                    DirectHandle<JSReceiver> object,
                                    DirectHandle<Name> name) {
  return HasPropertyOrElement(isolate, object, PropertyKey(isolate, name));
}

Maybe<bool> JSReceiver::HasElement(Isolate* isolate,
                                   DirectHandle<JSReceiver> object,
                                   uint32_t index) {
  LookupIterator it(isolate, object, index, object);
  return HasProperty(&it);
}

Maybe<bool> JSReceiver::HasPropertyOrElement(Isolate* isolate,
                                             DirectHandle<JSReceiver> object,
                                             PropertyKey key) {
  LookupIterator it(isolate, object, key, object);
  return HasProperty(&it);
}

Maybe<bool> JSReceiver::HasOwnProperty(Isolate* isolate,
                                       DirectHandle<JSReceiver> object,
                                       size_t index) {
  if (IsJSObject(*object)) {  // Shortcut.
    LookupIterator it(isolate, object, index, object, LookupIterator::OWN);
    return HasProperty(&it);
  }

  Maybe<PropertyAttributes> attributes =
      JSReceiver::GetOwnPropertyAttributes(isolate, object, index);
  MAYBE_RETURN(attributes, Nothing<bool>());
  return Just(attributes.FromJust() != ABSENT);
}

Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes(
    Isolate* isolate, DirectHandle<JSReceiver> object,
    DirectHandle<Name> name) {
  PropertyKey key(isolate, name);
  LookupIterator it(isolate, object, key, object);
  return GetPropertyAttributes(&it);
}

Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes(
    Isolate* isolate, DirectHandle<JSReceiver> object,
    DirectHandle<Name> name) {
  PropertyKey key(isolate, name);
  LookupIterator it(isolate, object, key, object, LookupIterator::OWN);
  return GetPropertyAttributes(&it);
}

Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes(
    Isolate* isolate, DirectHandle<JSReceiver> object, size_t index) {
  LookupIterator it(isolate, object, index, object, LookupIterator::OWN);
  return GetPropertyAttributes(&it);
}

Maybe<PropertyAttributes> JSReceiver::GetElementAttributes(
    Isolate* isolate, DirectHandle<JSReceiver> object, uint32_t index) {
  LookupIterator it(isolate, object, index, object);
  return GetPropertyAttributes(&it);
}

Maybe<PropertyAttributes> JSReceiver::GetOwnElementAttributes(
    Isolate* isolate, DirectHandle<JSReceiver> object, uint32_t index) {
  LookupIterator it(isolate, object, index, object, LookupIterator::OWN);
  return GetPropertyAttributes(&it);
}

Tagged<NativeContext> JSGlobalObject::native_context() {
  return *GetCreationContext();
}

bool JSGlobalObject::IsDetached() {
  return global_proxy()->IsDetachedFrom(this);
}

bool JSGlobalProxy::IsDetachedFrom(Tagged<JSGlobalObject> global) const {
  return map()->prototype() != global;
}

bool JSGlobalProxy::IsDetached() const {
  // Currently we expect a non-detached global proxy to have a non-null
  // hidden prototype.
  bool is_detached = IsNull(map()->prototype());
  DCHECK_IMPLIES(is_detached, !GetCreationContext().has_value());
  return is_detached;
}

inline int JSGlobalProxy::SizeWithEmbedderFields(int embedder_field_count) {
  DCHECK_GE(embedder_field_count, 0);
  return kHeaderSize + embedder_field_count * kEmbedderDataSlotSize;
}

ACCESSORS(JSIteratorResult, value, Tagged<Object>, kValueOffset)
ACCESSORS(JSIteratorResult, done, Tagged<Object>, kDoneOffset)

ACCESSORS(JSUint8ArraySetFromResult, read, Tagged<Object>, kReadOffset)
ACCESSORS(JSUint8ArraySetFromResult, written, Tagged<Object>, kWrittenOffset)

// If the fast-case backing storage takes up much more memory than a dictionary
// backing storage would, the object should have slow elements.
// static
static inline bool ShouldConvertToSlowElements(uint32_t used_elements,
                                               uint32_t new_capacity) {
  uint32_t size_threshold = NumberDictionary::kPreferFastElementsSizeFactor *
                            NumberDictionary::ComputeCapacity(used_elements) *
                            NumberDictionary::kEntrySize;
  return size_threshold <= new_capacity;
}

static inline bool ShouldConvertToSlowElements(Tagged<JSObject> object,
                                               uint32_t capacity,
                                               uint32_t index,
                                               uint32_t* new_capacity) {
  static_assert(JSObject::kMaxUncheckedOldFastElementsLength <=
                JSObject::kMaxUncheckedFastElementsLength);
  if (index < capacity) {
    *new_capacity = capacity;
    return false;
  }
  if (index - capacity >= JSObject::kMaxGap) return true;
  *new_capacity = JSObject::NewElementsCapacity(index + 1);
  DCHECK_LT(index, *new_capacity);
  if (*new_capacity <= JSObject::kMaxUncheckedOldFastElementsLength ||
      (*new_capacity <= JSObject::kMaxUncheckedFastElementsLength &&
       HeapLayout::InYoungGeneration(object))) {
    return false;
  }
  return ShouldConvertToSlowElements(object->GetFastElementsUsage(),
                                     *new_capacity);
}

}  // namespace v8::internal

#include "src/objects/object-macros-undef.h"

#endif  // V8_OBJECTS_JS_OBJECTS_INL_H_