// Copyright 2020 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 INCLUDE_CPPGC_MEMBER_H_ #define INCLUDE_CPPGC_MEMBER_H_ #include #include #include #include "cppgc/internal/pointer-policies.h" #include "cppgc/sentinel-pointer.h" #include "cppgc/type-traits.h" #include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { class Visitor; namespace internal { // MemberBase always refers to the object as const object and defers to // BasicMember on casting to the right type as needed. class MemberBase { protected: MemberBase() = default; explicit MemberBase(const void* value) : raw_(value) {} const void** GetRawSlot() const { return &raw_; } const void* GetRaw() const { return raw_; } void SetRaw(void* value) { raw_ = value; } const void* GetRawAtomic() const { return reinterpret_cast*>(&raw_)->load( std::memory_order_relaxed); } void SetRawAtomic(const void* value) { reinterpret_cast*>(&raw_)->store( value, std::memory_order_relaxed); } void ClearFromGC() const { raw_ = nullptr; } private: mutable const void* raw_ = nullptr; }; // The basic class from which all Member classes are 'generated'. template class BasicMember final : private MemberBase, private CheckingPolicy { public: using PointeeType = T; constexpr BasicMember() = default; constexpr BasicMember(std::nullptr_t) {} // NOLINT BasicMember(SentinelPointer s) : MemberBase(s) {} // NOLINT BasicMember(T* raw) : MemberBase(raw) { // NOLINT InitializingWriteBarrier(); this->CheckPointer(Get()); } BasicMember(T& raw) : BasicMember(&raw) {} // NOLINT // Copy ctor. BasicMember(const BasicMember& other) : BasicMember(other.Get()) {} // Allow heterogeneous construction. template ::value>> BasicMember( // NOLINT const BasicMember& other) : BasicMember(other.Get()) {} // Move ctor. BasicMember(BasicMember&& other) noexcept : BasicMember(other.Get()) { other.Clear(); } // Allow heterogeneous move construction. template ::value>> BasicMember( // NOLINT BasicMember&& other) noexcept : BasicMember(other.Get()) { other.Clear(); } // Construction from Persistent. template ::value>> BasicMember( // NOLINT const BasicPersistent& p) : BasicMember(p.Get()) {} // Copy assignment. BasicMember& operator=(const BasicMember& other) { return operator=(other.Get()); } // Allow heterogeneous copy assignment. template ::value>> BasicMember& operator=( const BasicMember& other) { return operator=(other.Get()); } // Move assignment. BasicMember& operator=(BasicMember&& other) noexcept { operator=(other.Get()); other.Clear(); return *this; } // Heterogeneous move assignment. template ::value>> BasicMember& operator=(BasicMember&& other) noexcept { operator=(other.Get()); other.Clear(); return *this; } // Assignment from Persistent. template ::value>> BasicMember& operator=( const BasicPersistent& other) { return operator=(other.Get()); } BasicMember& operator=(T* other) { SetRawAtomic(other); AssigningWriteBarrier(); this->CheckPointer(Get()); return *this; } BasicMember& operator=(std::nullptr_t) { Clear(); return *this; } BasicMember& operator=(SentinelPointer s) { SetRawAtomic(s); return *this; } template void Swap(BasicMember& other) { T* tmp = Get(); *this = other; other = tmp; } explicit operator bool() const { return Get(); } operator T*() const { return Get(); } // NOLINT T* operator->() const { return Get(); } T& operator*() const { return *Get(); } // CFI cast exemption to allow passing SentinelPointer through T* and support // heterogeneous assignments between different Member and Persistent handles // based on their actual types. V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { // Executed by the mutator, hence non atomic load. // // The const_cast below removes the constness from MemberBase storage. The // following static_cast re-adds any constness if specified through the // user-visible template parameter T. return static_cast(const_cast(MemberBase::GetRaw())); } void Clear() { SetRawAtomic(nullptr); } T* Release() { T* result = Get(); Clear(); return result; } const T** GetSlotForTesting() const { return reinterpret_cast(GetRawSlot()); } private: const T* GetRawAtomic() const { return static_cast(MemberBase::GetRawAtomic()); } void InitializingWriteBarrier() const { WriteBarrierPolicy::InitializingBarrier(GetRawSlot(), GetRaw()); } void AssigningWriteBarrier() const { WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), GetRaw()); } void ClearFromGC() const { MemberBase::ClearFromGC(); } friend class cppgc::Visitor; template friend struct cppgc::TraceTrait; }; template bool operator==( BasicMember member1, BasicMember member2) { return member1.Get() == member2.Get(); } template bool operator!=( BasicMember member1, BasicMember member2) { return !(member1 == member2); } template struct IsWeak< internal::BasicMember> : std::true_type {}; } // namespace internal /** * Members are used in classes to contain strong pointers to other garbage * collected objects. All Member fields of a class must be traced in the class' * trace method. */ template using Member = internal::BasicMember; /** * WeakMember is similar to Member in that it is used to point to other garbage * collected objects. However instead of creating a strong pointer to the * object, the WeakMember creates a weak pointer, which does not keep the * pointee alive. Hence if all pointers to to a heap allocated object are weak * the object will be garbage collected. At the time of GC the weak pointers * will automatically be set to null. */ template using WeakMember = internal::BasicMember; /** * UntracedMember is a pointer to an on-heap object that is not traced for some * reason. Do not use this unless you know what you are doing. Keeping raw * pointers to on-heap objects is prohibited unless used from stack. Pointee * must be kept alive through other means. */ template using UntracedMember = internal::BasicMember; } // namespace cppgc #endif // INCLUDE_CPPGC_MEMBER_H_