/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/objects/backing-store.h
350 строк
14 KB
Joyee Cheung
deps: V8: backport bef0d9c1bc90
24 апр 2026, 19:01
Не верифицирован
24 апр 2026, 19:01
13fb819
Код
Авторство
О чём код?
// Copyright 2019 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_BACKING_STORE_H_ #define V8_OBJECTS_BACKING_STORE_H_ #include <memory> #include <optional> #include "include/v8-array-buffer.h" #include "include/v8-internal.h" #include "src/base/bit-field.h" #include "src/handles/handles.h" #include "src/sandbox/sandbox.h" namespace v8::internal { class Isolate; class WasmMemoryObject; // Whether this is Wasm memory, and if 32 or 64 bit. enum class WasmMemoryFlag : uint8_t { kNotWasm, kWasmMemory32, kWasmMemory64 }; // Whether the backing store is shared or not. enum class SharedFlag : uint8_t { kNotShared, kShared }; // Whether the backing store is resizable or not. enum class ResizableFlag : uint8_t { kNotResizable, kResizable }; // Whether the backing store is mutable or not. enum class ImmutableFlag : uint8_t { kMutable, kImmutable }; // Whether the backing store memory is initialied to zero or not. enum class InitializedFlag : uint8_t { kUninitialized, kZeroInitialized }; // Internal information for shared wasm memories. E.g. contains // a list of all memory objects (across all isolates) that share this // backing store. struct SharedWasmMemoryData; // The {BackingStore} data structure stores all the low-level details about the // backing store of an array buffer or Wasm memory, including its base address // and length, whether it is shared, provided by the embedder, has guard // regions, etc. Instances of this classes *own* the underlying memory // when they are created through one of the {Allocate()} methods below, // and the destructor frees the memory (and page allocation if necessary). class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase { public: static constexpr ExternalPointerTag kManagedTag = kBackingStoreTag; ~BackingStore(); // Allocate an array buffer backing store using the default method, // which currently is the embedder-provided array buffer allocator. static std::unique_ptr<BackingStore> Allocate(Isolate* isolate, size_t byte_length, SharedFlag shared, InitializedFlag initialized); #if V8_ENABLE_WEBASSEMBLY // Allocate the backing store for a Wasm memory. static std::unique_ptr<BackingStore> AllocateWasmMemory( Isolate* isolate, size_t initial_pages, size_t maximum_pages, WasmMemoryFlag wasm_memory, SharedFlag shared); #endif // V8_ENABLE_WEBASSEMBLY // Tries to allocate `maximum_pages` of memory and commit `initial_pages`. // // If {isolate} is not null, initial failure to allocate the backing store may // trigger GC, after which the allocation is retried. If {isolate} is null, no // GC will be triggered. static std::unique_ptr<BackingStore> TryAllocateAndPartiallyCommitMemory( Isolate* isolate, size_t byte_length, size_t max_byte_length, size_t page_size, size_t initial_pages, size_t maximum_pages, WasmMemoryFlag wasm_memory, SharedFlag shared, bool has_guard_regions = false); // Create a backing store that wraps existing allocated memory. static std::unique_ptr<BackingStore> WrapAllocation( void* allocation_base, size_t allocation_length, v8::BackingStore::DeleterCallback deleter, void* deleter_data, SharedFlag shared); // Create an empty backing store. static std::unique_ptr<BackingStore> EmptyBackingStore(SharedFlag shared); // Accessors. // Internally, we treat nullptr as the empty buffer value. However, // externally, we should use the EmptyBackingStoreBuffer() constant for that // purpose as the buffer pointer should always point into the sandbox. As // such, this is the place where we convert between these two. void* buffer_start() const { return buffer_start_ != nullptr ? buffer_start_ : EmptyBackingStoreBuffer(); } size_t byte_length( std::memory_order memory_order = std::memory_order_relaxed) const { return byte_length_.load(memory_order); } size_t max_byte_length() const { return max_byte_length_; } size_t byte_capacity() const { return byte_capacity_; } bool is_shared() const { return has_flag(kIsShared); } bool is_resizable_by_js() const { return has_flag(kIsResizableByJs); } bool is_immutable() const { return has_flag(kIsImmutable); } bool is_wasm_memory() const { return has_flag(kIsWasmMemory); } bool is_wasm_memory64() const { return has_flag(kIsWasmMemory64); } bool has_guard_regions() const { return has_flag(kHasGuardRegions); } void set_is_immutable(bool immutable) { if (immutable) { set_flag(kIsImmutable); } else { clear_flag(kIsImmutable); } } bool IsEmpty() const { DCHECK_GE(byte_capacity_, byte_length_); return byte_capacity_ == 0; } enum ResizeOrGrowResult { kSuccess, kFailure, kRace }; ResizeOrGrowResult ResizeInPlace(Isolate* isolate, size_t new_byte_length); ResizeOrGrowResult GrowInPlace(Isolate* isolate, size_t new_byte_length); #if V8_ENABLE_WEBASSEMBLY // The IsResizableByJs flag is set for backing stores for a resizable // ArrayBuffer or a WebAssembly.Memory that exposes its buffer as resizable // ArrayBuffer. // // For backing stores of ArrayBuffers that are not Wasm memories, the flag // never changes. It always matches whether ArrayBuffers backed by it are // resizable. // // For unshared Wasm memories, this flag may change. WebAssembly.Memory // instances are born with their buffers exposed as fixed-length ArrayBuffers // (for backwards compat), but may transition to exposing their buffers as // resizable. It always matches whether the ArrayBuffer it backs is resizable, // since unshared ArrayBuffers never alias the same BackingStore. // // For shared Wasm memories, this field never changes, but may differ from the // value of the is_resizable_by_js field of SharedArrayBuffers it backs. // WebAssembly.Memory can create multiple SharedArrayBuffers backed by the // same BackingStore, some of which are exposed as growable, and some of which // as fixed-length. void MakeWasmMemoryResizableByJS(bool resizable); // Attempt to grow this backing store in place. std::optional<size_t> GrowWasmMemoryInPlace(Isolate* isolate, size_t delta_pages, size_t max_pages); // Allocate a new, larger, backing store for this Wasm memory and copy the // contents of this backing store into it. std::unique_ptr<BackingStore> CopyWasmMemory(Isolate* isolate, size_t new_pages, size_t max_pages, WasmMemoryFlag wasm_memory); // Attach the given memory object to this backing store. The memory object // will be updated if this backing store is grown. void AttachSharedWasmMemoryObject( Isolate* isolate, DirectHandle<WasmMemoryObject> memory_object); // Send asynchronous updates to attached memory objects in other isolates // after the backing store has been grown. Memory objects in this // isolate are updated synchronously. void BroadcastSharedWasmMemoryGrow(Isolate* isolate) const; // Remove all memory objects in the given isolate that refer to this // backing store. static void RemoveSharedWasmMemoryObjects(Isolate* isolate); // Update all shared memory objects in this isolate (after a grow operation). static void UpdateSharedWasmMemoryObjects(Isolate* isolate); #endif // V8_ENABLE_WEBASSEMBLY // Returns the size of the external memory owned by this backing store. // It is used for triggering GCs based on the external memory pressure. size_t PerIsolateAccountingLength() { // TODO(titzer): SharedArrayBuffers and shared WasmMemorys cause problems // with accounting for per-isolate external memory. In particular, sharing // the same array buffer or memory multiple times, which happens in stress // tests, can cause overcounting, leading to GC thrashing. Fix with global // accounting? if (has_flag(kIsShared)) return 0; // Wasm backing stores are already accounted for via the // `Managed<BackingStore>` stored in the `WasmMemoryObject`. The latter is // referenced from the `JSArrayBuffer` via the // `array_buffer_wasm_memory_symbol`. (There's a IfChange -> ThenChange lint // check in place for this.) if (is_wasm_memory()) return 0; // Check if the backing store has an empty deleter. Even if the backing // store is freed after GC, it would not free the memory block. if (has_flag(kEmptyDeleter)) return 0; return byte_length(); } uint32_t id() const { return id_; } // Return the size of the reservation needed for a wasm backing store. static size_t GetWasmReservationSize(bool has_guard_regions, size_t byte_capacity, bool is_wasm_memory64); private: friend class GlobalBackingStoreRegistry; enum Flag { kIsShared, kIsResizableByJs, kIsImmutable, kIsWasmMemory, kIsWasmMemory64, kHoldsSharedPtrToAllocater, kHasGuardRegions, kGloballyRegistered, kCustomDeleter, kEmptyDeleter }; BackingStore(void* buffer_start, size_t byte_length, size_t max_byte_length, size_t byte_capacity, SharedFlag shared, ResizableFlag resizable, ImmutableFlag immutable, bool is_wasm_memory, bool is_wasm_memory64, bool has_guard_regions, bool custom_deleter, bool empty_deleter); BackingStore(const BackingStore&) = delete; BackingStore& operator=(const BackingStore&) = delete; void SetAllocatorFromIsolate(Isolate* isolate); // Accessors for type-specific data. v8::ArrayBuffer::Allocator* get_v8_api_array_buffer_allocator(); SharedWasmMemoryData* get_shared_wasm_memory_data() const; bool has_flag(Flag flag) const { return flags_.load(std::memory_order_relaxed).contains(flag); } void set_flag(Flag flag) { base::EnumSet<Flag, uint16_t> old_flags = flags_.load(std::memory_order_relaxed); while (!flags_.compare_exchange_weak(old_flags, old_flags | flag, std::memory_order_relaxed)) { // Retry with updated `old_flags`. } } void clear_flag(Flag flag) { base::EnumSet<Flag, uint16_t> old_flags = flags_.load(std::memory_order_relaxed); while (!flags_.compare_exchange_weak(old_flags, old_flags - flag, std::memory_order_relaxed)) { // Retry with updated `old_flags`. } } bool holds_shared_ptr_to_allocator() const { return has_flag(kHoldsSharedPtrToAllocater); } bool custom_deleter() const { return has_flag(kCustomDeleter); } bool globally_registered() const { return has_flag(kGloballyRegistered); } #ifdef V8_ENABLE_SANDBOX void set_page_allocator(std::weak_ptr<v8::PageAllocator> page_allocator) { page_allocator_ = std::move(page_allocator); } #endif void* buffer_start_ = nullptr; std::atomic<size_t> byte_length_; // Max byte length of the corresponding JSArrayBuffer(s). size_t max_byte_length_; // Amount of the memory allocated size_t byte_capacity_; // Unique ID of this backing store. Currently only used by DevTools, to // identify stores used by several ArrayBuffers or WebAssembly memories // (reported by the inspector as [[ArrayBufferData]] internal property) const uint32_t id_; #ifdef V8_ENABLE_SANDBOX std::weak_ptr<v8::PageAllocator> page_allocator_; #endif union TypeSpecificData { TypeSpecificData() : v8_api_array_buffer_allocator(nullptr) {} ~TypeSpecificData() {} // If this backing store was allocated through the ArrayBufferAllocator API, // this is a direct pointer to the API object for freeing the backing // store. v8::ArrayBuffer::Allocator* v8_api_array_buffer_allocator; // Holds a shared_ptr to the ArrayBuffer::Allocator instance, if requested // so by the embedder through setting // Isolate::CreateParams::array_buffer_allocator_shared. std::shared_ptr<v8::ArrayBuffer::Allocator> v8_api_array_buffer_allocator_shared; // For shared Wasm memories, this is a list of all the attached memory // objects, which is needed to grow shared backing stores. SharedWasmMemoryData* shared_wasm_memory_data; // Custom deleter for the backing stores that wrap memory blocks that are // allocated with a custom allocator. struct DeleterInfo { v8::BackingStore::DeleterCallback callback; void* data; } deleter; } type_specific_data_; std::atomic<base::EnumSet<Flag, uint16_t>> flags_; }; // A global, per-process mapping from buffer addresses to backing stores // of wasm memory objects. class GlobalBackingStoreRegistry { public: // Register a backing store in the global registry. A mapping from the // {buffer_start} to the backing store object will be added. The backing // store will automatically unregister itself upon destruction. // Only wasm memory backing stores are supported. static void Register(std::shared_ptr<BackingStore> backing_store); private: friend class BackingStore; // Unregister a backing store in the global registry. static void Unregister(BackingStore* backing_store); // Adds the given memory object to the backing store's weak list // of memory objects (under the registry lock). static void AddSharedWasmMemoryObject( Isolate* isolate, BackingStore* backing_store, DirectHandle<WasmMemoryObject> memory_object); // Purge any shared wasm memory lists that refer to this isolate. static void Purge(Isolate* isolate); // Broadcast updates to all attached memory objects. static void BroadcastSharedWasmMemoryGrow(Isolate* isolate, const BackingStore* backing_store); // Update all shared memory objects in the given isolate. static void UpdateSharedWasmMemoryObjects(Isolate* isolate); }; } // namespace v8::internal #endif // V8_OBJECTS_BACKING_STORE_H_