#ifndef V8_OBJECTS_JS_ARRAY_BUFFER_H_
#define V8_OBJECTS_JS_ARRAY_BUFFER_H_
#include "include/v8-array-buffer.h"
#include "include/v8-typed-array.h"
#include "src/handles/maybe-handles.h"
#include "src/objects/backing-store.h"
#include "src/objects/js-objects.h"
#include "torque-generated/bit-fields.h"
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
class ArrayBufferExtension;
#include "torque-generated/src/objects/js-array-buffer-tq.inc"
class JSArrayBuffer
: public TorqueGeneratedJSArrayBuffer<JSArrayBuffer,
JSAPIObjectWithEmbedderSlots> {
public:
#if V8_ENABLE_SANDBOX
static constexpr size_t kMaxByteLength = kMaxSafeBufferSizeForSandbox;
#elif V8_HOST_ARCH_32_BIT
static constexpr size_t kMaxByteLength = kMaxInt;
#else
static constexpr size_t kMaxByteLength = kMaxSafeInteger;
#endif
DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
inline size_t byte_length_unchecked() const;
DECL_PRIMITIVE_ACCESSORS(max_byte_length, size_t)
DECL_GETTER(backing_store, void*)
inline void set_backing_store(Isolate* isolate, void* value);
DECL_PRIMITIVE_ACCESSORS(extension, ArrayBufferExtension*)
inline void init_extension();
DECL_PRIMITIVE_ACCESSORS(bit_field, uint32_t)
V8_INLINE void clear_padding();
DEFINE_TORQUE_GENERATED_JS_ARRAY_BUFFER_FLAGS()
DECL_BOOLEAN_ACCESSORS(is_external)
DECL_BOOLEAN_ACCESSORS(is_detachable)
DECL_BOOLEAN_ACCESSORS(was_detached)
DECL_BOOLEAN_ACCESSORS(is_shared)
DECL_BOOLEAN_ACCESSORS(is_resizable_by_js)
DECL_GETTER(IsEmpty, bool)
DECL_ACCESSORS(detach_key, Tagged<Object>)
V8_EXPORT_PRIVATE void Setup(SharedFlag shared, ResizableFlag resizable,
std::shared_ptr<BackingStore> backing_store,
Isolate* isolate);
V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static Maybe<bool> Detach(
DirectHandle<JSArrayBuffer> buffer, bool force_for_wasm_memory = false,
DirectHandle<Object> key = {});
inline std::shared_ptr<BackingStore> GetBackingStore() const;
inline size_t GetByteLength() const;
static size_t GsabByteLength(Isolate* isolate, Address raw_array_buffer);
static Maybe<bool> GetResizableBackingStorePageConfiguration(
Isolate* isolate, size_t byte_length, size_t max_byte_length,
ShouldThrow should_throw, size_t* page_size, size_t* initial_pages,
size_t* max_pages);
static std::optional<MessageTemplate>
GetResizableBackingStorePageConfigurationImpl(
Isolate* isolate, size_t byte_length, size_t max_byte_length,
size_t* page_size, size_t* initial_pages, size_t* max_pages);
V8_EXPORT_PRIVATE ArrayBufferExtension* CreateExtension(
Isolate* isolate, std::shared_ptr<BackingStore> backing_store);
std::shared_ptr<BackingStore> RemoveExtension();
void MarkExtension();
void YoungMarkExtension();
void YoungMarkExtensionPromoted();
inline uint32_t GetBackingStoreRefForDeserialization() const;
inline void SetBackingStoreRefForSerialization(uint32_t ref);
DECL_PRINTER(JSArrayBuffer)
DECL_VERIFIER(JSArrayBuffer)
static constexpr int kSizeWithEmbedderFields =
kHeaderSize +
v8::ArrayBuffer::kEmbedderFieldCount * kEmbedderDataSlotSize;
static constexpr bool kContainsEmbedderFields =
v8::ArrayBuffer::kEmbedderFieldCount > 0;
class BodyDescriptor;
private:
void DetachInternal(bool force_for_wasm_memory, Isolate* isolate);
#if V8_COMPRESS_POINTERS
inline ExternalPointerHandle* extension_handle_location() const;
#else
inline ArrayBufferExtension** extension_location() const;
#endif
TQ_OBJECT_CONSTRUCTORS(JSArrayBuffer)
};
class ArrayBufferExtension final
#ifdef V8_COMPRESS_POINTERS
: public ExternalPointerTable::ManagedResource {
#else
: public Malloced {
#endif
public:
enum class Age : uint8_t { kYoung = 0, kOld = 1 };
struct AccountingState final {
size_t accounting_length() const {
return AccountingLengthField::decode(value);
}
Age age() const { return static_cast<Age>(AgeField::decode(value)); }
uint64_t value;
};
ArrayBufferExtension(std::shared_ptr<BackingStore> backing_store,
ArrayBufferExtension::Age age)
: backing_store_(std::move(backing_store)),
accounting_state_(AccountingLengthField::encode(static_cast<size_t>(
backing_store_->PerIsolateAccountingLength())) |
AgeField::encode(static_cast<uint8_t>(age))) {}
void Mark() { marked_.store(true, std::memory_order_relaxed); }
void Unmark() { marked_.store(false, std::memory_order_relaxed); }
bool IsMarked() const { return marked_.load(std::memory_order_relaxed); }
void YoungMark() { set_young_gc_state(GcState::Copied); }
void YoungMarkPromoted() { set_young_gc_state(GcState::Promoted); }
void YoungUnmark() { set_young_gc_state(GcState::Dead); }
bool IsYoungMarked() const { return young_gc_state() != GcState::Dead; }
bool IsYoungPromoted() const { return young_gc_state() == GcState::Promoted; }
std::shared_ptr<BackingStore> backing_store() { return backing_store_; }
void set_backing_store(std::shared_ptr<BackingStore> backing_store) {
backing_store_ = std::move(backing_store);
}
std::shared_ptr<BackingStore> RemoveBackingStore() {
return std::move(backing_store_);
}
size_t accounting_length() const {
return AccountingState{accounting_state_.load(std::memory_order_relaxed)}
.accounting_length();
}
AccountingState UpdateAccountingLength(int64_t delta) {
if (delta >= 0) {
return {accounting_state_.fetch_add(
AccountingLengthField::encode(static_cast<size_t>(delta)),
std::memory_order_relaxed)};
}
return {accounting_state_.fetch_sub(
AccountingLengthField::encode(static_cast<size_t>(-delta)),
std::memory_order_relaxed)};
}
AccountingState ClearAccountingLength() {
return {accounting_state_.fetch_and(AgeField::kMask,
std::memory_order_relaxed)};
}
ArrayBufferExtension* next() const { return next_; }
void set_next(ArrayBufferExtension* extension) { next_ = extension; }
Age age() const {
return AccountingState{accounting_state_.load(std::memory_order_relaxed)}
.age();
}
AccountingState SetOld() {
return {
accounting_state_.fetch_or(AgeField::kMask, std::memory_order_relaxed)};
}
AccountingState SetYoung() {
return {accounting_state_.fetch_and(~AgeField::kMask,
std::memory_order_relaxed)};
}
private:
enum class GcState : uint8_t { Dead = 0, Copied, Promoted };
using AgeField = base::BitField<uint8_t, 0, 1, uint64_t>;
using AccountingLengthField = AgeField::Next<size_t, 63>;
GcState young_gc_state() const {
return young_gc_state_.load(std::memory_order_relaxed);
}
void set_young_gc_state(GcState value) {
young_gc_state_.store(value, std::memory_order_relaxed);
}
std::shared_ptr<BackingStore> backing_store_;
ArrayBufferExtension* next_ = nullptr;
std::atomic<uint64_t> accounting_state_;
std::atomic<bool> marked_{false};
std::atomic<GcState> young_gc_state_{GcState::Dead};
};
class JSArrayBufferView
: public TorqueGeneratedJSArrayBufferView<JSArrayBufferView,
JSAPIObjectWithEmbedderSlots> {
public:
class BodyDescriptor;
DECL_PRIMITIVE_ACCESSORS(byte_offset, size_t)
DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
DECL_VERIFIER(JSArrayBufferView)
DEFINE_TORQUE_GENERATED_JS_ARRAY_BUFFER_VIEW_FLAGS()
inline bool WasDetached() const;
inline bool IsDetachedOrOutOfBounds() const;
DECL_BOOLEAN_ACCESSORS(is_length_tracking)
DECL_BOOLEAN_ACCESSORS(is_backed_by_rab)
inline bool IsVariableLength() const;
static_assert(IsAligned(kRawByteOffsetOffset, kUIntptrSize));
static_assert(IsAligned(kRawByteLengthOffset, kUIntptrSize));
TQ_OBJECT_CONSTRUCTORS(JSArrayBufferView)
};
class JSTypedArray
: public TorqueGeneratedJSTypedArray<JSTypedArray, JSArrayBufferView> {
public:
static constexpr size_t kMaxByteLength = JSArrayBuffer::kMaxByteLength;
static_assert(kMaxByteLength == v8::TypedArray::kMaxByteLength);
static constexpr std::pair<ExternalArrayType, size_t> TypeAndElementSizeFor(
ElementsKind);
DECL_GETTER(base_pointer, Tagged<Object>)
DECL_ACQUIRE_GETTER(base_pointer, Tagged<Object>)
V8_WARN_UNUSED_RESULT static Maybe<bool> DefineOwnProperty(
Isolate* isolate, DirectHandle<JSTypedArray> o, DirectHandle<Object> key,
PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw);
ExternalArrayType type() const;
V8_EXPORT_PRIVATE size_t element_size() const;
V8_EXPORT_PRIVATE Handle<JSArrayBuffer> GetBuffer(Isolate* isolate);
static constexpr bool kOffHeapDataPtrEqualsExternalPointer = true;
inline void* DataPtr();
inline void SetOffHeapDataPtr(Isolate* isolate, void* base, Address offset);
inline bool is_on_heap() const;
inline bool is_on_heap(AcquireLoadTag tag) const;
size_t GetVariableByteLengthOrOutOfBounds(bool& out_of_bounds) const;
size_t GetVariableLengthOrOutOfBounds(bool& out_of_bounds) const;
inline size_t GetLengthOrOutOfBounds(bool& out_of_bounds) const;
inline size_t GetLength() const;
inline size_t GetByteLength() const;
inline bool IsOutOfBounds() const;
static inline void ForFixedTypedArray(ExternalArrayType array_type,
size_t* element_size,
ElementsKind* element_kind);
static size_t LengthTrackingGsabBackedTypedArrayLength(Isolate* isolate,
Address raw_array);
static inline Address ExternalPointerCompensationForOnHeapArray(
PtrComprCageBase cage_base);
inline uint32_t GetExternalBackingStoreRefForDeserialization() const;
inline void SetExternalBackingStoreRefForSerialization(uint32_t ref);
inline void RemoveExternalPointerCompensationForSerialization(
Isolate* isolate);
inline void AddExternalPointerCompensationForDeserialization(
Isolate* isolate);
static inline MaybeDirectHandle<JSTypedArray> Validate(
Isolate* isolate, DirectHandle<Object> receiver, const char* method_name);
DECL_PRINTER(JSTypedArray)
DECL_VERIFIER(JSTypedArray)
static constexpr int kSizeWithEmbedderFields =
kHeaderSize +
v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize;
static constexpr bool kContainsEmbedderFields =
v8::ArrayBufferView::kEmbedderFieldCount > 0;
class BodyDescriptor;
#ifdef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
static constexpr size_t kMaxSizeInHeap = V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP;
#else
static constexpr size_t kMaxSizeInHeap = 64;
#endif
private:
template <typename IsolateT>
friend class Deserializer;
friend class Factory;
DECL_PRIMITIVE_SETTER(length, size_t)
DECL_GETTER(external_pointer, Address)
DECL_SETTER(base_pointer, Tagged<Object>)
DECL_RELEASE_SETTER(base_pointer, Tagged<Object>)
inline void set_external_pointer(Isolate* isolate, Address value);
TQ_OBJECT_CONSTRUCTORS(JSTypedArray)
};
class JSDataViewOrRabGsabDataView
: public TorqueGeneratedJSDataViewOrRabGsabDataView<
JSDataViewOrRabGsabDataView, JSArrayBufferView> {
public:
DECL_GETTER(data_pointer, void*)
inline void set_data_pointer(Isolate* isolate, void* value);
static constexpr int kSizeWithEmbedderFields =
kHeaderSize +
v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize;
static constexpr bool kContainsEmbedderFields =
v8::ArrayBufferView::kEmbedderFieldCount > 0;
class BodyDescriptor;
TQ_OBJECT_CONSTRUCTORS(JSDataViewOrRabGsabDataView)
};
class JSDataView
: public TorqueGeneratedJSDataView<JSDataView,
JSDataViewOrRabGsabDataView> {
public:
DECL_PRINTER(JSDataView)
DECL_VERIFIER(JSDataView)
TQ_OBJECT_CONSTRUCTORS(JSDataView)
};
class JSRabGsabDataView
: public TorqueGeneratedJSRabGsabDataView<JSRabGsabDataView,
JSDataViewOrRabGsabDataView> {
public:
DECL_PRINTER(JSRabGsabDataView)
DECL_VERIFIER(JSRabGsabDataView)
inline size_t GetByteLength() const;
inline bool IsOutOfBounds() const;
TQ_OBJECT_CONSTRUCTORS(JSRabGsabDataView)
};
}
}
#include "src/objects/object-macros-undef.h"
#endif