1 // Copyright 2021 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 INCLUDE_V8_ARRAY_BUFFER_H_
6 #define INCLUDE_V8_ARRAY_BUFFER_H_
7 
8 #include <stddef.h>
9 
10 #include <memory>
11 
12 #include "v8-local-handle.h"  // NOLINT(build/include_directory)
13 #include "v8-object.h"        // NOLINT(build/include_directory)
14 #include "v8config.h"         // NOLINT(build/include_directory)
15 
16 namespace v8 {
17 
18 class SharedArrayBuffer;
19 
20 #ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT
21 // The number of required internal fields can be defined by embedder.
22 #define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2
23 #endif
24 
25 enum class ArrayBufferCreationMode { kInternalized, kExternalized };
26 
27 /**
28  * A wrapper around the backing store (i.e. the raw memory) of an array buffer.
29  * See a document linked in http://crbug.com/v8/9908 for more information.
30  *
31  * The allocation and destruction of backing stores is generally managed by
32  * V8. Clients should always use standard C++ memory ownership types (i.e.
33  * std::unique_ptr and std::shared_ptr) to manage lifetimes of backing stores
34  * properly, since V8 internal objects may alias backing stores.
35  *
36  * This object does not keep the underlying |ArrayBuffer::Allocator| alive by
37  * default. Use Isolate::CreateParams::array_buffer_allocator_shared when
38  * creating the Isolate to make it hold a reference to the allocator itself.
39  */
40 class V8_EXPORT BackingStore : public v8::internal::BackingStoreBase {
41  public:
42   ~BackingStore();
43 
44   /**
45    * Return a pointer to the beginning of the memory block for this backing
46    * store. The pointer is only valid as long as this backing store object
47    * lives.
48    */
49   void* Data() const;
50 
51   /**
52    * The length (in bytes) of this backing store.
53    */
54   size_t ByteLength() const;
55 
56   /**
57    * The maximum length (in bytes) that this backing store may grow to.
58    *
59    * If this backing store was created for a resizable ArrayBuffer or a growable
60    * SharedArrayBuffer, it is >= ByteLength(). Otherwise it is ==
61    * ByteLength().
62    */
63   size_t MaxByteLength() const;
64 
65   /**
66    * Indicates whether the backing store was created for an ArrayBuffer or
67    * a SharedArrayBuffer.
68    */
69   bool IsShared() const;
70 
71   /**
72    * Indicates whether the backing store was created for a resizable ArrayBuffer
73    * or a growable SharedArrayBuffer, and thus may be resized by user JavaScript
74    * code.
75    */
76   bool IsResizableByUserJavaScript() const;
77 
78   /**
79    * Prevent implicit instantiation of operator delete with size_t argument.
80    * The size_t argument would be incorrect because ptr points to the
81    * internal BackingStore object.
82    */
operator delete(void* ptr)83   void operator delete(void* ptr) { ::operator delete(ptr); }
84 
85   /**
86    * Wrapper around ArrayBuffer::Allocator::Reallocate that preserves IsShared.
87    * Assumes that the backing_store was allocated by the ArrayBuffer allocator
88    * of the given isolate.
89    */
90   static std::unique_ptr<BackingStore> Reallocate(
91       v8::Isolate* isolate, std::unique_ptr<BackingStore> backing_store,
92       size_t byte_length);
93 
94   /**
95    * This callback is used only if the memory block for a BackingStore cannot be
96    * allocated with an ArrayBuffer::Allocator. In such cases the destructor of
97    * the BackingStore invokes the callback to free the memory block.
98    */
99   using DeleterCallback = void (*)(void* data, size_t length,
100                                    void* deleter_data);
101 
102   /**
103    * If the memory block of a BackingStore is static or is managed manually,
104    * then this empty deleter along with nullptr deleter_data can be passed to
105    * ArrayBuffer::NewBackingStore to indicate that.
106    *
107    * The manually managed case should be used with caution and only when it
108    * is guaranteed that the memory block freeing happens after detaching its
109    * ArrayBuffer.
110    */
111   static void EmptyDeleter(void* data, size_t length, void* deleter_data);
112 
113  private:
114   /**
115    * See [Shared]ArrayBuffer::GetBackingStore and
116    * [Shared]ArrayBuffer::NewBackingStore.
117    */
118   BackingStore();
119 };
120 
121 #if !defined(V8_IMMINENT_DEPRECATION_WARNINGS)
122 // Use v8::BackingStore::DeleterCallback instead.
123 using BackingStoreDeleterCallback = void (*)(void* data, size_t length,
124                                              void* deleter_data);
125 
126 #endif
127 
128 /**
129  * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5).
130  */
131 class V8_EXPORT ArrayBuffer : public Object {
132  public:
133   /**
134    * A thread-safe allocator that V8 uses to allocate |ArrayBuffer|'s memory.
135    * The allocator is a global V8 setting. It has to be set via
136    * Isolate::CreateParams.
137    *
138    * Memory allocated through this allocator by V8 is accounted for as external
139    * memory by V8. Note that V8 keeps track of the memory for all internalized
140    * |ArrayBuffer|s. Responsibility for tracking external memory (using
141    * Isolate::AdjustAmountOfExternalAllocatedMemory) is handed over to the
142    * embedder upon externalization and taken over upon internalization (creating
143    * an internalized buffer from an existing buffer).
144    *
145    * Note that it is unsafe to call back into V8 from any of the allocator
146    * functions.
147    */
148   class V8_EXPORT Allocator {
149    public:
150     virtual ~Allocator() = default;
151 
152     /**
153      * Allocate |length| bytes. Return nullptr if allocation is not successful.
154      * Memory should be initialized to zeroes.
155      */
156     virtual void* Allocate(size_t length) = 0;
157 
158     /**
159      * Allocate |length| bytes. Return nullptr if allocation is not successful.
160      * Memory does not have to be initialized.
161      */
162     virtual void* AllocateUninitialized(size_t length) = 0;
163 
164     /**
165      * Free the memory block of size |length|, pointed to by |data|.
166      * That memory is guaranteed to be previously allocated by |Allocate|.
167      */
168     virtual void Free(void* data, size_t length) = 0;
169 
170     /**
171      * Reallocate the memory block of size |old_length| to a memory block of
172      * size |new_length| by expanding, contracting, or copying the existing
173      * memory block. If |new_length| > |old_length|, then the new part of
174      * the memory must be initialized to zeros. Return nullptr if reallocation
175      * is not successful.
176      *
177      * The caller guarantees that the memory block was previously allocated
178      * using Allocate or AllocateUninitialized.
179      *
180      * The default implementation allocates a new block and copies data.
181      */
182     virtual void* Reallocate(void* data, size_t old_length, size_t new_length);
183 
184     /**
185      * ArrayBuffer allocation mode. kNormal is a malloc/free style allocation,
186      * while kReservation is for larger allocations with the ability to set
187      * access permissions.
188      */
189     enum class AllocationMode { kNormal, kReservation };
190 
191     /**
192      * Convenience allocator.
193      *
194      * When the sandbox is enabled, this allocator will allocate its backing
195      * memory inside the sandbox. Otherwise, it will rely on malloc/free.
196      *
197      * Caller takes ownership, i.e. the returned object needs to be freed using
198      * |delete allocator| once it is no longer in use.
199      */
200     static Allocator* NewDefaultAllocator();
201   };
202 
203   /**
204    * Data length in bytes.
205    */
206   size_t ByteLength() const;
207 
208   /**
209    * Maximum length in bytes.
210    */
211   size_t MaxByteLength() const;
212 
213   /**
214    * Create a new ArrayBuffer. Allocate |byte_length| bytes.
215    * Allocated memory will be owned by a created ArrayBuffer and
216    * will be deallocated when it is garbage-collected,
217    * unless the object is externalized.
218    */
219   static Local<ArrayBuffer> New(Isolate* isolate, size_t byte_length);
220 
221   /**
222    * Create a new ArrayBuffer with an existing backing store.
223    * The created array keeps a reference to the backing store until the array
224    * is garbage collected. Note that the IsExternal bit does not affect this
225    * reference from the array to the backing store.
226    *
227    * In future IsExternal bit will be removed. Until then the bit is set as
228    * follows. If the backing store does not own the underlying buffer, then
229    * the array is created in externalized state. Otherwise, the array is created
230    * in internalized state. In the latter case the array can be transitioned
231    * to the externalized state using Externalize(backing_store).
232    */
233   static Local<ArrayBuffer> New(Isolate* isolate,
234                                 std::shared_ptr<BackingStore> backing_store);
235 
236   /**
237    * Returns a new standalone BackingStore that is allocated using the array
238    * buffer allocator of the isolate. The result can be later passed to
239    * ArrayBuffer::New.
240    *
241    * If the allocator returns nullptr, then the function may cause GCs in the
242    * given isolate and re-try the allocation. If GCs do not help, then the
243    * function will crash with an out-of-memory error.
244    */
245   static std::unique_ptr<BackingStore> NewBackingStore(Isolate* isolate,
246                                                        size_t byte_length);
247   /**
248    * Returns a new standalone BackingStore that takes over the ownership of
249    * the given buffer. The destructor of the BackingStore invokes the given
250    * deleter callback.
251    *
252    * The result can be later passed to ArrayBuffer::New. The raw pointer
253    * to the buffer must not be passed again to any V8 API function.
254    */
255   static std::unique_ptr<BackingStore> NewBackingStore(
256       void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter,
257       void* deleter_data);
258 
259   /**
260    * Returns a new resizable standalone BackingStore that is allocated using the
261    * array buffer allocator of the isolate. The result can be later passed to
262    * ArrayBuffer::New.
263    *
264    * |byte_length| must be <= |max_byte_length|.
265    *
266    * This function is usable without an isolate. Unlike |NewBackingStore| calls
267    * with an isolate, GCs cannot be triggered, and there are no
268    * retries. Allocation failure will cause the function to crash with an
269    * out-of-memory error.
270    */
271   static std::unique_ptr<BackingStore> NewResizableBackingStore(
272       size_t byte_length, size_t max_byte_length);
273 
274   /**
275    * Returns true if this ArrayBuffer may be detached.
276    */
277   bool IsDetachable() const;
278 
279   /**
280    * Returns true if this ArrayBuffer has been detached.
281    */
282   bool WasDetached() const;
283 
284   /**
285    * Detaches this ArrayBuffer and all its views (typed arrays).
286    * Detaching sets the byte length of the buffer and all typed arrays to zero,
287    * preventing JavaScript from ever accessing underlying backing store.
288    * ArrayBuffer should have been externalized and must be detachable.
289    */
290   V8_DEPRECATE_SOON(
291       "Use the version which takes a key parameter (passing a null handle is "
292       "ok).")
293   void Detach();
294 
295   /**
296    * Detaches this ArrayBuffer and all its views (typed arrays).
297    * Detaching sets the byte length of the buffer and all typed arrays to zero,
298    * preventing JavaScript from ever accessing underlying backing store.
299    * ArrayBuffer should have been externalized and must be detachable. Returns
300    * Nothing if the key didn't pass the [[ArrayBufferDetachKey]] check,
301    * Just(true) otherwise.
302    */
303   V8_WARN_UNUSED_RESULT Maybe<bool> Detach(v8::Local<v8::Value> key);
304 
305   /**
306    * Sets the ArrayBufferDetachKey.
307    */
308   void SetDetachKey(v8::Local<v8::Value> key);
309 
310   /**
311    * Get a shared pointer to the backing store of this array buffer. This
312    * pointer coordinates the lifetime management of the internal storage
313    * with any live ArrayBuffers on the heap, even across isolates. The embedder
314    * should not attempt to manage lifetime of the storage through other means.
315    *
316    * The returned shared pointer will not be empty, even if the ArrayBuffer has
317    * been detached. Use |WasDetached| to tell if it has been detached instead.
318    */
319   std::shared_ptr<BackingStore> GetBackingStore();
320 
321   /**
322    * More efficient shortcut for GetBackingStore()->Data(). The returned pointer
323    * is valid as long as the ArrayBuffer is alive.
324    */
325   void* Data() const;
326 
Cast(Value* value)327   V8_INLINE static ArrayBuffer* Cast(Value* value) {
328 #ifdef V8_ENABLE_CHECKS
329     CheckCast(value);
330 #endif
331     return static_cast<ArrayBuffer*>(value);
332   }
333 
334   static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
335   static const int kEmbedderFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
336 
337  private:
338   ArrayBuffer();
339   static void CheckCast(Value* obj);
340 };
341 
342 #ifndef V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT
343 // The number of required internal fields can be defined by embedder.
344 #define V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT 2
345 #endif
346 
347 /**
348  * A base class for an instance of one of "views" over ArrayBuffer,
349  * including TypedArrays and DataView (ES6 draft 15.13).
350  */
351 class V8_EXPORT ArrayBufferView : public Object {
352  public:
353   /**
354    * Returns underlying ArrayBuffer.
355    */
356   Local<ArrayBuffer> Buffer();
357   /**
358    * Byte offset in |Buffer|.
359    */
360   size_t ByteOffset();
361   /**
362    * Size of a view in bytes.
363    */
364   size_t ByteLength();
365 
366   /**
367    * Copy the contents of the ArrayBufferView's buffer to an embedder defined
368    * memory without additional overhead that calling ArrayBufferView::Buffer
369    * might incur.
370    *
371    * Will write at most min(|byte_length|, ByteLength) bytes starting at
372    * ByteOffset of the underlying buffer to the memory starting at |dest|.
373    * Returns the number of bytes actually written.
374    */
375   size_t CopyContents(void* dest, size_t byte_length);
376 
377   /**
378    * Returns true if ArrayBufferView's backing ArrayBuffer has already been
379    * allocated.
380    */
381   bool HasBuffer() const;
382 
Cast(Value* value)383   V8_INLINE static ArrayBufferView* Cast(Value* value) {
384 #ifdef V8_ENABLE_CHECKS
385     CheckCast(value);
386 #endif
387     return static_cast<ArrayBufferView*>(value);
388   }
389 
390   static const int kInternalFieldCount =
391       V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT;
392   static const int kEmbedderFieldCount =
393       V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT;
394 
395  private:
396   ArrayBufferView();
397   static void CheckCast(Value* obj);
398 };
399 
400 /**
401  * An instance of DataView constructor (ES6 draft 15.13.7).
402  */
403 class V8_EXPORT DataView : public ArrayBufferView {
404  public:
405   static Local<DataView> New(Local<ArrayBuffer> array_buffer,
406                              size_t byte_offset, size_t length);
407   static Local<DataView> New(Local<SharedArrayBuffer> shared_array_buffer,
408                              size_t byte_offset, size_t length);
Cast(Value* value)409   V8_INLINE static DataView* Cast(Value* value) {
410 #ifdef V8_ENABLE_CHECKS
411     CheckCast(value);
412 #endif
413     return static_cast<DataView*>(value);
414   }
415 
416  private:
417   DataView();
418   static void CheckCast(Value* obj);
419 };
420 
421 /**
422  * An instance of the built-in SharedArrayBuffer constructor.
423  */
424 class V8_EXPORT SharedArrayBuffer : public Object {
425  public:
426   /**
427    * Data length in bytes.
428    */
429   size_t ByteLength() const;
430 
431   /**
432    * Maximum length in bytes.
433    */
434   size_t MaxByteLength() const;
435 
436   /**
437    * Create a new SharedArrayBuffer. Allocate |byte_length| bytes.
438    * Allocated memory will be owned by a created SharedArrayBuffer and
439    * will be deallocated when it is garbage-collected,
440    * unless the object is externalized.
441    */
442   static Local<SharedArrayBuffer> New(Isolate* isolate, size_t byte_length);
443 
444   /**
445    * Create a new SharedArrayBuffer with an existing backing store.
446    * The created array keeps a reference to the backing store until the array
447    * is garbage collected. Note that the IsExternal bit does not affect this
448    * reference from the array to the backing store.
449    *
450    * In future IsExternal bit will be removed. Until then the bit is set as
451    * follows. If the backing store does not own the underlying buffer, then
452    * the array is created in externalized state. Otherwise, the array is created
453    * in internalized state. In the latter case the array can be transitioned
454    * to the externalized state using Externalize(backing_store).
455    */
456   static Local<SharedArrayBuffer> New(
457       Isolate* isolate, std::shared_ptr<BackingStore> backing_store);
458 
459   /**
460    * Returns a new standalone BackingStore that is allocated using the array
461    * buffer allocator of the isolate. The result can be later passed to
462    * SharedArrayBuffer::New.
463    *
464    * If the allocator returns nullptr, then the function may cause GCs in the
465    * given isolate and re-try the allocation. If GCs do not help, then the
466    * function will crash with an out-of-memory error.
467    */
468   static std::unique_ptr<BackingStore> NewBackingStore(Isolate* isolate,
469                                                        size_t byte_length);
470   /**
471    * Returns a new standalone BackingStore that takes over the ownership of
472    * the given buffer. The destructor of the BackingStore invokes the given
473    * deleter callback.
474    *
475    * The result can be later passed to SharedArrayBuffer::New. The raw pointer
476    * to the buffer must not be passed again to any V8 functions.
477    */
478   static std::unique_ptr<BackingStore> NewBackingStore(
479       void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter,
480       void* deleter_data);
481 
482   /**
483    * Get a shared pointer to the backing store of this array buffer. This
484    * pointer coordinates the lifetime management of the internal storage
485    * with any live ArrayBuffers on the heap, even across isolates. The embedder
486    * should not attempt to manage lifetime of the storage through other means.
487    */
488   std::shared_ptr<BackingStore> GetBackingStore();
489 
490   /**
491    * More efficient shortcut for GetBackingStore()->Data(). The returned pointer
492    * is valid as long as the ArrayBuffer is alive.
493    */
494   void* Data() const;
495 
Cast(Value* value)496   V8_INLINE static SharedArrayBuffer* Cast(Value* value) {
497 #ifdef V8_ENABLE_CHECKS
498     CheckCast(value);
499 #endif
500     return static_cast<SharedArrayBuffer*>(value);
501   }
502 
503   static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
504 
505  private:
506   SharedArrayBuffer();
507   static void CheckCast(Value* obj);
508 };
509 
510 }  // namespace v8
511 
512 #endif  // INCLUDE_V8_ARRAY_BUFFER_H_
513