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
8#include "include/v8-typed-array.h"
9#include "src/objects/backing-store.h"
10#include "src/objects/js-objects.h"
11#include "torque-generated/bit-fields.h"
12
13// Has to be the last include (doesn't have include guards):
14#include "src/objects/object-macros.h"
15
16namespace v8 {
17namespace internal {
18
19class ArrayBufferExtension;
20
21#include "torque-generated/src/objects/js-array-buffer-tq.inc"
22
23class JSArrayBuffer
24    : public TorqueGeneratedJSArrayBuffer<JSArrayBuffer,
25                                          JSObjectWithEmbedderSlots> {
26 public:
27// The maximum length for JSArrayBuffer's supported by V8.
28// On 32-bit architectures we limit this to 2GiB, so that
29// we can continue to use CheckBounds with the Unsigned31
30// restriction for the length.
31#if V8_HOST_ARCH_32_BIT
32  static constexpr size_t kMaxByteLength = kMaxInt;
33#else
34  static constexpr size_t kMaxByteLength = kMaxSafeInteger;
35#endif
36
37  // [byte_length]: length in bytes
38  DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
39
40  // [backing_store]: backing memory for this array
41  // It should not be assumed that this will be nullptr for empty ArrayBuffers.
42  DECL_GETTER(backing_store, void*)
43  inline void set_backing_store(Isolate* isolate, void* value);
44
45  // [extension]: extension object used for GC
46  DECL_PRIMITIVE_ACCESSORS(extension, ArrayBufferExtension*)
47
48  // [bit_field]: boolean flags
49  DECL_PRIMITIVE_ACCESSORS(bit_field, uint32_t)
50
51  // Clear uninitialized padding space. This ensures that the snapshot content
52  // is deterministic. Depending on the V8 build mode there could be no padding.
53  V8_INLINE void clear_padding();
54
55  // Bit positions for [bit_field].
56  DEFINE_TORQUE_GENERATED_JS_ARRAY_BUFFER_FLAGS()
57
58  // [is_external]: true indicates that the embedder is in charge of freeing the
59  // backing_store, while is_external == false means that v8 will free the
60  // memory block once all ArrayBuffers referencing it are collected by the GC.
61  DECL_BOOLEAN_ACCESSORS(is_external)
62
63  // [is_detachable]: false => this buffer cannot be detached.
64  DECL_BOOLEAN_ACCESSORS(is_detachable)
65
66  // [was_detached]: true => the buffer was previously detached.
67  DECL_BOOLEAN_ACCESSORS(was_detached)
68
69  // [is_asmjs_memory]: true => this buffer was once used as asm.js memory.
70  DECL_BOOLEAN_ACCESSORS(is_asmjs_memory)
71
72  // [is_shared]: true if this is a SharedArrayBuffer or a
73  // GrowableSharedArrayBuffer.
74  DECL_BOOLEAN_ACCESSORS(is_shared)
75
76  // [is_resizable]: true if this is a ResizableArrayBuffer or a
77  // GrowableSharedArrayBuffer.
78  DECL_BOOLEAN_ACCESSORS(is_resizable)
79
80  // An ArrayBuffer is empty if its BackingStore is empty or if there is none.
81  // An empty ArrayBuffer will have a byte_length of zero but not necessarily a
82  // nullptr backing_store. An ArrayBuffer with a byte_length of zero may not
83  // necessarily be empty though, as it may be a GrowableSharedArrayBuffer.
84  // An ArrayBuffer with a size greater than zero is never empty.
85  DECL_GETTER(IsEmpty, bool)
86
87  // Initializes the fields of the ArrayBuffer. The provided backing_store can
88  // be nullptr. If it is not nullptr, then the function registers it with
89  // src/heap/array-buffer-tracker.h.
90  V8_EXPORT_PRIVATE void Setup(SharedFlag shared, ResizableFlag resizable,
91                               std::shared_ptr<BackingStore> backing_store);
92
93  // Attaches the backing store to an already constructed empty ArrayBuffer.
94  // This is intended to be used only in ArrayBufferConstructor builtin.
95  V8_EXPORT_PRIVATE void Attach(std::shared_ptr<BackingStore> backing_store);
96  // Detach the backing store from this array buffer if it is detachable.
97  // This sets the internal pointer and length to 0 and unregisters the backing
98  // store from the array buffer tracker. If the array buffer is not detachable,
99  // this is a nop.
100  //
101  // Array buffers that wrap wasm memory objects are special in that they
102  // are normally not detachable, but can become detached as a side effect
103  // of growing the underlying memory object. The {force_for_wasm_memory} flag
104  // is used by the implementation of Wasm memory growth in order to bypass the
105  // non-detachable check.
106  V8_EXPORT_PRIVATE void Detach(bool force_for_wasm_memory = false);
107
108  // Get a reference to backing store of this array buffer, if there is a
109  // backing store. Returns nullptr if there is no backing store (e.g. detached
110  // or a zero-length array buffer).
111  inline std::shared_ptr<BackingStore> GetBackingStore() const;
112
113  inline size_t GetByteLength() const;
114
115  static size_t GsabByteLength(Isolate* isolate, Address raw_array_buffer);
116
117  static Maybe<bool> GetResizableBackingStorePageConfiguration(
118      Isolate* isolate, size_t byte_length, size_t max_byte_length,
119      ShouldThrow should_throw, size_t* page_size, size_t* initial_pages,
120      size_t* max_pages);
121
122  // Allocates an ArrayBufferExtension for this array buffer, unless it is
123  // already associated with an extension.
124  ArrayBufferExtension* EnsureExtension();
125
126  // Frees the associated ArrayBufferExtension and returns its backing store.
127  std::shared_ptr<BackingStore> RemoveExtension();
128
129  // Marks ArrayBufferExtension
130  void MarkExtension();
131  void YoungMarkExtension();
132  void YoungMarkExtensionPromoted();
133
134  //
135  // Serializer/deserializer support.
136  //
137
138  // Backing stores are serialized/deserialized separately. During serialization
139  // the backing store reference is stored in the backing store field and upon
140  // deserialization it is converted back to actual external (off-heap) pointer
141  // value.
142  inline uint32_t GetBackingStoreRefForDeserialization() const;
143  inline void SetBackingStoreRefForSerialization(uint32_t ref);
144
145  // Dispatched behavior.
146  DECL_PRINTER(JSArrayBuffer)
147  DECL_VERIFIER(JSArrayBuffer)
148
149  static constexpr int kEndOfTaggedFieldsOffset = JSObject::kHeaderSize;
150
151  static const int kSizeWithEmbedderFields =
152      kHeaderSize +
153      v8::ArrayBuffer::kEmbedderFieldCount * kEmbedderDataSlotSize;
154
155  class BodyDescriptor;
156
157 private:
158  inline ArrayBufferExtension** extension_location() const;
159
160#if V8_COMPRESS_POINTERS
161  static const int kUninitializedTagMask = 1;
162
163  inline uint32_t* extension_lo() const;
164  inline uint32_t* extension_hi() const;
165#endif
166
167  TQ_OBJECT_CONSTRUCTORS(JSArrayBuffer)
168};
169
170// Each JSArrayBuffer (with a backing store) has a corresponding native-heap
171// allocated ArrayBufferExtension for GC purposes and storing the backing store.
172// When marking a JSArrayBuffer, the GC also marks the native
173// extension-object. The GC periodically iterates all extensions concurrently
174// and frees unmarked ones.
175// https://docs.google.com/document/d/1-ZrLdlFX1nXT3z-FAgLbKal1gI8Auiaya_My-a0UJ28/edit
176class ArrayBufferExtension final : public Malloced {
177 public:
178  ArrayBufferExtension() : backing_store_(std::shared_ptr<BackingStore>()) {}
179  explicit ArrayBufferExtension(std::shared_ptr<BackingStore> backing_store)
180      : backing_store_(backing_store) {}
181
182  void Mark() { marked_.store(true, std::memory_order_relaxed); }
183  void Unmark() { marked_.store(false, std::memory_order_relaxed); }
184  bool IsMarked() const { return marked_.load(std::memory_order_relaxed); }
185
186  void YoungMark() { set_young_gc_state(GcState::Copied); }
187  void YoungMarkPromoted() { set_young_gc_state(GcState::Promoted); }
188  void YoungUnmark() { set_young_gc_state(GcState::Dead); }
189  bool IsYoungMarked() const { return young_gc_state() != GcState::Dead; }
190
191  bool IsYoungPromoted() const { return young_gc_state() == GcState::Promoted; }
192
193  std::shared_ptr<BackingStore> backing_store() { return backing_store_; }
194  BackingStore* backing_store_raw() { return backing_store_.get(); }
195
196  size_t accounting_length() const {
197    return accounting_length_.load(std::memory_order_relaxed);
198  }
199
200  void set_accounting_length(size_t accounting_length) {
201    accounting_length_.store(accounting_length, std::memory_order_relaxed);
202  }
203
204  size_t ClearAccountingLength() {
205    return accounting_length_.exchange(0, std::memory_order_relaxed);
206  }
207
208  std::shared_ptr<BackingStore> RemoveBackingStore() {
209    return std::move(backing_store_);
210  }
211
212  void set_backing_store(std::shared_ptr<BackingStore> backing_store) {
213    backing_store_ = std::move(backing_store);
214  }
215
216  void reset_backing_store() { backing_store_.reset(); }
217
218  ArrayBufferExtension* next() const { return next_; }
219  void set_next(ArrayBufferExtension* extension) { next_ = extension; }
220
221 private:
222  enum class GcState : uint8_t { Dead = 0, Copied, Promoted };
223
224  std::atomic<bool> marked_{false};
225  std::atomic<GcState> young_gc_state_{GcState::Dead};
226  std::shared_ptr<BackingStore> backing_store_;
227  ArrayBufferExtension* next_ = nullptr;
228  std::atomic<size_t> accounting_length_{0};
229
230  GcState young_gc_state() const {
231    return young_gc_state_.load(std::memory_order_relaxed);
232  }
233
234  void set_young_gc_state(GcState value) {
235    young_gc_state_.store(value, std::memory_order_relaxed);
236  }
237};
238
239class JSArrayBufferView
240    : public TorqueGeneratedJSArrayBufferView<JSArrayBufferView,
241                                              JSObjectWithEmbedderSlots> {
242 public:
243  // [byte_offset]: offset of typed array in bytes.
244  DECL_PRIMITIVE_ACCESSORS(byte_offset, size_t)
245
246  // [byte_length]: length of typed array in bytes.
247  DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
248
249  DECL_VERIFIER(JSArrayBufferView)
250
251  // Bit positions for [bit_field].
252  DEFINE_TORQUE_GENERATED_JS_ARRAY_BUFFER_VIEW_FLAGS()
253
254  inline bool WasDetached() const;
255
256  DECL_BOOLEAN_ACCESSORS(is_length_tracking)
257  DECL_BOOLEAN_ACCESSORS(is_backed_by_rab)
258  inline bool IsVariableLength() const;
259
260  static constexpr int kEndOfTaggedFieldsOffset = kByteOffsetOffset;
261
262  STATIC_ASSERT(IsAligned(kByteOffsetOffset, kUIntptrSize));
263  STATIC_ASSERT(IsAligned(kByteLengthOffset, kUIntptrSize));
264
265  TQ_OBJECT_CONSTRUCTORS(JSArrayBufferView)
266};
267
268class JSTypedArray
269    : public TorqueGeneratedJSTypedArray<JSTypedArray, JSArrayBufferView> {
270 public:
271  // TODO(v8:4153): This should be equal to JSArrayBuffer::kMaxByteLength
272  // eventually.
273  static constexpr size_t kMaxLength = v8::TypedArray::kMaxLength;
274
275  // [length]: length of typed array in elements.
276  DECL_PRIMITIVE_GETTER(length, size_t)
277
278  DECL_GETTER(base_pointer, Object)
279  DECL_ACQUIRE_GETTER(base_pointer, Object)
280
281  // ES6 9.4.5.3
282  V8_WARN_UNUSED_RESULT static Maybe<bool> DefineOwnProperty(
283      Isolate* isolate, Handle<JSTypedArray> o, Handle<Object> key,
284      PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw);
285
286  ExternalArrayType type();
287  V8_EXPORT_PRIVATE size_t element_size() const;
288
289  V8_EXPORT_PRIVATE Handle<JSArrayBuffer> GetBuffer();
290
291  // The `DataPtr` is `base_ptr + external_pointer`, and `base_ptr` is nullptr
292  // for off-heap typed arrays.
293  static constexpr bool kOffHeapDataPtrEqualsExternalPointer = true;
294
295  // Use with care: returns raw pointer into heap.
296  inline void* DataPtr();
297
298  inline void SetOffHeapDataPtr(Isolate* isolate, void* base, Address offset);
299
300  // Whether the buffer's backing store is on-heap or off-heap.
301  inline bool is_on_heap() const;
302  inline bool is_on_heap(AcquireLoadTag tag) const;
303
304  // Only valid to call when IsVariableLength() is true.
305  size_t GetVariableLengthOrOutOfBounds(bool& out_of_bounds) const;
306
307  inline size_t GetLengthOrOutOfBounds(bool& out_of_bounds) const;
308  inline size_t GetLength() const;
309  inline size_t GetByteLength() const;
310  inline bool IsOutOfBounds() const;
311  inline bool IsDetachedOrOutOfBounds() const;
312
313  static size_t LengthTrackingGsabBackedTypedArrayLength(Isolate* isolate,
314                                                         Address raw_array);
315
316  // Note: this is a pointer compression specific optimization.
317  // Normally, on-heap typed arrays contain HeapObject value in |base_pointer|
318  // field and an offset in |external_pointer|.
319  // When pointer compression is enabled we want to combine decompression with
320  // the offset addition. In order to do that we add an isolate root to the
321  // |external_pointer| value and therefore the data pointer computation can
322  // is a simple addition of a (potentially sign-extended) |base_pointer| loaded
323  // as Tagged_t value and an |external_pointer| value.
324  // For full-pointer mode the compensation value is zero.
325  static inline Address ExternalPointerCompensationForOnHeapArray(
326      PtrComprCageBase cage_base);
327
328  //
329  // Serializer/deserializer support.
330  //
331
332  // External backing stores are serialized/deserialized separately.
333  // During serialization the backing store reference is stored in the typed
334  // array object and upon deserialization it is converted back to actual
335  // external (off-heap) pointer value.
336  // The backing store reference is stored in the external_pointer field.
337  inline uint32_t GetExternalBackingStoreRefForDeserialization() const;
338  inline void SetExternalBackingStoreRefForSerialization(uint32_t ref);
339
340  // Subtracts external pointer compensation from the external pointer value.
341  inline void RemoveExternalPointerCompensationForSerialization(
342      Isolate* isolate);
343  // Adds external pointer compensation to the external pointer value.
344  inline void AddExternalPointerCompensationForDeserialization(
345      Isolate* isolate);
346
347  static inline MaybeHandle<JSTypedArray> Validate(Isolate* isolate,
348                                                   Handle<Object> receiver,
349                                                   const char* method_name);
350
351  // Dispatched behavior.
352  DECL_PRINTER(JSTypedArray)
353  DECL_VERIFIER(JSTypedArray)
354
355  // TODO(v8:9287): Re-enable when GCMole stops mixing 32/64 bit configs.
356  // STATIC_ASSERT(IsAligned(kLengthOffset, kTaggedSize));
357  // STATIC_ASSERT(IsAligned(kExternalPointerOffset, kTaggedSize));
358
359  static const int kSizeWithEmbedderFields =
360      kHeaderSize +
361      v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize;
362
363  class BodyDescriptor;
364
365#ifdef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
366  static constexpr size_t kMaxSizeInHeap = V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP;
367#else
368  static constexpr size_t kMaxSizeInHeap = 64;
369#endif
370
371 private:
372  template <typename IsolateT>
373  friend class Deserializer;
374  friend class Factory;
375
376  DECL_PRIMITIVE_SETTER(length, size_t)
377  // Reads the "length" field, doesn't assert the TypedArray is not RAB / GSAB
378  // backed.
379  inline size_t LengthUnchecked() const;
380
381  DECL_GETTER(external_pointer, Address)
382
383  DECL_SETTER(base_pointer, Object)
384  DECL_RELEASE_SETTER(base_pointer, Object)
385
386  inline void set_external_pointer(Isolate* isolate, Address value);
387
388  TQ_OBJECT_CONSTRUCTORS(JSTypedArray)
389};
390
391class JSDataView
392    : public TorqueGeneratedJSDataView<JSDataView, JSArrayBufferView> {
393 public:
394  // [data_pointer]: pointer to the actual data.
395  DECL_GETTER(data_pointer, void*)
396  inline void set_data_pointer(Isolate* isolate, void* value);
397
398  // Dispatched behavior.
399  DECL_PRINTER(JSDataView)
400  DECL_VERIFIER(JSDataView)
401
402  // TODO(v8:9287): Re-enable when GCMole stops mixing 32/64 bit configs.
403  // STATIC_ASSERT(IsAligned(kDataPointerOffset, kTaggedSize));
404
405  static const int kSizeWithEmbedderFields =
406      kHeaderSize +
407      v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize;
408
409  class BodyDescriptor;
410
411  TQ_OBJECT_CONSTRUCTORS(JSDataView)
412};
413
414}  // namespace internal
415}  // namespace v8
416
417#include "src/objects/object-macros-undef.h"
418
419#endif  // V8_OBJECTS_JS_ARRAY_BUFFER_H_
420