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