| // 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_ARRAY_BUFFER_H_ |
| #define V8_OBJECTS_JS_ARRAY_BUFFER_H_ |
| |
| #include "include/v8-array-buffer.h" |
| #include "include/v8-typed-array.h" |
| #include "src/base/bit-field.h" |
| #include "src/handles/maybe-handles.h" |
| #include "src/objects/backing-store.h" |
| #include "src/objects/js-function.h" |
| #include "src/objects/js-objects.h" |
| #include "src/sandbox/check.h" |
| #include "src/sandbox/external-pointer.h" |
| |
| // Has to be the last include (doesn't have include guards): |
| #include "src/objects/object-macros.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| class ArrayBufferExtension; |
| |
| V8_OBJECT class JSArrayBuffer : public JSAPIObjectWithEmbedderSlots { |
| public: |
| // The maximum length for JSArrayBuffer's supported by V8. |
| // On 32-bit architectures we limit this to 2GiB, so that |
| // we can continue to use CheckBounds with the Unsigned31 |
| // restriction for the length. |
| #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 |
| |
| // [byte_length]: length in bytes |
| inline size_t byte_length() const; |
| inline void set_byte_length(size_t value); |
| inline size_t byte_length_unchecked() const; |
| |
| // [max_byte_length]: maximum length in bytes |
| inline size_t max_byte_length() const; |
| inline void set_max_byte_length(size_t value); |
| |
| // [backing_store]: backing memory for this array |
| // It should not be assumed that this will be nullptr for empty ArrayBuffers. |
| inline void* backing_store() const; |
| inline void* backing_store(PtrComprCageBase cage_base) const; |
| inline void set_backing_store(Isolate* isolate, void* value); |
| |
| // [extension]: extension object used for GC |
| inline ArrayBufferExtension* extension() const; |
| inline void set_extension(ArrayBufferExtension* value); |
| inline void init_extension(); |
| // Returns the current extension and resets it to nullptr. Expects a witness |
| // DisallowGarbageCollection to make sure the returned extension isn't freed |
| // while a pointer to it is held on stack. |
| inline ArrayBufferExtension* extract_extension( |
| Isolate* isolate, |
| const DisallowGarbageCollection& disallow_gc V8_LIFETIME_BOUND); |
| |
| // [bit_field]: boolean flags |
| inline uint32_t bit_field() const; |
| inline void set_bit_field(uint32_t value); |
| |
| // Clear uninitialized padding space. This ensures that the snapshot content |
| // is deterministic. Depending on the V8 build mode there could be no padding. |
| V8_INLINE void clear_padding(); |
| |
| // Bit positions for [bit_field]. |
| using IsExternalBit = base::BitField<bool, 0, 1, uint32_t>; |
| using IsDetachableBit = IsExternalBit::Next<bool, 1>; |
| using WasDetachedBit = IsDetachableBit::Next<bool, 1>; |
| using IsSharedBit = WasDetachedBit::Next<SharedFlag, 1>; |
| using IsResizableByJsBit = IsSharedBit::Next<ResizableFlag, 1>; |
| using IsImmutableBit = IsResizableByJsBit::Next<ImmutableFlag, 1>; |
| enum Flag : uint32_t { |
| kNone = 0, |
| kIsExternal = IsExternalBit::kMask, |
| kIsDetachable = IsDetachableBit::kMask, |
| kWasDetached = WasDetachedBit::kMask, |
| kIsShared = IsSharedBit::kMask, |
| kIsResizableByJs = IsResizableByJsBit::kMask, |
| kIsImmutable = IsImmutableBit::kMask, |
| }; |
| using Flags = base::Flags<Flag>; |
| static constexpr int kFlagCount = 6; |
| |
| // [is_external]: true indicates that the embedder is in charge of freeing the |
| // backing_store, while is_external == false means that v8 will free the |
| // memory block once all ArrayBuffers referencing it are collected by the GC. |
| DECL_BOOLEAN_ACCESSORS(is_external) |
| |
| // [is_detachable]: false => this buffer cannot be detached. |
| DECL_BOOLEAN_ACCESSORS(is_detachable) |
| |
| // [was_detached]: true => the buffer was previously detached. |
| DECL_BOOLEAN_ACCESSORS(was_detached) |
| inline bool was_detached(AcquireLoadTag) const; |
| inline void set_was_detached(bool value, ReleaseStoreTag); |
| |
| // [is_shared]: true if this is a SharedArrayBuffer or a |
| // GrowableSharedArrayBuffer. |
| inline SharedFlag is_shared() const; |
| inline void set_is_shared(SharedFlag value); |
| |
| // [is_resizable_by_js]: true if this is a ResizableArrayBuffer or a |
| // GrowableSharedArrayBuffer. |
| inline ResizableFlag is_resizable_by_js() const; |
| inline void set_is_resizable_by_js(ResizableFlag value); |
| |
| // [is_immutable]: true if this is an ImmutableArrayBuffer. |
| inline bool is_immutable() const; |
| inline void set_is_immutable(ImmutableFlag value); |
| |
| V8_EXPORT_PRIVATE void MakeImmutable(Isolate* isolate); |
| |
| // An ArrayBuffer is empty if its BackingStore is empty or if there is none. |
| // An empty ArrayBuffer will have a byte_length of zero but not necessarily a |
| // nullptr backing_store. An ArrayBuffer with a byte_length of zero may not |
| // necessarily be empty though, as it may be a GrowableSharedArrayBuffer. |
| // An ArrayBuffer with a size greater than zero is never empty. |
| inline bool IsEmpty() const; |
| |
| inline Tagged<MaybeObject> views_or_detach_key() const; |
| inline void set_views_or_detach_key( |
| Tagged<MaybeObject> value, WriteBarrierMode mode = UPDATE_WRITE_BARRIER); |
| |
| inline Tagged<MaybeObject> views() const; |
| inline void set_views(Tagged<MaybeObject> value, |
| WriteBarrierMode mode = UPDATE_WRITE_BARRIER); |
| |
| void AttachView(Tagged<JSArrayBufferView> view); |
| |
| inline Tagged<Object> DetachKey(Isolate* isolate); |
| static void SetDetachKey(DirectHandle<JSArrayBuffer> array_buffer, |
| DirectHandle<Object> key, Isolate* isolate); |
| |
| static constexpr Tagged<Smi> kNoView = Smi::zero(); |
| static constexpr Tagged<Smi> kManyViews = Smi::FromInt(1); |
| |
| // Initializes the fields of the ArrayBuffer. The provided backing_store can |
| // be nullptr. If it is not nullptr, then the function registers it with |
| // src/heap/array-buffer-tracker.h. |
| V8_EXPORT_PRIVATE void Setup(SharedFlag shared, ResizableFlag resizable, |
| std::shared_ptr<BackingStore> backing_store, |
| Isolate* isolate, |
| Tagged<MaybeObject> views = kNoView); |
| |
| // Detach the backing store from this array buffer if it is detachable. |
| // This sets the internal pointer and length to 0 and unregisters the backing |
| // store from the array buffer tracker. If the array buffer is not detachable, |
| // this is a nop. |
| // |
| // Array buffers that wrap wasm memory objects are special in that they |
| // are normally not detachable, but can become detached as a side effect |
| // of growing the underlying memory object. The {force_for_wasm_memory} flag |
| // is used by the implementation of Wasm memory growth in order to bypass the |
| // non-detachable check. |
| V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static Maybe<bool> Detach( |
| DirectHandle<JSArrayBuffer> buffer, bool force_for_wasm_memory = false, |
| DirectHandle<Object> key = {}); |
| |
| // Get a reference to backing store of this array buffer, if there is a |
| // backing store. Returns nullptr if there is no backing store (e.g. detached |
| // or a zero-length array buffer). |
| 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); |
| |
| // Allocates an ArrayBufferExtension for this array buffer. This is assumed to |
| // be only called during setup as it always creates a new extension. |
| V8_EXPORT_PRIVATE ArrayBufferExtension* CreateExtension( |
| Isolate* isolate, std::shared_ptr<BackingStore> backing_store); |
| |
| // |
| // Serializer/deserializer support. |
| // |
| |
| // Backing stores are serialized/deserialized separately. During serialization |
| // the backing store reference is stored in the backing store field and upon |
| // deserialization it is converted back to actual external (off-heap) pointer |
| // value. |
| inline uint32_t GetBackingStoreRefForDeserialization() const; |
| inline void SetBackingStoreRefForSerialization(uint32_t ref); |
| |
| // Dispatched behavior. |
| DECL_PRINTER(JSArrayBuffer) |
| DECL_VERIFIER(JSArrayBuffer) |
| |
| class BodyDescriptor; |
| |
| static uint32_t NotValidMask(TypedArrayAccessMode mode) { |
| switch (mode) { |
| case TypedArrayAccessMode::kRead: |
| return JSArrayBuffer::WasDetachedBit::kMask; |
| case TypedArrayAccessMode::kWrite: |
| return JSArrayBuffer::WasDetachedBit::kMask | |
| JSArrayBuffer::IsImmutableBit::kMask; |
| } |
| UNREACHABLE(); |
| } |
| |
| #if TAGGED_SIZE_8_BYTES |
| static const int kOptionalPaddingOffset; |
| static const int kOptionalPaddingOffsetEnd; |
| #endif |
| static const int kHeaderSize; |
| static const int kSizeWithEmbedderFields; |
| static constexpr bool kContainsEmbedderFields = |
| v8::ArrayBuffer::kEmbedderFieldCount > 0; |
| |
| private: |
| inline Tagged<Cell> detach_key() const; |
| inline void set_detach_key(Tagged<Cell> value, |
| WriteBarrierMode mode = UPDATE_WRITE_BARRIER); |
| inline bool has_detach_key() const; |
| |
| static void DetachInternal(DirectHandle<JSArrayBuffer> array_buffer, |
| bool force_for_wasm_memory, Isolate* isolate); |
| |
| static bool TryDetachViews(DirectHandle<JSArrayBuffer> array_buffer, |
| Isolate* isolate); |
| |
| #if V8_COMPRESS_POINTERS |
| // When pointer compression is enabled, the pointer to the extension is |
| // stored in the external pointer table and the object itself only contains a |
| // 32-bit external pointer handles. This simplifies alignment requirements |
| // and is also necessary for the sandbox. |
| inline ExternalPointerHandle* extension_handle_location() const; |
| #else |
| inline ArrayBufferExtension** extension_location() const; |
| #endif // V8_COMPRESS_POINTERS |
| |
| public: |
| TaggedMember<MaybeObject> views_or_detach_key_; |
| UnalignedValueMember<uintptr_t> raw_byte_length_; |
| UnalignedValueMember<uintptr_t> raw_max_byte_length_; |
| UnalignedValueMember<Address> backing_store_; |
| ExternalPointerMember<kArrayBufferExtensionTag> extension_; |
| uint32_t bit_field_; |
| #if TAGGED_SIZE_8_BYTES |
| uint32_t optional_padding_; |
| #endif |
| } V8_OBJECT_END; |
| |
| #if TAGGED_SIZE_8_BYTES |
| inline constexpr int JSArrayBuffer::kOptionalPaddingOffset = |
| offsetof(JSArrayBuffer, optional_padding_); |
| inline constexpr int JSArrayBuffer::kOptionalPaddingOffsetEnd = |
| kOptionalPaddingOffset + sizeof(uint32_t) - 1; |
| #endif |
| inline constexpr int JSArrayBuffer::kHeaderSize = sizeof(JSArrayBuffer); |
| inline constexpr int JSArrayBuffer::kSizeWithEmbedderFields = |
| JSArrayBuffer::kHeaderSize + |
| v8::ArrayBuffer::kEmbedderFieldCount * kEmbedderDataSlotSize; |
| |
| // Each JSArrayBuffer (with a backing store) has a corresponding native-heap |
| // allocated ArrayBufferExtension for GC purposes and storing the backing store. |
| // When marking a JSArrayBuffer, the GC also marks the native |
| // extension-object. The GC periodically iterates all extensions concurrently |
| // and frees unmarked ones. |
| // https://docs.google.com/document/d/1-ZrLdlFX1nXT3z-FAgLbKal1gI8Auiaya_My-a0UJ28/edit |
| class ArrayBufferExtension final |
| #ifdef V8_COMPRESS_POINTERS |
| : public ExternalPointerTable::ManagedResource { |
| #else |
| : public Malloced { |
| #endif // V8_COMPRESS_POINTERS |
| public: |
| enum class Age : uint8_t { kYoung = 0, kOld = 1 }; |
| |
| // Packs `accounting_length` and `age` into a single integer for consistent |
| // accounting, allowing resize while concurrently sweeping. |
| 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, SharedFlag is_shared, |
| ResizableFlag is_resizable_by_js) |
| : backing_store_(std::move(backing_store)), |
| accounting_state_(AccountingLengthField::encode(static_cast<size_t>( |
| backing_store_->PerIsolateAccountingLength())) | |
| AgeField::encode(static_cast<uint8_t>(age))), |
| is_shared_(is_shared), |
| is_resizable_by_js_(is_resizable_by_js) { |
| initialized_for_gc_.store(true, std::memory_order_release); |
| } |
| |
| // Barrier used for publishing the object. This barrier must be used whenever |
| // the extension is accessed off the main thread. |
| // |
| // The constructor sets a value with release that is read with acquire in this |
| // varrier. |
| void InitializationBarrier() const { |
| std::ignore = initialized_for_gc_.load(std::memory_order_acquire); |
| } |
| |
| 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() { |
| SBXCHECK_EQ(ArrayBufferExtension::Age::kYoung, age()); |
| set_young_gc_state(GcState::Copied); |
| } |
| void YoungMarkPromoted() { |
| // When iterating promoted pages the extension object is already set as |
| // being promoted which happens before the page iteration. This may even run |
| // racefully between AB sweeping and page iteration. |
| set_young_gc_state(GcState::Promoted); |
| } |
| void YoungUnmark() { |
| DCHECK_EQ(ArrayBufferExtension::Age::kYoung, age()); |
| set_young_gc_state(GcState::Dead); |
| } |
| bool IsYoungMarked() const { |
| DCHECK_EQ(ArrayBufferExtension::Age::kYoung, age()); |
| return young_gc_state() != GcState::Dead; |
| } |
| bool IsYoungPromoted() const { |
| DCHECK_EQ(ArrayBufferExtension::Age::kYoung, age()); |
| 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(); |
| } |
| // Applies `delta` to `accounting_length` and returns the AccountingState |
| // before the update. |
| 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)}; |
| } |
| // Clears `accounting_length` and returns the AccountingState before the |
| // update. |
| 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; } |
| |
| SharedFlag is_shared() const { return is_shared_; } |
| |
| ResizableFlag is_resizable_by_js() const { return is_resizable_by_js_; } |
| void set_is_resizable_by_js(ResizableFlag value) { |
| is_resizable_by_js_ = value; |
| } |
| |
| Age age() const { |
| return AccountingState{accounting_state_.load(std::memory_order_relaxed)} |
| .age(); |
| } |
| // Updates `age` and returns the AccountingState before the update. |
| 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> initialized_for_gc_{false}; |
| std::atomic<bool> marked_{false}; |
| std::atomic<GcState> young_gc_state_{GcState::Dead}; |
| |
| // Trusted copies of the in-sandbox JSArrayBuffer flags. We verify that the |
| // in-sandbox flags match these trusted copies during critical operations. |
| const SharedFlag is_shared_; |
| ResizableFlag is_resizable_by_js_; |
| }; |
| |
| V8_OBJECT class JSArrayBufferView : public JSAPIObjectWithEmbedderSlots { |
| V8_IT_ABSTRACT; |
| |
| public: |
| // [buffer]: the underlying ArrayBuffer. |
| inline Tagged<JSArrayBuffer> buffer() const; |
| inline void set_buffer(Tagged<JSArrayBuffer> value, |
| WriteBarrierMode mode = UPDATE_WRITE_BARRIER); |
| |
| // [byte_offset]: offset of typed array in bytes. |
| inline size_t byte_offset() const; |
| inline void set_byte_offset(size_t value); |
| |
| // [byte_length]: length of typed array in bytes. |
| // Only use for fixed-size arrays (`!IsVariableLength()`). Otherwise use |
| // `JSTypedArray::GetByteLength()`. |
| inline size_t byte_length() const; |
| inline void set_byte_length(size_t value); |
| |
| // [bit_field]: boolean flags |
| inline uint32_t bit_field() const; |
| inline void set_bit_field(uint32_t value); |
| |
| DECL_VERIFIER(JSArrayBufferView) |
| |
| // Bit positions for [bit_field]. |
| using IsLengthTrackingBit = base::BitField<bool, 0, 1, uint32_t>; |
| using IsBackedByRabBit = IsLengthTrackingBit::Next<bool, 1>; |
| enum Flag : uint32_t { |
| kNone = 0, |
| kIsLengthTracking = IsLengthTrackingBit::kMask, |
| kIsBackedByRab = IsBackedByRabBit::kMask, |
| }; |
| using Flags = base::Flags<Flag>; |
| static constexpr int kFlagCount = 2; |
| |
| 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; |
| |
| class BodyDescriptor; |
| |
| static const int kHeaderSize; |
| |
| public: |
| TaggedMember<JSArrayBuffer> buffer_; |
| uint32_t bit_field_; |
| #if TAGGED_SIZE_8_BYTES |
| uint32_t optional_padding_; |
| #endif |
| UnalignedValueMember<uintptr_t> raw_byte_offset_; |
| UnalignedValueMember<uintptr_t> raw_byte_length_; |
| } V8_OBJECT_END; |
| |
| inline constexpr int JSArrayBufferView::kHeaderSize = sizeof(JSArrayBufferView); |
| |
| static_assert(IsAligned(offsetof(JSArrayBufferView, raw_byte_offset_), |
| kUIntptrSize)); |
| static_assert(IsAligned(offsetof(JSArrayBufferView, raw_byte_length_), |
| kUIntptrSize)); |
| |
| V8_OBJECT class JSTypedArray : public JSArrayBufferView { |
| V8_IT_OWN_TYPE; |
| |
| public: |
| static constexpr size_t kMaxByteLength = JSArrayBuffer::kMaxByteLength; |
| static_assert(kMaxByteLength == v8::TypedArray::kMaxByteLength); |
| |
| static constexpr std::pair<ExternalArrayType, size_t> TypeAndElementSizeFor( |
| ElementsKind); |
| |
| // [length]: length in elements. |
| inline size_t length() const; |
| |
| // [base_pointer]: the ByteArray containing the backing store, if it is |
| // on-heap, or Smi::zero() if it is off-heap. |
| inline Tagged<Object> base_pointer() const; |
| inline Tagged<Object> base_pointer(AcquireLoadTag) const; |
| |
| // ES6 9.4.5.3 |
| 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); |
| |
| // The `DataPtr` is `base_ptr + external_pointer`, and `base_ptr` is nullptr |
| // for off-heap typed arrays. |
| static constexpr bool kOffHeapDataPtrEqualsExternalPointer = true; |
| |
| // Use with care: returns raw pointer into heap. |
| inline void* DataPtr(); |
| |
| inline void SetOffHeapDataPtr(Isolate* isolate, void* base, Address offset); |
| |
| // Whether the buffer's backing store is on-heap or off-heap. |
| inline bool is_on_heap() const; |
| inline bool is_on_heap(AcquireLoadTag tag) const; |
| |
| // Only valid to call when IsVariableLength() is true. |
| 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); |
| |
| // Note: this is a pointer compression specific optimization. |
| // Normally, on-heap typed arrays contain HeapObject value in |base_pointer| |
| // field and an offset in |external_pointer|. |
| // When pointer compression is enabled we want to combine decompression with |
| // the offset addition. In order to do that we add an isolate root to the |
| // |external_pointer| value and therefore the data pointer computation can |
| // is a simple addition of a (potentially sign-extended) |base_pointer| loaded |
| // as Tagged_t value and an |external_pointer| value. |
| // For full-pointer mode the compensation value is zero. |
| static inline Address ExternalPointerCompensationForOnHeapArray( |
| PtrComprCageBase cage_base); |
| |
| // |
| // Serializer/deserializer support. |
| // |
| |
| // External backing stores are serialized/deserialized separately. |
| // During serialization the backing store reference is stored in the typed |
| // array object and upon deserialization it is converted back to actual |
| // external (off-heap) pointer value. |
| // The backing store reference is stored in the external_pointer field. |
| inline uint32_t GetExternalBackingStoreRefForDeserialization() const; |
| inline void SetExternalBackingStoreRefForSerialization(uint32_t ref); |
| |
| // Subtracts external pointer compensation from the external pointer value. |
| inline void RemoveExternalPointerCompensationForSerialization( |
| Isolate* isolate); |
| // Adds external pointer compensation to the external pointer value. |
| inline void AddExternalPointerCompensationForDeserialization( |
| Isolate* isolate); |
| |
| static inline MaybeDirectHandle<JSTypedArray> Validate( |
| Isolate* isolate, DirectHandle<Object> receiver, const char* method_name, |
| TypedArrayAccessMode access_mode = TypedArrayAccessMode::kRead); |
| |
| // Dispatched behavior. |
| DECL_PRINTER(JSTypedArray) |
| DECL_VERIFIER(JSTypedArray) |
| |
| 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 |
| |
| static inline void MarkDetached(DirectHandle<JSTypedArray> array, |
| Isolate* isolate); |
| |
| static const int kHeaderSize; |
| static const int kSizeWithEmbedderFields; |
| static constexpr bool kContainsEmbedderFields = |
| v8::ArrayBufferView::kEmbedderFieldCount > 0; |
| |
| private: |
| template <typename IsolateT> |
| friend class Deserializer; |
| friend class Factory; |
| |
| inline void set_length(size_t value); |
| inline Address external_pointer() const; |
| inline Address external_pointer(PtrComprCageBase cage_base) const; |
| |
| inline void set_base_pointer(Tagged<Object> value, |
| WriteBarrierMode mode = UPDATE_WRITE_BARRIER); |
| inline void set_base_pointer(Tagged<Object> value, ReleaseStoreTag, |
| WriteBarrierMode mode = UPDATE_WRITE_BARRIER); |
| |
| inline void set_external_pointer(Isolate* isolate, Address value); |
| |
| public: |
| UnalignedValueMember<uintptr_t> raw_length_; |
| UnalignedValueMember<Address> external_pointer_; |
| TaggedMember<Object> base_pointer_; |
| } V8_OBJECT_END; |
| |
| inline constexpr int JSTypedArray::kHeaderSize = sizeof(JSTypedArray); |
| inline constexpr int JSTypedArray::kSizeWithEmbedderFields = |
| JSTypedArray::kHeaderSize + |
| v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize; |
| |
| // TODO(v8:9287): Re-enable when GCMole stops mixing 32/64 bit configs. |
| // static_assert(IsAligned(JSTypedArray::kRawLengthOffset, kTaggedSize)); |
| // static_assert(IsAligned(JSTypedArray::kExternalPointerOffset, kTaggedSize)); |
| |
| V8_OBJECT class JSDetachedTypedArray : public JSTypedArray { |
| public: |
| DECL_PRINTER(JSDetachedTypedArray) |
| DECL_VERIFIER(JSDetachedTypedArray) |
| } V8_OBJECT_END; |
| |
| V8_OBJECT class JSDataViewOrRabGsabDataView : public JSArrayBufferView { |
| V8_IT_ABSTRACT; |
| |
| public: |
| // [data_pointer]: pointer to the actual data. |
| inline void* data_pointer() const; |
| inline void* data_pointer(PtrComprCageBase cage_base) const; |
| inline void set_data_pointer(Isolate* isolate, void* value); |
| |
| class BodyDescriptor; |
| |
| static const int kHeaderSize; |
| static const int kSizeWithEmbedderFields; |
| static constexpr bool kContainsEmbedderFields = |
| v8::ArrayBufferView::kEmbedderFieldCount > 0; |
| |
| public: |
| UnalignedValueMember<Address> data_pointer_; |
| } V8_OBJECT_END; |
| |
| inline constexpr int JSDataViewOrRabGsabDataView::kHeaderSize = |
| sizeof(JSDataViewOrRabGsabDataView); |
| inline constexpr int JSDataViewOrRabGsabDataView::kSizeWithEmbedderFields = |
| JSDataViewOrRabGsabDataView::kHeaderSize + |
| v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize; |
| |
| // TODO(v8:9287): Re-enable when GCMole stops mixing 32/64 bit configs. |
| // static_assert(IsAligned(JSDataViewOrRabGsabDataView::kDataPointerOffset, |
| // kTaggedSize)); |
| |
| V8_OBJECT class JSDataView : public JSDataViewOrRabGsabDataView { |
| public: |
| DECL_PRINTER(JSDataView) |
| DECL_VERIFIER(JSDataView) |
| } V8_OBJECT_END; |
| |
| V8_OBJECT class JSRabGsabDataView : public JSDataViewOrRabGsabDataView { |
| public: |
| DECL_PRINTER(JSRabGsabDataView) |
| DECL_VERIFIER(JSRabGsabDataView) |
| |
| inline size_t GetByteLength() const; |
| inline bool IsOutOfBounds() const; |
| } V8_OBJECT_END; |
| |
| V8_OBJECT class TypedArrayConstructor : public JSFunctionWithPrototype { |
| V8_IT_ABSTRACT; |
| } V8_OBJECT_END; |
| V8_OBJECT class Uint8TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Int8TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Uint16TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Int16TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Uint32TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Int32TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Float16TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Float32TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Float64TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Uint8ClampedTypedArrayConstructor |
| : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Biguint64TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| V8_OBJECT class Bigint64TypedArrayConstructor : public TypedArrayConstructor { |
| } V8_OBJECT_END; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #include "src/objects/object-macros-undef.h" |
| |
| #endif // V8_OBJECTS_JS_ARRAY_BUFFER_H_ |