current commit
261007.21 · deps: update V8 to 15.5.35.20
// 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_FIXED_ARRAY_H_
#define V8_OBJECTS_FIXED_ARRAY_H_

#include <optional>

#include "src/base/strong-alias.h"
#include "src/common/globals.h"
#include "src/handles/maybe-handles.h"
#include "src/objects/fixed-array-base.h"
#include "src/objects/heap-object.h"
#include "src/objects/instance-type.h"
#include "src/objects/objects.h"
#include "src/objects/smi.h"
#include "src/objects/tagged.h"
#include "src/objects/trusted-object.h"
#include "src/roots/roots.h"

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

namespace v8::internal {

template <typename T>
concept HasCapacity = requires(T a) { a.capacity_; };
template <typename T>
concept HasLength = requires(T a) { a.length_; };

template <typename T>
constexpr bool CheckFirstField() {
  static_assert(sizeof(TrustedObject) == sizeof(HeapObject));
  if constexpr (HasCapacity<T>) {
    return offsetof(T, capacity_) == sizeof(HeapObject) &&
           std::is_same_v<decltype(std::declval<T>().capacity_), uint32_t>;
  } else {
    static_assert(HasLength<T>);
    return offsetof(T, length_) == sizeof(HeapObject) &&
           std::is_same_v<decltype(std::declval<T>().length_), uint32_t>;
  }
}

template <typename T>
concept TaggedArrayBaseCompatible = requires(T a) {
  requires CheckFirstField<T>();
  a.objects();
  requires std::is_base_of_v<TaggedMemberBase,
                             std::remove_reference_t<decltype(a.objects()[0])>>;
};

// The Super template parameter names the concrete base class (e.g.
// (Trusted)FixedArrayBase) from which subclasses inherit the length_ field.
// Classes with a capacity_ + length_ layout pass HeapObject and declare both
// fields themselves.
V8_OBJECT template <class Derived, typename ElementT_,
                    class Super = FixedArrayBase>
class TaggedArrayBase : public Super {
 private:
  V8_INLINE Derived* derived() { return static_cast<Derived*>(this); }
  V8_INLINE const Derived* derived() const {
    return static_cast<const Derived*>(this);
  }

  uint32_t& capacity_field() {
    if constexpr (HasCapacity<Derived>) {
      return derived()->capacity_;
    } else {
      return derived()->length_;
    }
  }
  const uint32_t& capacity_field() const {
    if constexpr (HasCapacity<Derived>) {
      return derived()->capacity_;
    } else {
      return derived()->length_;
    }
  }

  static_assert(std::is_base_of_v<HeapObject, Super>);

  static_assert(sizeof(TaggedMember<ElementT_>) == kTaggedSize);
  static_assert(is_subtype_v<ElementT_, MaybeObject>);

  template <typename T>
  static constexpr bool kSupportsSmiElements = std::is_convertible_v<Smi, T>;

  static constexpr WriteBarrierMode kDefaultMode =
      std::is_same_v<ElementT_, Smi> ? SKIP_WRITE_BARRIER
                                     : UPDATE_WRITE_BARRIER;

 public:
  using ElementT = ElementT_;
  using ElementMemberT = TaggedMember<ElementT>;
  static constexpr bool kElementsAreMaybeObject = is_maybe_weak_v<ElementT>;
  static constexpr int kElementSize = kTaggedSize;

 private:
  using SlotType =
      std::conditional_t<kElementsAreMaybeObject, MaybeObjectSlot, ObjectSlot>;

 public:
  inline SafeHeapObjectSize capacity() const {
    return SafeHeapObjectSize(capacity_field());
  }
  inline SafeHeapObjectSize capacity(RelaxedLoadTag) const {
    return SafeHeapObjectSize(
        base::AsAtomic32::Relaxed_Load(&capacity_field()));
  }
  inline SafeHeapObjectSize capacity(AcquireLoadTag) const {
    return SafeHeapObjectSize(
        base::AsAtomic32::Acquire_Load(&capacity_field()));
  }
  inline void set_capacity(uint32_t value) { capacity_field() = value; }
  inline void set_capacity(uint32_t value, ReleaseStoreTag) {
    base::AsAtomic32::Release_Store(&capacity_field(), value);
  }

