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