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