• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 namespace v8 {
17 namespace internal {
18 
19 class ArrayBufferExtension;
20 
21 #include "torque-generated/src/objects/js-array-buffer-tq.inc"
22 
23 class 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
176 class ArrayBufferExtension final : public Malloced {
177  public:
ArrayBufferExtension()178   ArrayBufferExtension() : backing_store_(std::shared_ptr<BackingStore>()) {}
ArrayBufferExtension(std::shared_ptr<BackingStore> backing_store)179   explicit ArrayBufferExtension(std::shared_ptr<BackingStore> backing_store)
180       : backing_store_(backing_store) {}
181 
Mark()182   void Mark() { marked_.store(true, std::memory_order_relaxed); }
Unmark()183   void Unmark() { marked_.store(false, std::memory_order_relaxed); }
IsMarked()184   bool IsMarked() const { return marked_.load(std::memory_order_relaxed); }
185 
YoungMark()186   void YoungMark() { set_young_gc_state(GcState::Copied); }
YoungMarkPromoted()187   void YoungMarkPromoted() { set_young_gc_state(GcState::Promoted); }
YoungUnmark()188   void YoungUnmark() { set_young_gc_state(GcState::Dead); }
IsYoungMarked()189   bool IsYoungMarked() const { return young_gc_state() != GcState::Dead; }
190 
IsYoungPromoted()191   bool IsYoungPromoted() const { return young_gc_state() == GcState::Promoted; }
192 
backing_store()193   std::shared_ptr<BackingStore> backing_store() { return backing_store_; }
backing_store_raw()194   BackingStore* backing_store_raw() { return backing_store_.get(); }
195 
accounting_length()196   size_t accounting_length() const {
197     return accounting_length_.load(std::memory_order_relaxed);
198   }
199 
set_accounting_length(size_t accounting_length)200   void set_accounting_length(size_t accounting_length) {
201     accounting_length_.store(accounting_length, std::memory_order_relaxed);
202   }
203 
ClearAccountingLength()204   size_t ClearAccountingLength() {
205     return accounting_length_.exchange(0, std::memory_order_relaxed);
206   }
207 
RemoveBackingStore()208   std::shared_ptr<BackingStore> RemoveBackingStore() {
209     return std::move(backing_store_);
210   }
211 
set_backing_store(std::shared_ptr<BackingStore> backing_store)212   void set_backing_store(std::shared_ptr<BackingStore> backing_store) {
213     backing_store_ = std::move(backing_store);
214   }
215 
reset_backing_store()216   void reset_backing_store() { backing_store_.reset(); }
217 
next()218   ArrayBufferExtension* next() const { return next_; }
set_next(ArrayBufferExtension * extension)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 
young_gc_state()230   GcState young_gc_state() const {
231     return young_gc_state_.load(std::memory_order_relaxed);
232   }
233 
set_young_gc_state(GcState value)234   void set_young_gc_state(GcState value) {
235     young_gc_state_.store(value, std::memory_order_relaxed);
236   }
237 };
238 
239 class 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 
268 class 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 
391 class 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