  // length() / set_length() are inherited from (Trusted)FixedArrayBase.

  inline void clear_optional_padding() {
    if constexpr (requires { derived()->optional_padding_; }) {
      derived()->optional_padding_ = 0;
    }
  }

  // Index is never supposed to be negative.
  // See https://crbug.com/441221573.

  inline Tagged<ElementT> get(uint32_t index) const;
  inline Tagged<ElementT> get(uint32_t index, RelaxedLoadTag) const;
  inline Tagged<ElementT> get(uint32_t index, AcquireLoadTag) const;
  inline Tagged<ElementT> get(uint32_t index, SeqCstAccessTag) const;

  inline void set(uint32_t index, Tagged<ElementT> value,
                  WriteBarrierMode mode = kDefaultMode);
  template <typename T = ElementT,
            typename = std::enable_if<kSupportsSmiElements<T>>>
  inline void set(uint32_t index, Tagged<Smi> value);
  inline void set(uint32_t index, Tagged<ElementT> value, RelaxedStoreTag,
                  WriteBarrierMode mode = kDefaultMode);
  template <typename T = ElementT,
            typename = std::enable_if<kSupportsSmiElements<T>>>
  inline void set(uint32_t index, Tagged<Smi> value, RelaxedStoreTag);
  inline void set(uint32_t index, Tagged<ElementT> value, ReleaseStoreTag,
                  WriteBarrierMode mode = kDefaultMode);
  template <typename T = ElementT,
            typename = std::enable_if<kSupportsSmiElements<T>>>
  inline void set(uint32_t index, Tagged<Smi> value, ReleaseStoreTag);
  inline void set(uint32_t index, Tagged<ElementT> value, SeqCstAccessTag,
                  WriteBarrierMode mode = kDefaultMode);
  template <typename T = ElementT,
            typename = std::enable_if<kSupportsSmiElements<T>>>
  inline void set(uint32_t index, Tagged<Smi> value, SeqCstAccessTag);

  inline Tagged<ElementT> swap(uint32_t index, Tagged<ElementT> value,
                               SeqCstAccessTag,
                               WriteBarrierMode mode = kDefaultMode);
  inline Tagged<ElementT> compare_and_swap(
      uint32_t index, Tagged<ElementT> expected, Tagged<ElementT> value,
      SeqCstAccessTag, WriteBarrierMode mode = kDefaultMode);

  // Move vs. Copy behaves like memmove vs. memcpy: for Move, the memory
  // regions may overlap, for Copy they must not overlap.
  inline static void MoveElements(Isolate* isolate, Tagged<Derived> dst,
                                  uint32_t dst_index, Tagged<Derived> src,
                                  uint32_t src_index, uint32_t len,
                                  WriteBarrierMode mode = kDefaultMode);
  inline static void CopyElements(Isolate* isolate, Tagged<Derived> dst,
                                  uint32_t dst_index, Tagged<Derived> src,
                                  uint32_t src_index, uint32_t len,
                                  WriteBarrierMode mode = kDefaultMode);

  // Right-trim the array.
  // Invariant: 0 < new_length <= length()
  inline void RightTrim(Isolate* isolate, uint32_t new_capacity);

  inline int AllocatedSize() const;
  static constexpr int SizeFor(int capacity);
  static constexpr int OffsetOfElementAt(int index);

  // Gives access to raw memory which stores the array's data.
  inline SlotType RawFieldOfFirstElement() const;
  inline SlotType RawFieldOfElementAt(uint32_t index) const;

