current commit
261004.7 · deps: update V8 to 15.2.124.38
// Copyright 2026 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_PRIMITIVE_ARRAY_INL_H_
#define V8_OBJECTS_FIXED_PRIMITIVE_ARRAY_INL_H_

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

#include <optional>

#include "src/base/numerics/checked_math.h"
#include "src/common/globals.h"
#include "src/common/ptr-compr-inl.h"
#include "src/handles/handles-inl.h"
#include "src/heap/factory-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/heap/read-only-heap-inl.h"
#include "src/objects/heap-object-set-map-inl.h"
#include "src/objects/heap-object.h"
#include "src/objects/hole.h"
#include "src/objects/map-inl.h"
#include "src/objects/oddball-predicates-inl.h"
#include "src/objects/slots-inl.h"
#include "src/roots/roots-inl.h"

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

namespace v8::internal {

template <class D, class ElementT_, class P>
bool PrimitiveArrayBase<D, ElementT_, P>::IsInBounds(int index) const {
  return static_cast<unsigned>(index) < this->ulength().value();
}

template <class D, class ElementT_, class P>
auto PrimitiveArrayBase<D, ElementT_, P>::get(int index) const
    -> ElementMemberT {
  DCHECK(IsInBounds(index));
  return derived()->values()[index];
}

template <class D, class ElementT_, class P>
void PrimitiveArrayBase<D, ElementT_, P>::set(int index, ElementMemberT value) {
  DCHECK(IsInBounds(index));
  derived()->values()[index] = value;
}

// Due to right-trimming (which creates a filler object before publishing the
// length through a release-store, see Heap::RightTrimArray), concurrent
// visitors need to read the length with acquire semantics.
template <class D, class ElementT_, class P>
int PrimitiveArrayBase<D, ElementT_, P>::AllocatedSize() const {
  return SizeFor(this->length(kAcquireLoad).value());
}

template <class D, class ElementT_, class P>
auto PrimitiveArrayBase<D, ElementT_, P>::begin() -> ElementMemberT* {
  return &derived()->values()[0];
}

template <class D, class ElementT_, class P>
auto PrimitiveArrayBase<D, ElementT_, P>::begin() const
    -> const ElementMemberT* {
  return &derived()->values()[0];
}

template <class D, class ElementT_, class P>
auto PrimitiveArrayBase<D, ElementT_, P>::end() -> ElementMemberT* {
  return &derived()->values()[this->ulength().value()];
}

template <class D, class ElementT_, class P>
auto PrimitiveArrayBase<D, ElementT_, P>::end() const -> const ElementMemberT* {
  return &derived()->values()[this->ulength().value()];
}

template <class D, class ElementT_, class P>
int PrimitiveArrayBase<D, ElementT_, P>::DataSize() const {
  int data_size = SizeFor(this->ulength().value()) - OFFSET_OF_DATA_START(D);
  DCHECK_EQ(data_size,
            OBJECT_POINTER_ALIGN(this->ulength().value() * kElementSize));
  return data_size;
}

// static
template <class D, class ElementT_, class P>
inline Tagged<D> PrimitiveArrayBase<D, ElementT_, P>::FromAddressOfFirstElement(
    Address address) {
  DCHECK_TAG_ALIGNED(address);
  return Cast<D>(
      Tagged<Object>(address - OFFSET_OF_DATA_START(D) + kHeapObjectTag));
}

// static
template <class D, class ElementT_, class P>
template <class IsolateT>
Handle<D> PrimitiveArrayBase<D, ElementT_, P>::Allocate(
    IsolateT* isolate, uint32_t length,
    std::optional<DisallowGarbageCollection>* no_gc_out,
    AllocationType allocation, AllocationAlignment alignment) {
  // Note 0-length is explicitly allowed since not all subtypes can be
  // assumed to have canonical 0-length instances.
  DCHECK_LE(length, kMaxLength);
  DCHECK(!no_gc_out->has_value());

  Tagged<D> xs = UncheckedCast<D>(isolate->factory()->AllocateRawArray(
      SizeFor(length), allocation, AllocationHint(), alignment));

  ReadOnlyRoots roots{isolate};
  if (DEBUG_BOOL) no_gc_out->emplace();
  Tagged<Map> map = Cast<Map>(roots.object_at(D::kMapRootIndex));
  DCHECK(ReadOnlyHeap::Contains(map));

  xs->set_map_after_allocation(isolate, map, SKIP_WRITE_BARRIER);
  xs->set_length(length);
#if TAGGED_SIZE_8_BYTES
  xs->clear_optional_padding();
#endif  // TAGGED_SIZE_8_BYTES

  return handle(xs, isolate);
}

// static
template <class IsolateT>
Handle<FixedArrayBase> FixedDoubleArray::New(IsolateT* isolate, uint32_t length,
                                             AllocationType allocation) {
  if (V8_UNLIKELY(length > kMaxLength)) {
    base::FatalNoSecurityImpact(
        "Fatal JavaScript invalid size error %d (see crbug.com/1201626)",
        length);
  } else if (V8_UNLIKELY(length == 0)) {
    return isolate->factory()->empty_fixed_array();
  }

  std::optional<DisallowGarbageCollection> no_gc;
  return Cast<FixedDoubleArray>(Allocate(isolate, length, &no_gc, allocation));
}

// static
template <class IsolateT, typename ElementsCallback>
Handle<FixedArrayBase> FixedDoubleArray::New(IsolateT* isolate, uint32_t length,
                                             ElementsCallback elements_callback,
                                             AllocationType allocation) {
  if (V8_UNLIKELY(length > kMaxLength)) {
    base::FatalNoSecurityImpact(
        "Fatal JavaScript invalid size error %d (see crbug.com/1201626)",
        length);
  } else if (V8_UNLIKELY(length == 0)) {
    return isolate->factory()->empty_fixed_array();
  }

  std::optional<DisallowGarbageCollection> no_gc;
  Handle<FixedDoubleArray> array =
      Cast<FixedDoubleArray>(Allocate(isolate, length, &no_gc, allocation));
  for (uint32_t i = 0; i < length; ++i) {
    array->set(i, elements_callback(i));
  }
  return array;
}

double FixedDoubleArray::get_scalar(uint32_t index) {
  DCHECK(!is_the_hole(index));
  return values()[index].value();
}

uint64_t FixedDoubleArray::get_representation(uint32_t index) {
  DCHECK(IsInBounds(index));
  return values()[index].value_as_bits();
}

Handle<Object> FixedDoubleArray::get(Tagged<FixedDoubleArray> array,
                                     uint32_t index, Isolate* isolate) {
  if (array->is_the_hole(index)) {
    return isolate->factory()->the_hole_value();
#ifdef V8_ENABLE_UNDEFINED_DOUBLE
  } else if (array->is_undefined(index)) {
    return isolate->factory()->undefined_value();
#endif  // V8_ENABLE_UNDEFINED_DOUBLE
  } else {
    return isolate->factory()->NewNumber(array->get_scalar(index));
  }
}

void FixedDoubleArray::set(uint32_t index, double value) {
  if (std::isnan(value)) {
    value = std::numeric_limits<double>::quiet_NaN();
  }
  values()[index].set_value(value);
  DCHECK(!is_the_hole(index));
}

#ifdef V8_ENABLE_UNDEFINED_DOUBLE
void FixedDoubleArray::set_undefined(uint32_t index) {
  DCHECK(IsInBounds(index));
  values()[index].set_value_as_bits(kUndefinedNanInt64);
  DCHECK(!is_the_hole(index));
  DCHECK(is_undefined(index));
}

bool FixedDoubleArray::is_undefined(uint32_t index) {
  return get_representation(index) == kUndefinedNanInt64;
}
#endif  // V8_ENABLE_UNDEFINED_DOUBLE

void FixedDoubleArray::set_the_hole(Isolate* isolate, uint32_t index) {
  set_the_hole(index);
}

void FixedDoubleArray::set_the_hole(uint32_t index) {
  DCHECK(IsInBounds(index));
  values()[index].set_value_as_bits(kHoleNanInt64);
}

bool FixedDoubleArray::is_the_hole(Isolate* isolate, uint32_t index) {
  return is_the_hole(index);
}

bool FixedDoubleArray::is_the_hole(uint32_t index) {
  return get_representation(index) == kHoleNanInt64;
}

void FixedDoubleArray::MoveElements(Isolate* isolate, uint32_t dst_index,
                                    uint32_t src_index, uint32_t len,
                                    WriteBarrierMode mode) {
  DCHECK_EQ(SKIP_WRITE_BARRIER, mode);
  MemMove(&values()[dst_index], &values()[src_index], len * kElementSize);
}

void FixedDoubleArray::FillWithHoles(uint32_t from, uint32_t to) {
  for (uint32_t i = from; i < to; i++) {
    set_the_hole(i);
  }
}

// static
template <class IsolateT>
Handle<ByteArray> ByteArray::New(IsolateT* isolate, uint32_t length,
                                 AllocationType allocation,
                                 AllocationAlignment alignment) {
  if (V8_UNLIKELY(length > kMaxLength)) {
    base::FatalNoSecurityImpact("Fatal JavaScript invalid size error %u",
                                length);
  } else if (V8_UNLIKELY(length == 0)) {
    return isolate->factory()->empty_byte_array();
  }

  std::optional<DisallowGarbageCollection> no_gc;
  Handle<ByteArray> result =
      Cast<ByteArray>(Allocate(isolate, length, &no_gc, allocation, alignment));

  int padding_size = SizeFor(length) - OffsetOfElementAt(length);
  memset(&result->values()[length], 0, padding_size);

  return result;
}

uint32_t ByteArray::get_int(int offset) const {
  DCHECK(IsInBounds(offset));
  DCHECK_LE(static_cast<uint32_t>(offset) + sizeof(uint32_t),
            ulength().value());
  return base::ReadUnalignedValue<uint32_t>(
      reinterpret_cast<Address>(&values()[offset]));
}

void ByteArray::set_int(int offset, uint32_t value) {
  DCHECK(IsInBounds(offset));
  DCHECK_LE(static_cast<uint32_t>(offset) + sizeof(uint32_t),
            ulength().value());
  base::WriteUnalignedValue<uint32_t>(
      reinterpret_cast<Address>(&values()[offset]), value);
}

// static
template <class IsolateT>
Handle<TrustedByteArray> TrustedByteArray::New(IsolateT* isolate,
                                               uint32_t length,
                                               AllocationType allocation_type) {
  DCHECK(allocation_type == AllocationType::kTrusted ||
         allocation_type == AllocationType::kSharedTrusted);
  if (V8_UNLIKELY(length > kMaxLength)) {
    base::FatalNoSecurityImpact("Fatal JavaScript invalid size error %u",
                                length);
  }

  std::optional<DisallowGarbageCollection> no_gc;
  Handle<TrustedByteArray> result = TrustedCast<TrustedByteArray>(
      Allocate(isolate, length, &no_gc, allocation_type));

  int padding_size = SizeFor(length) - OffsetOfElementAt(length);
  memset(&result->values()[length], 0, padding_size);

  return result;
}

uint32_t TrustedByteArray::get_int(int offset) const {
  DCHECK(IsInBounds(offset));
  DCHECK_LE(static_cast<uint32_t>(offset) + sizeof(uint32_t),
            ulength().value());
  return base::ReadUnalignedValue<uint32_t>(
      reinterpret_cast<Address>(&values()[offset]));
}

void TrustedByteArray::set_int(int offset, uint32_t value) {
  DCHECK(IsInBounds(offset));
  DCHECK_LE(static_cast<uint32_t>(offset) + sizeof(uint32_t),
            ulength().value());
  base::WriteUnalignedValue<uint32_t>(
      reinterpret_cast<Address>(&values()[offset]), value);
}

template <typename... MoreArgs>
// static
DirectHandle<TrustedFixedAddressArray> TrustedFixedAddressArray::New(
    Isolate* isolate, uint32_t length, MoreArgs&&... more_args) {
  return TrustedCast<TrustedFixedAddressArray>(
      Underlying::New(isolate, length, std::forward<MoreArgs>(more_args)...));
}

template <typename T, typename Base>
template <typename... MoreArgs>
// static
Handle<FixedIntegerArrayBase<T, Base>> FixedIntegerArrayBase<T, Base>::New(
    Isolate* isolate, uint32_t length, MoreArgs&&... more_args) {
  uint32_t byte_length;
  base::internal::CheckedNumeric<uint32_t> checked_byte_length = length;
  checked_byte_length *= sizeof(T);
  CHECK(checked_byte_length.AssignIfValid(&byte_length));
  return TrustedCast<FixedIntegerArrayBase<T, Base>>(
      Base::New(isolate, byte_length, std::forward<MoreArgs>(more_args)...));
}

template <typename T, typename Base>
Address FixedIntegerArrayBase<T, Base>::get_element_address(
    uint32_t index) const {
  DCHECK_LT(index, length().value());
  return reinterpret_cast<Address>(&this->values()[index * sizeof(T)]);
}

template <typename T, typename Base>
T FixedIntegerArrayBase<T, Base>::get(uint32_t index) const {
  static_assert(std::is_integral_v<T>);
  return base::ReadUnalignedValue<T>(get_element_address(index));
}

template <typename T, typename Base>
void FixedIntegerArrayBase<T, Base>::set(uint32_t index, T value) {
  static_assert(std::is_integral_v<T>);
  base::WriteUnalignedValue<T>(get_element_address(index), value);
}

template <typename T, typename Base>
SafeHeapObjectSize FixedIntegerArrayBase<T, Base>::length() const {
  uint32_t len = Base::length().value();
  DCHECK_EQ(len % sizeof(T), 0);
  return SafeHeapObjectSize(len / sizeof(T));
}

}  // namespace v8::internal

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

#endif  // V8_OBJECTS_FIXED_PRIMITIVE_ARRAY_INL_H_