summaryrefslogtreecommitdiff
path: root/chromium/v8/src/objects/js-array-buffer.h
blob: 3ec5e0d51776f52cfbc81fb0568309d1bd009294 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_OBJECTS_JS_ARRAY_BUFFER_H_
#define V8_OBJECTS_JS_ARRAY_BUFFER_H_

#include "src/objects/backing-store.h"
#include "src/objects/js-objects.h"
#include "torque-generated/bit-fields.h"

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

class ArrayBufferExtension;

#include "torque-generated/src/objects/js-array-buffer-tq.inc"

class JSArrayBuffer
    : public TorqueGeneratedJSArrayBuffer<JSArrayBuffer, JSObject> {
 public:
// The maximum length for JSArrayBuffer's supported by V8.
// On 32-bit architectures we limit this to 2GiB, so that
// we can continue to use CheckBounds with the Unsigned31
// restriction for the length.
#if V8_HOST_ARCH_32_BIT
  static constexpr size_t kMaxByteLength = kMaxInt;
#else
  static constexpr size_t kMaxByteLength = kMaxSafeInteger;
#endif

  // When soft sandbox is enabled, creates entries in external pointer table for
  // all JSArrayBuffer's fields that require soft sandbox protection (backing
  // store pointer, backing store length, etc.).
  // When sandbox is not enabled, it's a no-op.
  inline void AllocateExternalPointerEntries(Isolate* isolate);

  // [byte_length]: length in bytes
  DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)

  // [backing_store]: backing memory for this array
  DECL_GETTER(backing_store, void*)
  inline void set_backing_store(Isolate* isolate, void* value);

  // [extension]: extension object used for GC
  DECL_PRIMITIVE_ACCESSORS(extension, ArrayBufferExtension*)

  // For non-wasm, allocation_length and allocation_base are byte_length and
  // backing_store, respectively.
  inline size_t allocation_length() const;
  inline void* allocation_base() const;

  // [bit_field]: boolean flags
  DECL_PRIMITIVE_ACCESSORS(bit_field, uint32_t)

  // Clear uninitialized padding space. This ensures that the snapshot content
  // is deterministic. Depending on the V8 build mode there could be no padding.
  V8_INLINE void clear_padding();

  // Bit positions for [bit_field].
  DEFINE_TORQUE_GENERATED_JS_ARRAY_BUFFER_FLAGS()

  // [is_external]: true indicates that the embedder is in charge of freeing the
  // backing_store, while is_external == false means that v8 will free the
  // memory block once all ArrayBuffers referencing it are collected by the GC.
  DECL_BOOLEAN_ACCESSORS(is_external)

  // [is_detachable]: false => this buffer cannot be detached.
  DECL_BOOLEAN_ACCESSORS(is_detachable)

  // [was_detached]: true => the buffer was previously detached.
  DECL_BOOLEAN_ACCESSORS(was_detached)

  // [is_asmjs_memory]: true => this buffer was once used as asm.js memory.
  DECL_BOOLEAN_ACCESSORS(is_asmjs_memory)

  // [is_shared]: tells whether this is an ArrayBuffer or a SharedArrayBuffer.
  DECL_BOOLEAN_ACCESSORS(is_shared)

  // Initializes the fields of the ArrayBuffer. The provided backing_store can
  // be nullptr. If it is not nullptr, then the function registers it with
  // src/heap/array-buffer-tracker.h.
  V8_EXPORT_PRIVATE void Setup(SharedFlag shared,
                               std::shared_ptr<BackingStore> backing_store);

  // Attaches the backing store to an already constructed empty ArrayBuffer.
  // This is intended to be used only in ArrayBufferConstructor builtin.
  V8_EXPORT_PRIVATE void Attach(std::shared_ptr<BackingStore> backing_store);
  // Detach the backing store from this array buffer if it is detachable.
  // This sets the internal pointer and length to 0 and unregisters the backing
  // store from the array buffer tracker. If the array buffer is not detachable,
  // this is a nop.
  //
  // Array buffers that wrap wasm memory objects are special in that they
  // are normally not detachable, but can become detached as a side effect
  // of growing the underlying memory object. The {force_for_wasm_memory} flag
  // is used by the implementation of Wasm memory growth in order to bypass the
  // non-detachable check.
  V8_EXPORT_PRIVATE void Detach(bool force_for_wasm_memory = false);

  // Get a reference to backing store of this array buffer, if there is a
  // backing store. Returns nullptr if there is no backing store (e.g. detached
  // or a zero-length array buffer).
  std::shared_ptr<BackingStore> GetBackingStore();

  // Allocates an ArrayBufferExtension for this array buffer, unless it is
  // already associated with an extension.
  ArrayBufferExtension* EnsureExtension();

  // Frees the associated ArrayBufferExtension and returns its backing store.
  std::shared_ptr<BackingStore> RemoveExtension();

  // Marks ArrayBufferExtension
  void MarkExtension();
  void YoungMarkExtension();
  void YoungMarkExtensionPromoted();

  //
  // Serializer/deserializer support.
  //

  // Backing stores are serialized/deserialized separately. During serialization
  // the backing store reference is stored in the backing store field and upon
  // deserialization it is converted back to actual external (off-heap) pointer
  // value.
  inline uint32_t GetBackingStoreRefForDeserialization() const;
  inline void SetBackingStoreRefForSerialization(uint32_t ref);

  // Dispatched behavior.
  DECL_PRINTER(JSArrayBuffer)
  DECL_VERIFIER(JSArrayBuffer)

  static constexpr int kEndOfTaggedFieldsOffset = JSObject::kHeaderSize;

  static const int kSizeWithEmbedderFields =
      kHeaderSize +
      v8::ArrayBuffer::kEmbedderFieldCount * kEmbedderDataSlotSize;

  class BodyDescriptor;

 private:
  inline ArrayBufferExtension** extension_location() const;