  // Maximal allowed capacity, in number of elements. Chosen s.t. the byte size
  // fits into a Smi which is necessary for being able to create a free space
  // filler.
  static constexpr uint32_t kMaxCapacity = kMaxFixedArrayCapacity;

  static constexpr int MaxRegularCapacity();

 protected:
  template <class IsolateT>
  static Handle<Derived> Allocate(
      IsolateT* isolate, uint32_t capacity,
      std::optional<DisallowGarbageCollection>* no_gc_out,
      AllocationType allocation = AllocationType::kYoung,
      AllocationHint hint = AllocationHint());

  static constexpr uint32_t NewCapacityForIndex(uint32_t index,
                                                uint32_t old_capacity);

  inline bool IsInBounds(int index) const;
  inline bool IsCowArray() const;
} V8_OBJECT_END;

template <class Derived, typename ElementT, class Super>
constexpr int TaggedArrayBase<Derived, ElementT, Super>::SizeFor(int capacity) {
  static_assert(TaggedArrayBaseCompatible<Derived>);
  return OFFSET_OF_DATA_START(Derived) + capacity * kElementSize;
}

template <class Derived, typename ElementT, class Super>
constexpr int TaggedArrayBase<Derived, ElementT, Super>::OffsetOfElementAt(
    int index) {
  return SizeFor(index);
}

template <class Derived, typename ElementT, class Super>
constexpr int TaggedArrayBase<Derived, ElementT, Super>::MaxRegularCapacity() {
  return (kMaxRegularHeapObjectSize - OFFSET_OF_DATA_START(Derived)) /
         kElementSize;
}

// FixedArray describes fixed-sized arrays with element type Object.
V8_OBJECT class FixedArray
    : public TaggedArrayBase<FixedArray, Object, FixedArrayBase> {
  V8_IT_OWN_TYPE;
  using Super = TaggedArrayBase<FixedArray, Object, FixedArrayBase>;

 public:
  static constexpr RootIndex kMapRootIndex = RootIndex::kFixedArrayMap;
  using ElementMemberT = TaggedMember<Object>;

 public:
  template <class IsolateT>
  static inline Handle<FixedArray> New(
      IsolateT* isolate, uint32_t length,
      AllocationType allocation = AllocationType::kYoung,
      AllocationHint hint = AllocationHint());
  template <class IsolateT, typename ElementsCallback>
  static inline Handle<FixedArray> New(
      IsolateT* isolate, uint32_t length, ElementsCallback elements_callback,
      AllocationType allocation = AllocationType::kYoung,
      AllocationHint hint = AllocationHint());

  using Super::CopyElements;
  using Super::MoveElements;

  // TODO(jgruber): Only needed for FixedArrays used as JSObject elements.
  inline void MoveElements(Isolate* isolate, uint32_t dst_index,
                           uint32_t src_index, uint32_t len,
                           WriteBarrierMode mode);
  inline void CopyElements(Isolate* isolate, uint32_t dst_index,
                           Tagged<FixedArray> src, uint32_t src_index,
                           uint32_t len, WriteBarrierMode mode);

  // Return a grown copy if the index is bigger than the array's length.
  template <template <typename> typename HandleType>
    requires(
        std::is_convertible_v<HandleType<FixedArray>, DirectHandle<FixedArray>>)
  V8_EXPORT_PRIVATE static HandleType<FixedArray> SetAndGrow(
      Isolate* isolate, HandleType<FixedArray> array, uint32_t index,
      DirectHandle<Object> value);
  template <template <typename> typename HandleType>
    requires(
        std::is_convertible_v<HandleType<FixedArray>, DirectHandle<FixedArray>>)
  V8_EXPORT_PRIVATE static HandleType<FixedArray> SetAndGrow(
      Isolate* isolate, HandleType<FixedArray> array, uint32_t index,
      Tagged<Smi> value);

  // Right-trim the array.
  // Invariant: 0 < new_length <= length()
  V8_EXPORT_PRIVATE void RightTrim(Isolate* isolate, uint32_t new_capacity);
  // Right-trims the array, and canonicalizes length 0 to empty_fixed_array.
  template <template <typename> typename HandleType>
    requires(
        std::is_convertible_v<HandleType<FixedArray>, DirectHandle<FixedArray>>)
  static HandleType<FixedArray> RightTrimOrEmpty(Isolate* isolate,
                                                 HandleType<FixedArray> array,
                                                 uint32_t new_length);

  // TODO(jgruber): Only needed for FixedArrays used as JSObject elements.
  inline void FillWithHoles(uint32_t from, uint32_t to);

  // For compatibility with FixedDoubleArray:
  // TODO(jgruber): Only needed for FixedArrays used as JSObject elements.
  inline bool is_the_hole(Isolate* isolate, uint32_t index);
  inline void set_the_hole(Isolate* isolate, uint32_t index);
  inline void set_the_hole(ReadOnlyRoots ro_roots, uint32_t index);

  DECL_PRINTER(FixedArray)
  DECL_VERIFIER(FixedArray)

  class BodyDescriptor;

  static constexpr uint32_t kMaxLength = Super::kMaxCapacity;
  static constexpr int kMaxRegularLength =
      (kMaxRegularHeapObjectSize -
       (sizeof(HeapObject) +
        (TAGGED_SIZE_8_BYTES ? kTaggedSize : kUInt32Size))) /
      kTaggedSize;

 private:
  template <template <typename> typename HandleType>
    requires(
        std::is_convertible_v<HandleType<FixedArray>, DirectHandle<FixedArray>>)
  static HandleType<FixedArray> Grow(Isolate* isolate,
                                     HandleType<FixedArray> array,
                                     uint32_t index);

  inline static Handle<FixedArray> Resize(
      Isolate* isolate, DirectHandle<FixedArray> xs, uint32_t new_capacity,
      AllocationType allocation = AllocationType::kYoung,
      WriteBarrierMode mode = UPDATE_WRITE_BARRIER);

 public:
  // length_ / optional_padding_ live in FixedArrayBase.
  FLEXIBLE_ARRAY_MEMBER(TaggedMember<Object>, objects);
} V8_OBJECT_END;

static_assert(sizeof(FixedArray) == Internals::kFixedArrayHeaderSize);

// A FixedArray in trusted space and with a unique instance type.
//
// Note: while the array itself is trusted, it contains tagged pointers into
// the main pointer compression heap and therefore to _untrusted_ objects.
// If you are storing references to other trusted object (i.e. protected
// pointers), use ProtectedFixedArray.
V8_OBJECT class TrustedFixedArray
    : public TaggedArrayBase<TrustedFixedArray, Object, TrustedFixedArrayBase> {
  using Super =
      TaggedArrayBase<TrustedFixedArray, Object, TrustedFixedArrayBase>;

 public:
  static constexpr RootIndex kMapRootIndex = RootIndex::kTrustedFixedArrayMap;
  using ElementMemberT = TaggedMember<Object>;

 public:
  template <class IsolateT>
  static inline Handle<TrustedFixedArray> New(
      IsolateT* isolate, uint32_t capacity,
      AllocationType allocation = AllocationType::kTrusted);

  DECL_PRINTER(TrustedFixedArray)
  DECL_VERIFIER(TrustedFixedArray)

  class BodyDescriptor;

  static constexpr uint32_t kMaxLength = Super::kMaxCapacity;
  static constexpr int kMaxRegularLength =
      (kMaxRegularHeapObjectSize -
       (sizeof(TrustedObject) +
        (TAGGED_SIZE_8_BYTES ? kTaggedSize : kUInt32Size))) /
      kTaggedSize;

 public:
  // length_ / optional_padding_ live in TrustedFixedArrayBase.
  FLEXIBLE_ARRAY_MEMBER(TaggedMember<Object>, objects);
} V8_OBJECT_END;

// A FixedArray in trusted space, holding protected pointers (to other trusted
// objects). If you want to store JS-heap references, use TrustedFixedArray.
// ProtectedFixedArray has a unique instance type.
V8_OBJECT class ProtectedFixedArray
    : public TaggedArrayBase<ProtectedFixedArray, UnionOf<TrustedObject, Smi>,
                             TrustedFixedArrayBase> {
  using Super =
      TaggedArrayBase<ProtectedFixedArray, UnionOf<TrustedObject, Smi>,
                      TrustedFixedArrayBase>;

 public:
  static constexpr RootIndex kMapRootIndex = RootIndex::kProtectedFixedArrayMap;
  using ElementMemberT = ProtectedTaggedMember<UnionOf<TrustedObject, Smi>>;

 public:
  // Allocate a new ProtectedFixedArray of the given capacity, initialized with
  // Smi::zero().
  template <class IsolateT>
  static inline Handle<ProtectedFixedArray> New(IsolateT* isolate,
                                                uint32_t capacity);

  DECL_PRINTER(ProtectedFixedArray)
  DECL_VERIFIER(ProtectedFixedArray)

  class BodyDescriptor;

  static constexpr uint32_t kMaxLength = Super::kMaxCapacity;
  static constexpr int kMaxRegularLength =
      (kMaxRegularHeapObjectSize -
       (sizeof(TrustedObject) +
        (TAGGED_SIZE_8_BYTES ? kTaggedSize : kUInt32Size))) /
      kTaggedSize;

 public:
  // length_ / optional_padding_ live in TrustedFixedArrayBase.
  FLEXIBLE_ARRAY_MEMBER(ElementMemberT, objects);
} V8_OBJECT_END;

// FixedArray alias added only because of IsFixedArrayExact() predicate, which
// checks for the exact instance type FIXED_ARRAY_TYPE instead of a range
// check: [FIRST_FIXED_ARRAY_TYPE, LAST_FIXED_ARRAY_TYPE].
V8_OBJECT
class FixedArrayExact final : public FixedArray {
  V8_IT_NO_AUTO_CHECKER;
} V8_OBJECT_END;

// WeakFixedArray describes fixed-sized arrays with element type
// Tagged<MaybeObject>.
V8_OBJECT class WeakFixedArray
    : public TaggedArrayBase<WeakFixedArray, MaybeObject, FixedArrayBase> {
  V8_IT_OWN_TYPE;
  using Super = TaggedArrayBase<WeakFixedArray, MaybeObject, FixedArrayBase>;

 public:
  static constexpr RootIndex kMapRootIndex = RootIndex::kWeakFixedArrayMap;
  using ElementMemberT = TaggedMember<MaybeObject>;

 public:
  template <class IsolateT>
  static inline Handle<WeakFixedArray> New(
      IsolateT* isolate, uint32_t capacity,
      AllocationType allocation = AllocationType::kYoung,
      MaybeDirectHandle<Object> initial_value = {});

  DECL_PRINTER(WeakFixedArray)
  DECL_VERIFIER(WeakFixedArray)

  class BodyDescriptor;

 public:
  // length_ / optional_padding_ live in FixedArrayBase.
  FLEXIBLE_ARRAY_MEMBER(TaggedMember<MaybeObject>, objects);
} V8_OBJECT_END;

// WeakHomomorphicFixedArray describes fixed-sized arrays with element type
// Tagged<MaybeObject>, used for homomorphic feedback.
// It acts as a fixed-size lossy hashmap-based cache, where collisions
// overwrite existing entries.
V8_OBJECT class WeakHomomorphicFixedArray
    : public TaggedArrayBase<WeakHomomorphicFixedArray, MaybeObject,
                             FixedArrayBase> {
  using Super =
      TaggedArrayBase<WeakHomomorphicFixedArray, MaybeObject, FixedArrayBase>;

 public:
  static constexpr RootIndex kMapRootIndex =
      RootIndex::kWeakHomomorphicFixedArrayMap;
  using ElementMemberT = TaggedMember<MaybeObject>;

 public:
  template <class IsolateT>
  static inline Handle<WeakHomomorphicFixedArray> New(
      IsolateT* isolate, uint32_t capacity,
      AllocationType allocation = AllocationType::kYoung,
      MaybeDirectHandle<Object> initial_value = {});

  DECL_PRINTER(WeakHomomorphicFixedArray)
  DECL_VERIFIER(WeakHomomorphicFixedArray)

  class BodyDescriptor;

 public:
  // length_ / optional_padding_ live in FixedArrayBase.
  FLEXIBLE_ARRAY_MEMBER(TaggedMember<MaybeObject>, objects);
} V8_OBJECT_END;

// A WeakFixedArray in trusted space holding pointers into the main cage.
V8_OBJECT class TrustedWeakFixedArray
    : public TaggedArrayBase<TrustedWeakFixedArray, MaybeObject,
                             TrustedFixedArrayBase> {
  using Super = TaggedArrayBase<TrustedWeakFixedArray, MaybeObject,
                                TrustedFixedArrayBase>;

 public:
  static constexpr RootIndex kMapRootIndex =
      RootIndex::kTrustedWeakFixedArrayMap;
  using ElementMemberT = TaggedMember<MaybeObject>;

 public:
  template <class IsolateT>
  static inline Handle<TrustedWeakFixedArray> New(IsolateT* isolate,
                                                  uint32_t capacity);

  DECL_PRINTER(TrustedWeakFixedArray)
  DECL_VERIFIER(TrustedWeakFixedArray)

  class BodyDescriptor;

 public:
  // length_ / optional_padding_ live in TrustedFixedArrayBase.
  FLEXIBLE_ARRAY_MEMBER(TaggedMember<MaybeObject>, objects);
} V8_OBJECT_END;

// A WeakFixedArray in trusted space, containing weak pointers to other
// trusted objects (or smis).
V8_OBJECT class ProtectedWeakFixedArray
    : public TaggedArrayBase<ProtectedWeakFixedArray,
                             UnionOf<MaybeWeak<TrustedObject>, Smi>,
                             TrustedFixedArrayBase> {
  using Super = TaggedArrayBase<ProtectedWeakFixedArray,
                                UnionOf<MaybeWeak<TrustedObject>, Smi>,
                                TrustedFixedArrayBase>;

 public:
  static constexpr RootIndex kMapRootIndex =
      RootIndex::kProtectedWeakFixedArrayMap;
  using ElementMemberT =
      ProtectedTaggedMember<UnionOf<MaybeWeak<TrustedObject>, Smi>>;

 public:
  template <class IsolateT>
  static inline Handle<ProtectedWeakFixedArray> New(IsolateT* isolate,
                                                    uint32_t capacity);
  DECL_PRINTER(ProtectedWeakFixedArray)
  DECL_VERIFIER(ProtectedWeakFixedArray)

  class BodyDescriptor;

 public:
  // length_ / optional_padding_ live in TrustedFixedArrayBase.
  FLEXIBLE_ARRAY_MEMBER(ElementMemberT, objects);
} V8_OBJECT_END;

// WeakArrayList is like a WeakFixedArray with static convenience methods for
// adding more elements. length() returns the number of elements in the list and
// capacity() returns the allocated size. The number of elements is stored at
// kLengthOffset and is updated with every insertion. The array grows
// dynamically with O(1) amortized insertion.
V8_OBJECT class WeakArrayList
    : public TaggedArrayBase<WeakArrayList, MaybeObject, HeapObject> {
  V8_IT_OWN_TYPE;
  using Super = TaggedArrayBase<WeakArrayList, MaybeObject, HeapObject>;

 public:
  static constexpr RootIndex kMapRootIndex = RootIndex::kWeakArrayListMap;
  using ElementMemberT = TaggedMember<MaybeObject>;

 public:
  DECL_PRINTER(WeakArrayList)
  DECL_VERIFIER(WeakArrayList)

  V8_EXPORT_PRIVATE static Handle<WeakArrayList> AddToEnd(
      Isolate* isolate, Handle<WeakArrayList> array,
      MaybeObjectDirectHandle value);

  // A version that adds two elements. This ensures that the elements are
  // inserted atomically w.r.t GC.
  V8_EXPORT_PRIVATE static Handle<WeakArrayList> AddToEnd(
      Isolate* isolate, Handle<WeakArrayList> array,
      MaybeObjectDirectHandle value1, Tagged<Smi> value2);

  // Appends an element to the array and possibly compacts and shrinks live weak
  // references to the start of the collection. Only use this method when
  // indices to elements can change.
  static V8_WARN_UNUSED_RESULT DirectHandle<WeakArrayList> Append(
      Isolate* isolate, DirectHandle<WeakArrayList> array,
      MaybeObjectDirectHandle value,
      AllocationType allocation = AllocationType::kYoung);

  // Compact weak references to the beginning of the array.
  V8_EXPORT_PRIVATE void Compact(Isolate* isolate);

  inline Tagged<MaybeObject> Get(uint32_t index) const;

  // Set the element at index to obj. The underlying array must be large enough.
  // If you need to grow the WeakArrayList, use the static AddToEnd() method
  // instead.
  inline void Set(uint32_t index, Tagged<MaybeObject> value,
                  WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
  inline void Set(uint32_t index, Tagged<Smi> value);

  inline SafeHeapObjectSize capacity() const;
  inline SafeHeapObjectSize capacity(RelaxedLoadTag) const;

  // The function returns an alias instead of uint32_t to incrementally convert
  // callsites without missing any implicit casts.
  inline SafeHeapObjectSize length() const;
  inline SafeHeapObjectSize ulength() const;
  inline void set_length(uint32_t value);

  static constexpr int SizeForCapacity(uint32_t capacity);

  static constexpr uint32_t CapacityForLength(uint32_t length) {
    return length + std::max(length / 2, 2u);
  }

  // Gives access to raw memory which stores the array's data.
  inline MaybeObjectSlot data_start();

  inline void CopyElements(Isolate* isolate, uint32_t dst_index,
                           Tagged<WeakArrayList> src, uint32_t src_index,
                           uint32_t len, WriteBarrierMode mode);

  V8_EXPORT_PRIVATE bool IsFull() const;

  inline int AllocatedSize() const;

  class BodyDescriptor;

  // Maximal allowed length, in number of elements. Chosen s.t. the byte size
  // fits into a Smi which is necessary for being able to create a free space
  // filler.
  // TODO(jgruber): The kMaxLength could be larger (`(Smi::kMaxValue -
  // sizeof(Header)) / kElementSize`), but our tests rely on a
  // smaller maximum to avoid timeouts.
  static constexpr uint32_t kMaxCapacity = kMaxFixedArrayCapacity;

  static Handle<WeakArrayList> EnsureSpace(
      Isolate* isolate, Handle<WeakArrayList> array, uint32_t length,
      AllocationType allocation = AllocationType::kYoung);

  // Returns the number of non-cleaned weak references in the array.
  uint32_t CountLiveWeakReferences() const;

  // Returns the number of non-cleaned elements in the array.
  uint32_t CountLiveElements() const;

  // Returns whether an entry was found and removed. Will move the elements
  // around in the array - this method can only be used in cases where the user
  // doesn't care about the indices! Users should make sure there are no
  // duplicates.
  V8_EXPORT_PRIVATE bool RemoveOne(MaybeObjectDirectHandle value);

  // Searches the array (linear time) and returns whether it contains the value.
  V8_EXPORT_PRIVATE bool Contains(Tagged<MaybeObject> value);

  class Iterator;

  static constexpr uint32_t kCapacityOffset = sizeof(HeapObject);
  static constexpr uint32_t kLengthOffset = kCapacityOffset + kUInt32Size;
  static constexpr uint32_t kHeaderSize = kLengthOffset + kUInt32Size;

 private:
  static constexpr int OffsetOfElementAt(int index) {
    return kHeaderSize + index * kTaggedSize;
  }

 public:
  uint32_t capacity_;
  uint32_t length_;
  FLEXIBLE_ARRAY_MEMBER(TaggedMember<MaybeObject>, objects);
} V8_OBJECT_END;

constexpr int WeakArrayList::SizeForCapacity(uint32_t capacity) {
  return SizeFor(capacity);
}

class WeakArrayList::Iterator {
 public:
  explicit Iterator(Tagged<WeakArrayList> array) : index_(0), array_(array) {}
  Iterator(const Iterator&) = delete;
  Iterator& operator=(const Iterator&) = delete;

  inline Tagged<HeapObject> Next();

 private:
  uint32_t index_;
  Tagged<WeakArrayList> array_;
  DISALLOW_GARBAGE_COLLECTION(no_gc_)
};

// A generic array that grows dynamically with O(1) amortized insertion.
V8_OBJECT class ArrayList
    : public TaggedArrayBase<ArrayList, Object, HeapObject> {
  using Super = TaggedArrayBase<ArrayList, Object, HeapObject>;

 public:
  static constexpr RootIndex kMapRootIndex = RootIndex::kArrayListMap;
  using ElementMemberT = TaggedMember<Object>;

  template <class IsolateT>
  static inline DirectHandle<ArrayList> New(
      IsolateT* isolate, uint32_t capacity,
      AllocationType allocation = AllocationType::kYoung);

  inline int length() const;
  // The function returns an alias instead of uint32_t to force conversion at
  // the callsites without missing any implicit casts.
  inline SafeHeapObjectSize ulength() const;
  inline void set_length(uint32_t value);

  V8_EXPORT_PRIVATE static DirectHandle<ArrayList> Add(
      Isolate* isolate, DirectHandle<ArrayList> array, Tagged<Smi> obj,
      AllocationType allocation = AllocationType::kYoung);
  V8_EXPORT_PRIVATE static DirectHandle<ArrayList> Add(
      Isolate* isolate, DirectHandle<ArrayList> array, DirectHandle<Object> obj,
      AllocationType allocation = AllocationType::kYoung);
  V8_EXPORT_PRIVATE static DirectHandle<ArrayList> Add(
      Isolate* isolate, DirectHandle<ArrayList> array,
      DirectHandle<Object> obj0, DirectHandle<Object> obj1,
      AllocationType allocation = AllocationType::kYoung);

  V8_EXPORT_PRIVATE static DirectHandle<FixedArray> ToFixedArray(
      Isolate* isolate, DirectHandle<ArrayList> array,
      AllocationType allocation = AllocationType::kYoung);

  // Right-trim the array.
  // Invariant: 0 < new_length <= length()
  void RightTrim(Isolate* isolate, uint32_t new_capacity);

  DECL_PRINTER(ArrayList)
  DECL_VERIFIER(ArrayList)

  class BodyDescriptor;

  static constexpr uint32_t kCapacityOffset = sizeof(HeapObject);
  static constexpr uint32_t kLengthOffset = kCapacityOffset + kUInt32Size;
  static constexpr uint32_t kHeaderSize = kLengthOffset + kUInt32Size;

 private:
  static DirectHandle<ArrayList> EnsureSpace(
      Isolate* isolate, DirectHandle<ArrayList> array, uint32_t length,
      AllocationType allocation = AllocationType::kYoung);

 public:
  uint32_t capacity_;
  uint32_t length_;
  FLEXIBLE_ARRAY_MEMBER(TaggedMember<Object>, objects);
} V8_OBJECT_END;

}  // namespace v8::internal

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

#endif  // V8_OBJECTS_FIXED_ARRAY_H_