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