#if V8_COMPRESS_POINTERS
  static const int kUninitializedTagMask = 1;

  inline uint32_t* extension_lo() const;
  inline uint32_t* extension_hi() const;
#endif

  TQ_OBJECT_CONSTRUCTORS(JSArrayBuffer)
};

// Each JSArrayBuffer (with a backing store) has a corresponding native-heap
// allocated ArrayBufferExtension for GC purposes and storing the backing store.
// When marking a JSArrayBuffer, the GC also marks the native
// extension-object. The GC periodically iterates all extensions concurrently
// and frees unmarked ones.
// https://docs.google.com/document/d/1-ZrLdlFX1nXT3z-FAgLbKal1gI8Auiaya_My-a0UJ28/edit
class ArrayBufferExtension : public Malloced {
  enum class GcState : uint8_t { Dead = 0, Copied, Promoted };

  std::atomic<bool> marked_;
  std::atomic<GcState> young_gc_state_;
  std::shared_ptr<BackingStore> backing_store_;
  ArrayBufferExtension* next_;
  std::size_t accounting_length_;

  GcState young_gc_state() {
    return young_gc_state_.load(std::memory_order_relaxed);
  }

  void set_young_gc_state(GcState value) {
    young_gc_state_.store(value, std::memory_order_relaxed);
  }

 public:
  ArrayBufferExtension()
      : marked_(false),
        young_gc_state_(GcState::Dead),
        backing_store_(std::shared_ptr<BackingStore>()),
        next_(nullptr),
        accounting_length_(0) {}
  explicit ArrayBufferExtension(std::shared_ptr<BackingStore> backing_store)
      : marked_(false),
        young_gc_state_(GcState::Dead),
        backing_store_(backing_store),
        next_(nullptr),
        accounting_length_(0) {}

  void Mark() { marked_.store(true, std::memory_order_relaxed); }
  void Unmark() { marked_.store(false, std::memory_order_relaxed); }
  bool IsMarked() { return marked_.load(std::memory_order_relaxed); }

  void YoungMark() { set_young_gc_state(GcState::Copied); }
  void YoungMarkPromoted() { set_young_gc_state(GcState::Promoted); }
  void YoungUnmark() { set_young_gc_state(GcState::Dead); }
  bool IsYoungMarked() { return young_gc_state() != GcState::Dead; }

  bool IsYoungPromoted() { return young_gc_state() == GcState::Promoted; }

  std::shared_ptr<BackingStore> backing_store() { return backing_store_; }
  BackingStore* backing_store_raw() { return backing_store_.get(); }

  size_t accounting_length() { return accounting_length_; }

  void set_accounting_length(size_t accounting_length) {
    accounting_length_ = accounting_length;
  }

  std::shared_ptr<BackingStore> RemoveBackingStore() {
    return std::move(backing_store_);
  }

  void set_backing_store(std::shared_ptr<BackingStore> backing_store) {
    backing_store_ = std::move(backing_store);
  }

  void reset_backing_store() { backing_store_.reset(); }

  ArrayBufferExtension* next() { return next_; }
  void set_next(ArrayBufferExtension* extension) { next_ = extension; }
};

class JSArrayBufferView
    : public TorqueGeneratedJSArrayBufferView<JSArrayBufferView, JSObject> {
 public:
  // [byte_offset]: offset of typed array in bytes.
  DECL_PRIMITIVE_ACCESSORS(byte_offset, size_t)

  // [byte_length]: length of typed array in bytes.
  DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)

  DECL_VERIFIER(JSArrayBufferView)

  inline bool WasDetached() const;

  static constexpr int kEndOfTaggedFieldsOffset = kByteOffsetOffset;

  STATIC_ASSERT(IsAligned(kByteOffsetOffset, kUIntptrSize));
  STATIC_ASSERT(IsAligned(kByteLengthOffset, kUIntptrSize));

  TQ_OBJECT_CONSTRUCTORS(JSArrayBufferView)
};

