js-array-buffer.h 13.6 KB
Newer Older
1 2 3 4 5 6 7
// 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_

8
#include "src/objects/backing-store.h"
9
#include "src/objects/js-objects.h"
10
#include "torque-generated/bit-fields.h"
11 12 13 14 15 16 17

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

namespace v8 {
namespace internal {

18 19
class ArrayBufferExtension;

20 21
class JSArrayBuffer
    : public TorqueGeneratedJSArrayBuffer<JSArrayBuffer, JSObject> {
22
 public:
23 24 25 26 27 28 29 30 31 32
// 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

33 34 35 36 37 38
  // 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);

39
  // [byte_length]: length in bytes
40
  DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
41 42

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

  // [extension]: extension object used for GC
47
  DECL_PRIMITIVE_ACCESSORS(extension, ArrayBufferExtension*)
48 49 50 51 52 53

  // 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;

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

57 58 59 60
  // 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();

61
  // Bit positions for [bit_field].
62
  DEFINE_TORQUE_GENERATED_JS_ARRAY_BUFFER_FLAGS()
63 64 65 66

  // [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.
67 68
  DECL_BOOLEAN_ACCESSORS(is_external)

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

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

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

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

81 82 83 84 85 86 87 88
  // 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.
89
  V8_EXPORT_PRIVATE void Attach(std::shared_ptr<BackingStore> backing_store);
90 91 92 93
  // 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.
94 95 96 97 98 99
  //
  // 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.
100
  V8_EXPORT_PRIVATE void Detach(bool force_for_wasm_memory = false);
101 102 103 104 105

  // 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();
106

107 108
  // Allocates an ArrayBufferExtension for this array buffer, unless it is
  // already associated with an extension.
109
  ArrayBufferExtension* EnsureExtension();
110 111 112 113 114 115

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

  // Marks ArrayBufferExtension
  void MarkExtension();
116 117
  void YoungMarkExtension();
  void YoungMarkExtensionPromoted();
118

119 120 121 122 123 124 125 126 127 128 129
  //
  // 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);

130 131 132 133
  // Dispatched behavior.
  DECL_PRINTER(JSArrayBuffer)
  DECL_VERIFIER(JSArrayBuffer)

134
  static constexpr int kEndOfTaggedFieldsOffset = JSObject::kHeaderSize;
135 136

  static const int kSizeWithEmbedderFields =
137 138
      kHeaderSize +
      v8::ArrayBuffer::kEmbedderFieldCount * kEmbedderDataSlotSize;
139 140 141

  class BodyDescriptor;

142 143
 private:
  inline ArrayBufferExtension** extension_location() const;
144 145 146 147 148 149 150

#if V8_COMPRESS_POINTERS
  static const int kUninitializedTagMask = 1;

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

  TQ_OBJECT_CONSTRUCTORS(JSArrayBuffer)
153 154 155 156 157 158 159 160 161
};

// 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 {
162 163
  enum class GcState : uint8_t { Dead = 0, Copied, Promoted };

164
  std::atomic<bool> marked_;
165
  std::atomic<GcState> young_gc_state_;
166 167
  std::shared_ptr<BackingStore> backing_store_;
  ArrayBufferExtension* next_;
168
  std::size_t accounting_length_;
169

170 171 172 173 174 175 176 177
  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);
  }

178 179 180
 public:
  ArrayBufferExtension()
      : marked_(false),
181
        young_gc_state_(GcState::Dead),
182
        backing_store_(std::shared_ptr<BackingStore>()),
183 184
        next_(nullptr),
        accounting_length_(0) {}
185
  explicit ArrayBufferExtension(std::shared_ptr<BackingStore> backing_store)
186 187 188
      : marked_(false),
        young_gc_state_(GcState::Dead),
        backing_store_(backing_store),
189 190
        next_(nullptr),
        accounting_length_(0) {}
191 192 193 194 195

  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); }

196 197 198 199 200 201 202
  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; }

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

206 207 208 209 210 211
  size_t accounting_length() { return accounting_length_; }

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

212 213 214 215 216 217 218 219 220 221 222 223
  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; }
224 225
};

226 227
class JSArrayBufferView
    : public TorqueGeneratedJSArrayBufferView<JSArrayBufferView, JSObject> {
228 229
 public:
  // [byte_offset]: offset of typed array in bytes.
230
  DECL_PRIMITIVE_ACCESSORS(byte_offset, size_t)
231 232

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

  DECL_VERIFIER(JSArrayBufferView)

237
  inline bool WasDetached() const;
238

239
  static constexpr int kEndOfTaggedFieldsOffset = kByteOffsetOffset;
240

241 242 243
  STATIC_ASSERT(IsAligned(kByteOffsetOffset, kUIntptrSize));
  STATIC_ASSERT(IsAligned(kByteLengthOffset, kUIntptrSize));

244
  TQ_OBJECT_CONSTRUCTORS(JSArrayBufferView)
245 246
};

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

254
  // [length]: length of typed array in elements.
255
  DECL_PRIMITIVE_ACCESSORS(length, size_t)
256 257 258 259

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

  ExternalArrayType type();
  V8_EXPORT_PRIVATE size_t element_size();

265
  V8_EXPORT_PRIVATE Handle<JSArrayBuffer> GetBuffer();
266

267 268 269 270 271 272
  // 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);

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

276 277 278
  inline void SetOffHeapDataPtr(Isolate* isolate, void* base, Address offset);
  inline void SetOnHeapDataPtr(Isolate* isolate, HeapObject base,
                               Address offset);
279

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

283 284 285 286 287 288 289 290 291 292
  // 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(
293
      const Isolate* isolate);
294

295 296 297 298 299 300 301 302 303 304 305 306
  //
  // 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);

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

311 312 313 314 315 316 317 318
  static inline MaybeHandle<JSTypedArray> Validate(Isolate* isolate,
                                                   Handle<Object> receiver,
                                                   const char* method_name);

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

319 320 321
  STATIC_ASSERT(IsAligned(kLengthOffset, kUIntptrSize));
  STATIC_ASSERT(IsAligned(kExternalPointerOffset, kSystemPointerSize));

322
  static const int kSizeWithEmbedderFields =
323 324
      kHeaderSize +
      v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize;
325

326 327
  class BodyDescriptor;

328 329 330 331 332 333
#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

334
 private:
335 336 337
  friend class Deserializer;

  // [external_pointer]: TODO(v8:4153)
338
  DECL_GETTER(external_pointer, Address)
339 340
  DECL_GETTER(external_pointer_raw, ExternalPointer_t)

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

343
  TQ_OBJECT_CONSTRUCTORS(JSTypedArray)
344 345
};

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

353 354 355 356 357 358
  // 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);

359 360 361 362
  // Dispatched behavior.
  DECL_PRINTER(JSDataView)
  DECL_VERIFIER(JSDataView)

363 364
  STATIC_ASSERT(IsAligned(kDataPointerOffset, kUIntptrSize));

365
  static const int kSizeWithEmbedderFields =
366 367
      kHeaderSize +
      v8::ArrayBufferView::kEmbedderFieldCount * kEmbedderDataSlotSize;
368

369 370
  class BodyDescriptor;

371
  TQ_OBJECT_CONSTRUCTORS(JSDataView)
372 373 374 375 376 377 378 379
};

}  // namespace internal
}  // namespace v8

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

#endif  // V8_OBJECTS_JS_ARRAY_BUFFER_H_