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