class JSTypedArray
    : public TorqueGeneratedJSTypedArray<JSTypedArray, JSArrayBufferView> {
 public:
  // TODO(v8:4153): This should be equal to JSArrayBuffer::kMaxByteLength
  // eventually.
  static constexpr size_t kMaxLength = v8::TypedArray::kMaxLength;

  // [length]: length of typed array in elements.
  DECL_PRIMITIVE_GETTER(length, size_t)

  DECL_GETTER(base_pointer, Object)
  DECL_ACQUIRE_GETTER(base_pointer, Object)

  // ES6 9.4.5.3
  V8_WARN_UNUSED_RESULT static Maybe<bool> DefineOwnProperty(
      Isolate* isolate, Handle<JSTypedArray> o, Handle<Object> key,
      PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw);

  ExternalArrayType type();
  V8_EXPORT_PRIVATE size_t element_size();

  V8_EXPORT_PRIVATE Handle<JSArrayBuffer> GetBuffer();

  // When soft sandbox is enabled, creates entries in external pointer table for
  // all JSTypedArray's fields that require soft sandbox protection (external
  // pointer, offset, length, etc.).
  // When sandbox is not enabled, it's a no-op.
  inline void AllocateExternalPointerEntries(Isolate* isolate);

  // The `DataPtr` is `base_ptr + external_pointer`, and `base_ptr` is nullptr
  // for off-heap typed arrays.
  static constexpr bool kOffHeapDataPtrEqualsExternalPointer = true;

  // Use with care: returns raw pointer into heap.
  inline void* DataPtr();

  inline void SetOffHeapDataPtr(Isolate* isolate, void* base, Address offset);
  inline void SetOnHeapDataPtr(Isolate* isolate, HeapObject base,
                               Address offset);

  // Whether the buffer's backing store is on-heap or off-heap.
  inline bool is_on_heap() const;
  inline bool is_on_heap(AcquireLoadTag tag) const;

  // Note: this is a pointer compression specific optimization.
  // Normally, on-heap typed arrays contain HeapObject value in |base_pointer|
  // field and an offset in |external_pointer|.
  // When pointer compression is enabled we want to combine decompression with
  // the offset addition. In order to do that we add an isolate root to the
  // |external_pointer| value and therefore the data pointer computation can
  // is a simple addition of a (potentially sign-extended) |base_pointer| loaded
  // as Tagged_t value and an |external_pointer| value.
  // For full-pointer mode the compensation value is zero.
  static inline Address ExternalPointerCompensationForOnHeapArray(
      PtrComprCageBase cage_base);

  //
  // Serializer/deserializer support.
  //

  // External backing stores are serialized/deserialized separately.
  // During serialization the backing store reference is stored in the typed
  // array object and upon deserialization it is converted back to actual
  // external (off-heap) pointer value.
  // The backing store reference is stored in the external_pointer field.
  inline uint32_t GetExternalBackingStoreRefForDeserialization() const;
  inline void SetExternalBackingStoreRefForSerialization(uint32_t ref);

  // Subtracts external pointer compensation from the external pointer value.
  inline void RemoveExternalPointerCompensationForSerialization(
      Isolate* isolate);

  static inline MaybeHandle<JSTypedArray> Validate(Isolate* isolate,
                                                   Handle<Object> receiver,
                                                   const char* method_name);

  // Dispatched behavior.
  DECL_PRINTER(JSTypedArray)
  DECL_VERIFIER(JSTypedArray)

  STATIC_ASSERT(IsAligned(kLengthOffset, kUIntptrSize));
  STATIC_ASSERT(IsAligned(kExternalPointerOffset, kSystemPointerSize));

  static const int kSizeWithEmbedderFields =
      kHeaderSize +
      v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize;

  class BodyDescriptor;

#ifdef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
  static constexpr size_t kMaxSizeInHeap = V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP;
#else
  static constexpr size_t kMaxSizeInHeap = 64;
#endif

 private:
  friend class Deserializer;
  friend class Factory;

  DECL_PRIMITIVE_SETTER(length, size_t)

  DECL_GETTER(external_pointer, Address)
  DECL_GETTER(external_pointer_raw, ExternalPointer_t)

  DECL_SETTER(base_pointer, Object)
  DECL_RELEASE_SETTER(base_pointer, Object)

  inline void set_external_pointer(Isolate* isolate, Address value);

  TQ_OBJECT_CONSTRUCTORS(JSTypedArray)
};

class JSDataView
    : public TorqueGeneratedJSDataView<JSDataView, JSArrayBufferView> {
 public:
  // [data_pointer]: pointer to the actual data.
  DECL_GETTER(data_pointer, void*)
  inline void set_data_pointer(Isolate* isolate, void* value);

  // When soft sandbox is enabled, creates entries in external pointer table for
  // all JSDataView's fields that require soft sandbox protection (data pointer,
  // offset, length, etc.).
  // When sandbox is not enabled, it's a no-op.
  inline void AllocateExternalPointerEntries(Isolate* isolate);

  // Dispatched behavior.
  DECL_PRINTER(JSDataView)
  DECL_VERIFIER(JSDataView)

  STATIC_ASSERT(IsAligned(kDataPointerOffset, kUIntptrSize));

  static const int kSizeWithEmbedderFields =
      kHeaderSize +
      v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize;

  class BodyDescriptor;

  TQ_OBJECT_CONSTRUCTORS(JSDataView)
};

}  // namespace internal
}  // namespace v8

#include "src/objects/object-macros-undef.h"

#endif  // V8_OBJECTS_JS_ARRAY_BUFFER_H_