v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
js-array-buffer.h
Go to the documentation of this file.
1// Copyright 2018 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_OBJECTS_JS_ARRAY_BUFFER_H_
6#define V8_OBJECTS_JS_ARRAY_BUFFER_H_
7
13#include "torque-generated/bit-fields.h"
14
15// Has to be the last include (doesn't have include guards):
17
18namespace v8 {
19namespace internal {
20
21class ArrayBufferExtension;
22
23#include "torque-generated/src/objects/js-array-buffer-tq.inc"
24
26 : public TorqueGeneratedJSArrayBuffer<JSArrayBuffer,
27 JSAPIObjectWithEmbedderSlots> {
28 public:
29// The maximum length for JSArrayBuffer's supported by V8.
30// On 32-bit architectures we limit this to 2GiB, so that
31// we can continue to use CheckBounds with the Unsigned31
32// restriction for the length.
33#if V8_ENABLE_SANDBOX
34 static constexpr size_t kMaxByteLength = kMaxSafeBufferSizeForSandbox;
35#elif V8_HOST_ARCH_32_BIT
36 static constexpr size_t kMaxByteLength = kMaxInt;
37#else
38 static constexpr size_t kMaxByteLength = kMaxSafeInteger;
39#endif
40
41 // [byte_length]: length in bytes
42 DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
43 inline size_t byte_length_unchecked() const;
44
45 // [max_byte_length]: maximum length in bytes
46 DECL_PRIMITIVE_ACCESSORS(max_byte_length, size_t)
47
48 // [backing_store]: backing memory for this array
49 // It should not be assumed that this will be nullptr for empty ArrayBuffers.
50 DECL_GETTER(backing_store, void*)
51 inline void set_backing_store(Isolate* isolate, void* value);
52
53 // [extension]: extension object used for GC
55 inline void init_extension();
56
57 // [bit_field]: boolean flags
59
60 // Clear uninitialized padding space. This ensures that the snapshot content
61 // is deterministic. Depending on the V8 build mode there could be no padding.
63
64 // Bit positions for [bit_field].
65 DEFINE_TORQUE_GENERATED_JS_ARRAY_BUFFER_FLAGS()
66
67 // [is_external]: true indicates that the embedder is in charge of freeing the
68 // backing_store, while is_external == false means that v8 will free the
69 // memory block once all ArrayBuffers referencing it are collected by the GC.
70 DECL_BOOLEAN_ACCESSORS(is_external)
71
72 // [is_detachable]: false => this buffer cannot be detached.
74
75 // [was_detached]: true => the buffer was previously detached.
76 DECL_BOOLEAN_ACCESSORS(was_detached)
77
78 // [is_shared]: true if this is a SharedArrayBuffer or a
79 // GrowableSharedArrayBuffer.
81
82 // [is_resizable_by_js]: true if this is a ResizableArrayBuffer or a
83 // GrowableSharedArrayBuffer.
84 DECL_BOOLEAN_ACCESSORS(is_resizable_by_js)
85
86 // An ArrayBuffer is empty if its BackingStore is empty or if there is none.
87 // An empty ArrayBuffer will have a byte_length of zero but not necessarily a
88 // nullptr backing_store. An ArrayBuffer with a byte_length of zero may not
89 // necessarily be empty though, as it may be a GrowableSharedArrayBuffer.
90 // An ArrayBuffer with a size greater than zero is never empty.
91 DECL_GETTER(IsEmpty, bool)
92
93 DECL_ACCESSORS(detach_key, Tagged<Object>)
94
95 // Initializes the fields of the ArrayBuffer. The provided backing_store can
96 // be nullptr. If it is not nullptr, then the function registers it with
97 // src/heap/array-buffer-tracker.h.
98 V8_EXPORT_PRIVATE void Setup(SharedFlag shared, ResizableFlag resizable,
99 std::shared_ptr<BackingStore> backing_store,
100 Isolate* isolate);
101
102 // Detach the backing store from this array buffer if it is detachable.
103 // This sets the internal pointer and length to 0 and unregisters the backing
104 // store from the array buffer tracker. If the array buffer is not detachable,
105 // this is a nop.
106 //
107 // Array buffers that wrap wasm memory objects are special in that they
108 // are normally not detachable, but can become detached as a side effect
109 // of growing the underlying memory object. The {force_for_wasm_memory} flag
110 // is used by the implementation of Wasm memory growth in order to bypass the
111 // non-detachable check.
113 DirectHandle<JSArrayBuffer> buffer, bool force_for_wasm_memory = false,
114 DirectHandle<Object> key = {});
115
116 // Get a reference to backing store of this array buffer, if there is a
117 // backing store. Returns nullptr if there is no backing store (e.g. detached
118 // or a zero-length array buffer).
119 inline std::shared_ptr<BackingStore> GetBackingStore() const;
120
121 inline size_t GetByteLength() const;
122
123 static size_t GsabByteLength(Isolate* isolate, Address raw_array_buffer);
124
126 Isolate* isolate, size_t byte_length, size_t max_byte_length,
127 ShouldThrow should_throw, size_t* page_size, size_t* initial_pages,
128 size_t* max_pages);
129
130 static std::optional<MessageTemplate>
132 Isolate* isolate, size_t byte_length, size_t max_byte_length,
133 size_t* page_size, size_t* initial_pages, size_t* max_pages);
134
135 // Allocates an ArrayBufferExtension for this array buffer. This is assumed to
136 // be only called during setup as it always creates a new extension.
138 Isolate* isolate, std::shared_ptr<BackingStore> backing_store);
139
140 // Frees the associated ArrayBufferExtension and returns its backing store.
141 std::shared_ptr<BackingStore> RemoveExtension();
142
143 // Marks ArrayBufferExtension
144 void MarkExtension();
145 void YoungMarkExtension();
147
148 //
149 // Serializer/deserializer support.
150 //
151
152 // Backing stores are serialized/deserialized separately. During serialization
153 // the backing store reference is stored in the backing store field and upon
154 // deserialization it is converted back to actual external (off-heap) pointer
155 // value.
156 inline uint32_t GetBackingStoreRefForDeserialization() const;
157 inline void SetBackingStoreRefForSerialization(uint32_t ref);
158
159 // Dispatched behavior.
162
163 static constexpr int kSizeWithEmbedderFields =
164 kHeaderSize +
165 v8::ArrayBuffer::kEmbedderFieldCount * kEmbedderDataSlotSize;
166 static constexpr bool kContainsEmbedderFields =
167 v8::ArrayBuffer::kEmbedderFieldCount > 0;
168
169 class BodyDescriptor;
170
171 private:
172 void DetachInternal(bool force_for_wasm_memory, Isolate* isolate);
173
174#if V8_COMPRESS_POINTERS
175 // When pointer compression is enabled, the pointer to the extension is
176 // stored in the external pointer table and the object itself only contains a
177 // 32-bit external pointer handles. This simplifies alignment requirements
178 // and is also necessary for the sandbox.
179 inline ExternalPointerHandle* extension_handle_location() const;
180#else
182#endif // V8_COMPRESS_POINTERS
183
185};
186
187// Each JSArrayBuffer (with a backing store) has a corresponding native-heap
188// allocated ArrayBufferExtension for GC purposes and storing the backing store.
189// When marking a JSArrayBuffer, the GC also marks the native
190// extension-object. The GC periodically iterates all extensions concurrently
191// and frees unmarked ones.
192// https://docs.google.com/document/d/1-ZrLdlFX1nXT3z-FAgLbKal1gI8Auiaya_My-a0UJ28/edit
194#ifdef V8_COMPRESS_POINTERS
195 : public ExternalPointerTable::ManagedResource {
196#else
197 : public Malloced {
198#endif // V8_COMPRESS_POINTERS
199 public:
200 enum class Age : uint8_t { kYoung = 0, kOld = 1 };
201
202 // Packs `accounting_length` and `age` into a single integer for consistent
203 // accounting, allowing resize while concurrently sweeping.
204 struct AccountingState final {
205 size_t accounting_length() const {
206 return AccountingLengthField::decode(value);
207 }
208 Age age() const { return static_cast<Age>(AgeField::decode(value)); }
209
210 uint64_t value;
211 };
212
213 ArrayBufferExtension(std::shared_ptr<BackingStore> backing_store,
217 backing_store_->PerIsolateAccountingLength())) |
218 AgeField::encode(static_cast<uint8_t>(age))) {}
219
220 void Mark() { marked_.store(true, std::memory_order_relaxed); }
221 void Unmark() { marked_.store(false, std::memory_order_relaxed); }
222 bool IsMarked() const { return marked_.load(std::memory_order_relaxed); }
223
227 bool IsYoungMarked() const { return young_gc_state() != GcState::Dead; }
229
230 std::shared_ptr<BackingStore> backing_store() { return backing_store_; }
231 void set_backing_store(std::shared_ptr<BackingStore> backing_store) {
232 backing_store_ = std::move(backing_store);
233 }
234 std::shared_ptr<BackingStore> RemoveBackingStore() {
235 return std::move(backing_store_);
236 }
237
238 size_t accounting_length() const {
239 return AccountingState{accounting_state_.load(std::memory_order_relaxed)}
241 }
242 // Applies `delta` to `accounting_length` and returns the AccountingState
243 // before the update.
245 if (delta >= 0) {
246 return {accounting_state_.fetch_add(
247 AccountingLengthField::encode(static_cast<size_t>(delta)),
248 std::memory_order_relaxed)};
249 }
250 return {accounting_state_.fetch_sub(
251 AccountingLengthField::encode(static_cast<size_t>(-delta)),
252 std::memory_order_relaxed)};
253 }
254 // Clears `accounting_length` and returns the AccountingState before the
255 // update.
257 return {accounting_state_.fetch_and(AgeField::kMask,
258 std::memory_order_relaxed)};
259 }
260
261 ArrayBufferExtension* next() const { return next_; }
263
264 Age age() const {
265 return AccountingState{accounting_state_.load(std::memory_order_relaxed)}
266 .age();
267 }
268 // Updates `age` and returns the AccountingState before the update.
270 return {
271 accounting_state_.fetch_or(AgeField::kMask, std::memory_order_relaxed)};
272 }
274 return {accounting_state_.fetch_and(~AgeField::kMask,
275 std::memory_order_relaxed)};
276 }
277
278 private:
279 enum class GcState : uint8_t { Dead = 0, Copied, Promoted };
280
283
285 return young_gc_state_.load(std::memory_order_relaxed);
286 }
287
289 young_gc_state_.store(value, std::memory_order_relaxed);
290 }
291
292 std::shared_ptr<BackingStore> backing_store_;
294 std::atomic<uint64_t> accounting_state_;
295 std::atomic<bool> marked_{false};
296 std::atomic<GcState> young_gc_state_{GcState::Dead};
297};
298
300 : public TorqueGeneratedJSArrayBufferView<JSArrayBufferView,
301 JSAPIObjectWithEmbedderSlots> {
302 public:
303 class BodyDescriptor;
304
305 // [byte_offset]: offset of typed array in bytes.
306 DECL_PRIMITIVE_ACCESSORS(byte_offset, size_t)
307
308 // [byte_length]: length of typed array in bytes.
309 DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
310
312
313 // Bit positions for [bit_field].
314 DEFINE_TORQUE_GENERATED_JS_ARRAY_BUFFER_VIEW_FLAGS()
315
316 inline bool WasDetached() const;
317
318 DECL_BOOLEAN_ACCESSORS(is_length_tracking)
320 inline bool IsVariableLength() const;
321
322 static_assert(IsAligned(kRawByteOffsetOffset, kUIntptrSize));
323 static_assert(IsAligned(kRawByteLengthOffset, kUIntptrSize));
324
326};
327
329 : public TorqueGeneratedJSTypedArray<JSTypedArray, JSArrayBufferView> {
330 public:
331 static constexpr size_t kMaxByteLength = JSArrayBuffer::kMaxByteLength;
332 static_assert(kMaxByteLength == v8::TypedArray::kMaxByteLength);
333
334 // [length]: length of typed array in elements.
335 DECL_PRIMITIVE_GETTER(length, size_t)
336
337 DECL_GETTER(base_pointer, Tagged<Object>)
339
340 // ES6 9.4.5.3
341 V8_WARN_UNUSED_RESULT static Maybe<bool> DefineOwnProperty(
343 PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw);
344
346 V8_EXPORT_PRIVATE size_t element_size() const;
347
349
350 // The `DataPtr` is `base_ptr + external_pointer`, and `base_ptr` is nullptr
351 // for off-heap typed arrays.
352 static constexpr bool kOffHeapDataPtrEqualsExternalPointer = true;
353
354 // Use with care: returns raw pointer into heap.
355 inline void* DataPtr();
356
357 inline void SetOffHeapDataPtr(Isolate* isolate, void* base, Address offset);
358
359 // Whether the buffer's backing store is on-heap or off-heap.
360 inline bool is_on_heap() const;
361 inline bool is_on_heap(AcquireLoadTag tag) const;
362
363 // Only valid to call when IsVariableLength() is true.
364 size_t GetVariableByteLengthOrOutOfBounds(bool& out_of_bounds) const;
365 size_t GetVariableLengthOrOutOfBounds(bool& out_of_bounds) const;
366
367 inline size_t GetLengthOrOutOfBounds(bool& out_of_bounds) const;
368 inline size_t GetLength() const;
369 inline size_t GetByteLength() const;
370 inline bool IsOutOfBounds() const;
371 inline bool IsDetachedOrOutOfBounds() const;
372
373 static inline void ForFixedTypedArray(ExternalArrayType array_type,
374 size_t* element_size,
375 ElementsKind* element_kind);
376
377 static size_t LengthTrackingGsabBackedTypedArrayLength(Isolate* isolate,
378 Address raw_array);
379
380 // Note: this is a pointer compression specific optimization.
381 // Normally, on-heap typed arrays contain HeapObject value in |base_pointer|
382 // field and an offset in |external_pointer|.
383 // When pointer compression is enabled we want to combine decompression with
384 // the offset addition. In order to do that we add an isolate root to the
385 // |external_pointer| value and therefore the data pointer computation can
386 // is a simple addition of a (potentially sign-extended) |base_pointer| loaded
387 // as Tagged_t value and an |external_pointer| value.
388 // For full-pointer mode the compensation value is zero.
389 static inline Address ExternalPointerCompensationForOnHeapArray(
390 PtrComprCageBase cage_base);
391
392 //
393 // Serializer/deserializer support.
394 //
395
396 // External backing stores are serialized/deserialized separately.
397 // During serialization the backing store reference is stored in the typed
398 // array object and upon deserialization it is converted back to actual
399 // external (off-heap) pointer value.
400 // The backing store reference is stored in the external_pointer field.
401 inline uint32_t GetExternalBackingStoreRefForDeserialization() const;
402 inline void SetExternalBackingStoreRefForSerialization(uint32_t ref);
403
404 // Subtracts external pointer compensation from the external pointer value.
405 inline void RemoveExternalPointerCompensationForSerialization(
406 Isolate* isolate);
407 // Adds external pointer compensation to the external pointer value.
408 inline void AddExternalPointerCompensationForDeserialization(
409 Isolate* isolate);
410
411 static inline MaybeDirectHandle<JSTypedArray> Validate(
412 Isolate* isolate, DirectHandle<Object> receiver, const char* method_name);
413
414 // Dispatched behavior.
417
418 // TODO(v8:9287): Re-enable when GCMole stops mixing 32/64 bit configs.
419 // static_assert(IsAligned(kLengthOffset, kTaggedSize));
420 // static_assert(IsAligned(kExternalPointerOffset, kTaggedSize));
421
422 static constexpr int kSizeWithEmbedderFields =
423 kHeaderSize +
424 v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize;
425 static constexpr bool kContainsEmbedderFields =
426 v8::ArrayBufferView::kEmbedderFieldCount > 0;
427
428 class BodyDescriptor;
429
430#ifdef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
431 static constexpr size_t kMaxSizeInHeap = V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP;
432#else
433 static constexpr size_t kMaxSizeInHeap = 64;
434#endif
435
436 private:
437 template <typename IsolateT>
438 friend class Deserializer;
439 friend class Factory;
440
441 DECL_PRIMITIVE_SETTER(length, size_t)
442 // Reads the "length" field, doesn't assert the TypedArray is not RAB / GSAB
443 // backed.
444 inline size_t LengthUnchecked() const;
445
446 DECL_GETTER(external_pointer, Address)
447
448 DECL_SETTER(base_pointer, Tagged<Object>)
450
451 inline void set_external_pointer(Isolate* isolate, Address value);
452
454};
455
457 : public TorqueGeneratedJSDataViewOrRabGsabDataView<
458 JSDataViewOrRabGsabDataView, JSArrayBufferView> {
459 public:
460 // [data_pointer]: pointer to the actual data.
461 DECL_GETTER(data_pointer, void*)
462 inline void set_data_pointer(Isolate* isolate, void* value);
463
464 // TODO(v8:9287): Re-enable when GCMole stops mixing 32/64 bit configs.
465 // static_assert(IsAligned(kDataPointerOffset, kTaggedSize));
466
467 static constexpr int kSizeWithEmbedderFields =
468 kHeaderSize +
470 static constexpr bool kContainsEmbedderFields =
472
473 class BodyDescriptor;
474
476};
477
479 : public TorqueGeneratedJSDataView<JSDataView,
480 JSDataViewOrRabGsabDataView> {
481 public:
482 // Dispatched behavior.
485
487};
488
490 : public TorqueGeneratedJSRabGsabDataView<JSRabGsabDataView,
491 JSDataViewOrRabGsabDataView> {
492 public:
493 // Dispatched behavior.
496
497 inline size_t GetByteLength() const;
498 inline bool IsOutOfBounds() const;
499
501};
502
503} // namespace internal
504} // namespace v8
505
507
508#endif // V8_OBJECTS_JS_ARRAY_BUFFER_H_
static const int kEmbedderFieldCount
static constexpr size_t kMaxByteLength
static constexpr T decode(U value)
Definition bit-field.h:66
static constexpr U encode(T value)
Definition bit-field.h:55
static constexpr U kMask
Definition bit-field.h:41
std::atomic< uint64_t > accounting_state_
ArrayBufferExtension * next() const
void set_next(ArrayBufferExtension *extension)
std::shared_ptr< BackingStore > RemoveBackingStore()
std::shared_ptr< BackingStore > backing_store_
std::atomic< GcState > young_gc_state_
ArrayBufferExtension(std::shared_ptr< BackingStore > backing_store, ArrayBufferExtension::Age age)
AccountingState UpdateAccountingLength(int64_t delta)
std::shared_ptr< BackingStore > backing_store()
void set_backing_store(std::shared_ptr< BackingStore > backing_store)
size_t byte_length_unchecked() const
std::shared_ptr< BackingStore > RemoveExtension()
V8_EXPORT_PRIVATE ArrayBufferExtension * CreateExtension(Isolate *isolate, std::shared_ptr< BackingStore > backing_store)
void SetBackingStoreRefForSerialization(uint32_t ref)
uint32_t GetBackingStoreRefForDeserialization() const
V8_EXPORT_PRIVATE void Setup(SharedFlag shared, ResizableFlag resizable, std::shared_ptr< BackingStore > backing_store, Isolate *isolate)
void DetachInternal(bool force_for_wasm_memory, Isolate *isolate)
static constexpr int kSizeWithEmbedderFields
static size_t GsabByteLength(Isolate *isolate, Address raw_array_buffer)
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT Maybe< bool > Detach(DirectHandle< JSArrayBuffer > buffer, bool force_for_wasm_memory=false, DirectHandle< Object > key={})
static constexpr size_t kMaxByteLength
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 constexpr bool kContainsEmbedderFields
void set_backing_store(Isolate *isolate, void *value)
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)
ArrayBufferExtension ** extension_location() const
std::shared_ptr< BackingStore > GetBackingStore() const
int32_t offset
std::string extension
TNode< Object > receiver
STL namespace.
constexpr double kMaxSafeInteger
Definition globals.h:1985
constexpr int kEmbedderDataSlotSize
Definition globals.h:664
constexpr int kUIntptrSize
Definition globals.h:409
uint32_t ExternalPointerHandle
constexpr int kMaxInt
Definition globals.h:374
JSArrayBuffer::IsDetachableBit is_shared
uint32_t GetLength(Tagged< JSArray > array)
Definition api.cc:8179
#define DECL_ACCESSORS(name,...)
#define DECL_GETTER(name,...)
#define DECL_ACQUIRE_GETTER(name,...)
#define DECL_VERIFIER(Name)
#define DECL_BOOLEAN_ACCESSORS(name)
#define DECL_RELEASE_SETTER(name,...)
#define DECL_PRIMITIVE_GETTER(name, type)
#define DECL_PRIMITIVE_ACCESSORS(name, type)
#define DECL_PRIMITIVE_SETTER(name, type)
#define DECL_PRINTER(Name)
#define TQ_OBJECT_CONSTRUCTORS(Type)
#define DECL_SETTER(name,...)
#define V8_EXPORT_PRIVATE
Definition macros.h:460
constexpr bool IsAligned(T value, U alignment)
Definition macros.h:403
#define V8_INLINE
Definition v8config.h:500
#define V8_WARN_UNUSED_RESULT
Definition v8config.h:671
std::unique_ptr< ValueMirror > key
wasm::ValueType type