| // 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_SLOTS_H_ |
| #define V8_OBJECTS_SLOTS_H_ |
| |
| #include "src/base/memory.h" |
| #include "src/common/assert-scope.h" |
| #include "src/common/globals.h" |
| #include "src/sandbox/external-pointer-table.h" |
| #include "src/sandbox/external-pointer.h" |
| #include "src/sandbox/indirect-pointer-tag.h" |
| #include "src/sandbox/isolate.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| class Object; |
| class ExposedTrustedObject; |
| using TaggedBase = TaggedImpl<HeapObjectReferenceType::STRONG, Address>; |
| |
| template <typename Subclass, typename Data, |
| size_t SlotDataAlignment = sizeof(Data)> |
| class SlotBase { |
| public: |
| using TData = Data; |
| |
| static constexpr size_t kSlotDataSize = sizeof(Data); |
| static constexpr size_t kSlotDataAlignment = SlotDataAlignment; |
| |
| Subclass& operator++() { // Prefix increment. |
| ptr_ += kSlotDataSize; |
| return *static_cast<Subclass*>(this); |
| } |
| Subclass operator++(int) { // Postfix increment. |
| Subclass result = *static_cast<Subclass*>(this); |
| ptr_ += kSlotDataSize; |
| return result; |
| } |
| Subclass& operator--() { // Prefix decrement. |
| ptr_ -= kSlotDataSize; |
| return *static_cast<Subclass*>(this); |
| } |
| Subclass operator--(int) { // Postfix decrement. |
| Subclass result = *static_cast<Subclass*>(this); |
| ptr_ -= kSlotDataSize; |
| return result; |
| } |
| |
| bool operator<(const SlotBase& other) const { return ptr_ < other.ptr_; } |
| bool operator<=(const SlotBase& other) const { return ptr_ <= other.ptr_; } |
| bool operator>(const SlotBase& other) const { return ptr_ > other.ptr_; } |
| bool operator>=(const SlotBase& other) const { return ptr_ >= other.ptr_; } |
| bool operator==(const SlotBase& other) const { return ptr_ == other.ptr_; } |
| bool operator!=(const SlotBase& other) const { return ptr_ != other.ptr_; } |
| size_t operator-(const SlotBase& other) const { |
| DCHECK_GE(ptr_, other.ptr_); |
| return static_cast<size_t>((ptr_ - other.ptr_) / kSlotDataSize); |
| } |
| Subclass operator-(int i) const { return Subclass(ptr_ - i * kSlotDataSize); } |
| Subclass operator+(int i) const { return Subclass(ptr_ + i * kSlotDataSize); } |
| friend Subclass operator+(int i, const Subclass& slot) { |
| return Subclass(slot.ptr_ + i * kSlotDataSize); |
| } |
| Subclass& operator+=(int i) { |
| ptr_ += i * kSlotDataSize; |
| return *static_cast<Subclass*>(this); |
| } |
| Subclass operator-(int i) { return Subclass(ptr_ - i * kSlotDataSize); } |
| Subclass& operator-=(int i) { |
| ptr_ -= i * kSlotDataSize; |
| return *static_cast<Subclass*>(this); |
| } |
| |
| void* ToVoidPtr() const { return reinterpret_cast<void*>(address()); } |
| |
| Address address() const { return ptr_; } |
| // For symmetry with Handle. |
| TData* location() const { return reinterpret_cast<TData*>(ptr_); } |
| |
| protected: |
| explicit SlotBase(Address ptr) : ptr_(ptr) { |
| DCHECK(IsAligned(ptr, kSlotDataAlignment)); |
| } |
| |
| private: |
| // This field usually describes an on-heap address (a slot within an object), |
| // so its type should not be a pointer to another C++ wrapper class. |
| // Type safety is provided by well-defined conversion operations. |
| Address ptr_; |
| }; |
| |
| // An FullObjectSlot instance describes a kSystemPointerSize-sized field |
| // ("slot") holding a tagged pointer (smi or strong heap object). |
| // Its address() is the address of the slot. |
| // The slot's contents can be read and written using operator* and store(). |
| class FullObjectSlot : public SlotBase<FullObjectSlot, Address> { |
| public: |
| using TObject = Tagged<Object>; |
| using THeapObjectSlot = FullHeapObjectSlot; |
| |
| // Tagged value stored in this slot is guaranteed to never be a weak pointer. |
| static constexpr bool kCanBeWeak = false; |
| |
| FullObjectSlot() : SlotBase(kNullAddress) {} |
| explicit FullObjectSlot(Address ptr) : SlotBase(ptr) {} |
| explicit FullObjectSlot(const Address* ptr) |
| : SlotBase(reinterpret_cast<Address>(ptr)) {} |
| inline explicit FullObjectSlot(TaggedBase* object); |
| template <typename T> |
| explicit FullObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot) |
| : SlotBase(slot.address()) {} |
| |
| // Compares memory representation of a value stored in the slot with given |
| // raw value. |
| inline bool contains_map_value(Address raw_value) const; |
| inline bool Relaxed_ContainsMapValue(Address raw_value) const; |
| |
| inline Tagged<Object> operator*() const; |
| inline Tagged<Object> load() const; |
| inline Tagged<Object> load(PtrComprCageBase cage_base) const; |
| inline void store(Tagged<Object> value) const; |
| inline void store_map(Tagged<Map> map) const; |
| |
| inline Tagged<Map> load_map() const; |
| |
| inline Tagged<Object> Acquire_Load() const; |
| inline Tagged<Object> Acquire_Load(PtrComprCageBase cage_base) const; |
| inline Tagged<Object> Relaxed_Load() const; |
| inline Tagged<Object> Relaxed_Load(PtrComprCageBase cage_base) const; |
| inline Address Relaxed_Load_Raw() const; |
| static inline Tagged<Object> RawToTagged(PtrComprCageBase cage_base, |
| Address raw); |
| inline void Relaxed_Store(Tagged<Object> value) const; |
| inline void Release_Store(Tagged<Object> value) const; |
| inline Tagged<Object> Relaxed_CompareAndSwap(Tagged<Object> old, |
| Tagged<Object> target) const; |
| inline Tagged<Object> Release_CompareAndSwap(Tagged<Object> old, |
| Tagged<Object> target) const; |
| }; |
| |
| // A FullMaybeObjectSlot instance describes a kSystemPointerSize-sized field |
| // ("slot") holding a possibly-weak tagged pointer (think: Tagged<MaybeObject>). |
| // Its address() is the address of the slot. |
| // The slot's contents can be read and written using operator* and store(). |
| class FullMaybeObjectSlot |
| : public SlotBase<FullMaybeObjectSlot, Address, kSystemPointerSize> { |
| public: |
| using TObject = Tagged<MaybeObject>; |
| using THeapObjectSlot = FullHeapObjectSlot; |
| |
| // Tagged value stored in this slot can be a weak pointer. |
| static constexpr bool kCanBeWeak = true; |
| |
| FullMaybeObjectSlot() : SlotBase(kNullAddress) {} |
| explicit FullMaybeObjectSlot(Address ptr) : SlotBase(ptr) {} |
| explicit FullMaybeObjectSlot(TaggedBase* ptr) |
| : SlotBase(reinterpret_cast<Address>(ptr)) {} |
| explicit FullMaybeObjectSlot(Tagged<MaybeObject>* ptr) |
| : SlotBase(reinterpret_cast<Address>(ptr)) {} |
| template <typename T> |
| explicit FullMaybeObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot) |
| : SlotBase(slot.address()) {} |
| |
| inline Tagged<MaybeObject> operator*() const; |
| inline Tagged<MaybeObject> load(PtrComprCageBase cage_base) const; |
| inline void store(Tagged<MaybeObject> value) const; |
| |
| inline Tagged<MaybeObject> Relaxed_Load() const; |
| inline Tagged<MaybeObject> Relaxed_Load(PtrComprCageBase cage_base) const; |
| inline Address Relaxed_Load_Raw() const; |
| static inline Tagged<Object> RawToTagged(PtrComprCageBase cage_base, |
| Address raw); |
| inline void Relaxed_Store(Tagged<MaybeObject> value) const; |
| inline void Release_CompareAndSwap(Tagged<MaybeObject> old, |
| Tagged<MaybeObject> target) const; |
| }; |
| |
| // A FullHeapObjectSlot instance describes a kSystemPointerSize-sized field |
| // ("slot") holding a weak or strong pointer to a heap object (think: |
| // Tagged<HeapObjectReference>). |
| // Its address() is the address of the slot. |
| // The slot's contents can be read and written using operator* and store(). |
| // In case it is known that that slot contains a strong heap object pointer, |
| // ToHeapObject() can be used to retrieve that heap object. |
| class FullHeapObjectSlot : public SlotBase<FullHeapObjectSlot, Address> { |
| public: |
| FullHeapObjectSlot() : SlotBase(kNullAddress) {} |
| explicit FullHeapObjectSlot(Address ptr) : SlotBase(ptr) {} |
| explicit FullHeapObjectSlot(TaggedBase* ptr) |
| : SlotBase(reinterpret_cast<Address>(ptr)) {} |
| template <typename T> |
| explicit FullHeapObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot) |
| : SlotBase(slot.address()) {} |
| |
| inline Tagged<HeapObjectReference> operator*() const; |
| inline Tagged<HeapObjectReference> load(PtrComprCageBase cage_base) const; |
| inline void store(Tagged<HeapObjectReference> value) const; |
| |
| inline Tagged<HeapObject> ToHeapObject() const; |
| |
| inline void StoreHeapObject(Tagged<HeapObject> value) const; |
| }; |
| |
| // TODO(ishell, v8:8875): When pointer compression is enabled the [u]intptr_t |
| // and double fields are only kTaggedSize aligned so in order to avoid undefined |
| // behavior in C++ code we use this iterator adaptor when using STL algorithms |
| // with unaligned pointers. |
| // It will be removed once all v8:8875 is fixed and all the full pointer and |
| // double values in compressed V8 heap are properly aligned. |
| template <typename T> |
| class UnalignedSlot : public SlotBase<UnalignedSlot<T>, T, 1> { |
| public: |
| // This class is a stand-in for "T&" that uses custom read/write operations |
| // for the actual memory accesses. |
| class Reference { |
| public: |
| explicit Reference(Address address) : address_(address) {} |
| Reference(const Reference&) V8_NOEXCEPT = default; |
| |
| Reference& operator=(const Reference& other) V8_NOEXCEPT { |
| base::WriteUnalignedValue<T>(address_, other.value()); |
| return *this; |
| } |
| Reference& operator=(T value) { |
| base::WriteUnalignedValue<T>(address_, value); |
| return *this; |
| } |
| |
| // Values of type UnalignedSlot::reference must be implicitly convertible |
| // to UnalignedSlot::value_type. |
| operator T() const { return value(); } |
| |
| void swap(Reference& other) { |
| T tmp = value(); |
| base::WriteUnalignedValue<T>(address_, other.value()); |
| base::WriteUnalignedValue<T>(other.address_, tmp); |
| } |
| |
| bool operator<(const Reference& other) const { |
| return value() < other.value(); |
| } |
| |
| bool operator==(const Reference& other) const { |
| return value() == other.value(); |
| } |
| |
| private: |
| T value() const { return base::ReadUnalignedValue<T>(address_); } |
| |
| Address address_; |
| }; |
| |
| // The rest of this class follows C++'s "RandomAccessIterator" requirements. |
| // Most of the heavy lifting is inherited from SlotBase. |
| using difference_type = int; |
| using value_type = T; |
| using reference = Reference; |
| using pointer = T*; |
| using iterator_category = std::random_access_iterator_tag; |
| |
| UnalignedSlot() : SlotBase<UnalignedSlot<T>, T, 1>(kNullAddress) {} |
| explicit UnalignedSlot(Address address) |
| : SlotBase<UnalignedSlot<T>, T, 1>(address) {} |
| explicit UnalignedSlot(T* address) |
| : SlotBase<UnalignedSlot<T>, T, 1>(reinterpret_cast<Address>(address)) {} |
| |
| Reference operator*() const { |
| return Reference(SlotBase<UnalignedSlot<T>, T, 1>::address()); |
| } |
| Reference operator[](difference_type i) const { |
| return Reference(SlotBase<UnalignedSlot<T>, T, 1>::address() + |
| i * sizeof(T)); |
| } |
| |
| friend void swap(Reference lhs, Reference rhs) { lhs.swap(rhs); } |
| |
| friend difference_type operator-(UnalignedSlot a, UnalignedSlot b) { |
| return static_cast<int>(a.address() - b.address()) / sizeof(T); |
| } |
| }; |
| |
| // An off-heap uncompressed object slot can be the same as an on-heap one, with |
| // a few methods deleted. |
| class OffHeapFullObjectSlot : public FullObjectSlot { |
| public: |
| OffHeapFullObjectSlot() : FullObjectSlot() {} |
| explicit OffHeapFullObjectSlot(Address ptr) : FullObjectSlot(ptr) {} |
| explicit OffHeapFullObjectSlot(const Address* ptr) : FullObjectSlot(ptr) {} |
| |
| inline Tagged<Object> operator*() const = delete; |
| |
| using FullObjectSlot::Relaxed_Load; |
| }; |
| |
| // An ExternalPointerSlot instance describes a kExternalPointerSlotSize-sized |
| // field ("slot") holding a pointer to objects located outside the V8 heap and |
| // V8 sandbox (think: ExternalPointer_t). |
| // It's basically an ExternalPointer_t* but abstracting away the fact that the |
| // pointer might not be kExternalPointerSlotSize-aligned in certain |
| // configurations. Its address() is the address of the slot. |
| class ExternalPointerSlot |
| : public SlotBase<ExternalPointerSlot, ExternalPointer_t, |
| kTaggedSize /* slot alignment */> { |
| public: |
| ExternalPointerSlot() |
| : SlotBase(kNullAddress) |
| #ifdef V8_COMPRESS_POINTERS |
| , |
| tag_(kExternalPointerNullTag) |
| #endif |
| { |
| } |
| |
| explicit ExternalPointerSlot(Address ptr, ExternalPointerTag tag) |
| : SlotBase(ptr) |
| #ifdef V8_COMPRESS_POINTERS |
| , |
| tag_(tag) |
| #endif |
| { |
| } |
| |
| template <ExternalPointerTag tag> |
| explicit ExternalPointerSlot(ExternalPointerMember<tag>* member) |
| : SlotBase(member->storage_address()) |
| #ifdef V8_COMPRESS_POINTERS |
| , |
| tag_(tag) |
| #endif |
| { |
| } |
| |
| inline void init(IsolateForSandbox isolate, Tagged<HeapObject> host, |
| Address value); |
| |
| #ifdef V8_COMPRESS_POINTERS |
| // When the external pointer is sandboxed, or for array buffer extensions when |
| // pointer compression is on, its slot stores a handle to an entry in an |
| // ExternalPointerTable. These methods allow access to the underlying handle |
| // while the load/store methods below resolve the handle to the real pointer. |
| // Handles should generally be accessed atomically as they may be accessed |
| // from other threads, for example GC marking threads. |
| // |
| // TODO(wingo): Remove if we switch to use the EPT for all external pointers |
| // when pointer compression is enabled. |
| bool HasExternalPointerHandle() const { |
| return V8_ENABLE_SANDBOX_BOOL || tag() == kArrayBufferExtensionTag; |
| } |
| inline ExternalPointerHandle Relaxed_LoadHandle() const; |
| inline void Relaxed_StoreHandle(ExternalPointerHandle handle) const; |
| inline void Release_StoreHandle(ExternalPointerHandle handle) const; |
| #endif // V8_COMPRESS_POINTERS |
| |
| inline Address load(IsolateForSandbox isolate); |
| inline void store(IsolateForSandbox isolate, Address value); |
| |
| // ExternalPointerSlot serialization support. |
| // These methods can be used to clear an external pointer slot prior to |
| // serialization and restore it afterwards. This is useful in cases where the |
| // external pointer is not contained in the snapshot but will instead be |
| // reconstructed during deserialization. |
| // Note that GC must be disallowed while an object's external slot is cleared |
| // as otherwise the corresponding entry in the external pointer table may not |
| // be marked as alive. |
| using RawContent = ExternalPointer_t; |
| inline RawContent GetAndClearContentForSerialization( |
| const DisallowGarbageCollection& no_gc); |
| inline void RestoreContentAfterSerialization( |
| RawContent content, const DisallowGarbageCollection& no_gc); |
| // The ReadOnlySerializer replaces the RawContent in-place. |
| inline void ReplaceContentWithIndexForSerialization( |
| const DisallowGarbageCollection& no_gc, uint32_t index); |
| inline uint32_t GetContentAsIndexAfterDeserialization( |
| const DisallowGarbageCollection& no_gc); |
| |
| #ifdef V8_COMPRESS_POINTERS |
| ExternalPointerTag tag() const { return tag_; } |
| #else |
| ExternalPointerTag tag() const { return kExternalPointerNullTag; } |
| #endif // V8_COMPRESS_POINTERS |
| |
| private: |
| #ifdef V8_COMPRESS_POINTERS |
| ExternalPointerHandle* handle_location() const { |
| DCHECK(HasExternalPointerHandle()); |
| return reinterpret_cast<ExternalPointerHandle*>(address()); |
| } |
| |
| // The tag associated with this slot. |
| ExternalPointerTag tag_; |
| #endif // V8_COMPRESS_POINTERS |
| }; |
| |
| // Similar to ExternalPointerSlot with the difference that it refers to an |
| // `CppHeapPointer_t` which has different sizing and alignment than |
| // `ExternalPointer_t`. |
| class CppHeapPointerSlot |
| : public SlotBase<CppHeapPointerSlot, CppHeapPointer_t, |
| /*SlotDataAlignment=*/sizeof(CppHeapPointer_t)> { |
| public: |
| CppHeapPointerSlot() |
| : SlotBase(kNullAddress) |
| #ifdef V8_COMPRESS_POINTERS |
| , |
| tag_(kExternalPointerNullTag) |
| #endif |
| { |
| } |
| |
| CppHeapPointerSlot(Address ptr, ExternalPointerTag tag) |
| : SlotBase(ptr) |
| #ifdef V8_COMPRESS_POINTERS |
| , |
| tag_(tag) |
| #endif |
| { |
| } |
| |
| #ifdef V8_COMPRESS_POINTERS |
| |
| // When V8 runs with pointer compression, the slots here store a handle to an |
| // entry in a dedicated ExternalPointerTable that is only used for CppHeap |
| // references. These methods allow access to the underlying handle while the |
| // load/store methods below resolve the handle to the real pointer. Handles |
| // should generally be accessed atomically as they may be accessed from other |
| // threads, for example GC marking threads. |
| inline CppHeapPointerHandle Relaxed_LoadHandle() const; |
| inline void Relaxed_StoreHandle(CppHeapPointerHandle handle) const; |
| inline void Release_StoreHandle(CppHeapPointerHandle handle) const; |
| |
| #endif // V8_COMPRESS_POINTERS |
| |
| inline Address try_load(IsolateForPointerCompression isolate) const; |
| inline void store(IsolateForPointerCompression isolate, Address value) const; |
| inline void init() const; |
| |
| #ifdef V8_COMPRESS_POINTERS |
| ExternalPointerTag tag() const { return tag_; } |
| #else |
| ExternalPointerTag tag() const { return kExternalPointerNullTag; } |
| #endif // V8_COMPRESS_POINTERS |
| |
| private: |
| #ifdef V8_COMPRESS_POINTERS |
| // The tag associated with this slot. |
| ExternalPointerTag tag_; |
| #endif // V8_COMPRESS_POINTERS |
| }; |
| |
| // An IndirectPointerSlot instance describes a 32-bit field ("slot") containing |
| // an IndirectPointerHandle, i.e. an index to an entry in a pointer table which |
| // contains the "real" pointer to the referenced HeapObject. These slots are |
| // used when the sandbox is enabled to securely reference HeapObjects outside |
| // of the sandbox. |
| class IndirectPointerSlot |
| : public SlotBase<IndirectPointerSlot, IndirectPointerHandle, |
| kTaggedSize /* slot alignment */> { |
| public: |
| IndirectPointerSlot() |
| : SlotBase(kNullAddress) |
| #ifdef V8_ENABLE_SANDBOX |
| , |
| tag_(kIndirectPointerNullTag) |
| #endif |
| { |
| } |
| |
| explicit IndirectPointerSlot(Address ptr, IndirectPointerTag tag) |
| : SlotBase(ptr) |
| #ifdef V8_ENABLE_SANDBOX |
| , |
| tag_(tag) |
| #endif |
| { |
| } |
| |
| // Even though only HeapObjects can be stored into an IndirectPointerSlot, |
| // these slots can be empty (containing kNullIndirectPointerHandle), in which |
| // case load() will return Smi::zero(). |
| inline Tagged<Object> load(IsolateForSandbox isolate) const; |
| inline void store(Tagged<ExposedTrustedObject> value) const; |
| |
| // Load the value of this slot. |
| // The isolate parameter is required unless using the kCodeTag tag, as these |
| // object use a different pointer table. |
| inline Tagged<Object> Relaxed_Load(IsolateForSandbox isolate) const; |
| inline Tagged<Object> Acquire_Load(IsolateForSandbox isolate) const; |
| |
| // Store a reference to the given object into this slot. The object must be |
| // indirectly refereceable. |
| inline void Relaxed_Store(Tagged<ExposedTrustedObject> value) const; |
| inline void Release_Store(Tagged<ExposedTrustedObject> value) const; |
| |
| inline IndirectPointerHandle Relaxed_LoadHandle() const; |
| inline IndirectPointerHandle Acquire_LoadHandle() const; |
| inline void Relaxed_StoreHandle(IndirectPointerHandle handle) const; |
| inline void Release_StoreHandle(IndirectPointerHandle handle) const; |
| |
| #ifdef V8_ENABLE_SANDBOX |
| IndirectPointerTag tag() const { return tag_; } |
| #else |
| IndirectPointerTag tag() const { return kIndirectPointerNullTag; } |
| #endif |
| |
| // Whether this slot is empty, i.e. contains a null handle. |
| inline bool IsEmpty() const; |
| |
| // Retrieve the object referenced by the given handle by determining the |
| // appropriate pointer table to use and loading the referenced entry in it. |
| // This method is used internally by load() and related functions but can |
| // also be used to manually implement indirect pointer accessors. |
| inline Tagged<Object> ResolveHandle(IndirectPointerHandle handle, |
| IsolateForSandbox isolate) const; |
| |
| private: |
| #ifdef V8_ENABLE_SANDBOX |
| // Retrieve the object referenced through the given trusted pointer handle |
| // from the trusted pointer table. |
| inline Tagged<Object> ResolveTrustedPointerHandle( |
| IndirectPointerHandle handle, IsolateForSandbox isolate) const; |
| // Retrieve the Code object referenced through the given code pointer handle |
| // from the code pointer table. |
| inline Tagged<Object> ResolveCodePointerHandle( |
| IndirectPointerHandle handle) const; |
| |
| // The tag associated with this slot. |
| IndirectPointerTag tag_; |
| #endif // V8_ENABLE_SANDBOX |
| }; |
| |
| class WritableJitAllocation; |
| |
| template <typename SlotT> |
| class WriteProtectedSlot : public SlotT { |
| public: |
| using TObject = typename SlotT::TObject; |
| using SlotT::kCanBeWeak; |
| |
| explicit WriteProtectedSlot(WritableJitAllocation& jit_allocation, |
| Address ptr) |
| : SlotT(ptr), jit_allocation_(jit_allocation) {} |
| |
| inline TObject Relaxed_Load() const { return SlotT::Relaxed_Load(); } |
| inline TObject Relaxed_Load(PtrComprCageBase cage_base) const { |
| return SlotT::Relaxed_Load(cage_base); |
| } |
| |
| inline void Relaxed_Store(TObject value) const; |
| |
| private: |
| WritableJitAllocation& jit_allocation_; |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_OBJECTS_SLOTS_H_ |