// 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_PERSISTENT_H_ #define INCLUDE_CPPGC_PERSISTENT_H_ #include #include "cppgc/internal/persistent-node.h" #include "cppgc/internal/pointer-policies.h" #include "cppgc/sentinel-pointer.h" #include "cppgc/source-location.h" #include "cppgc/type-traits.h" #include "cppgc/visitor.h" #include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { class Visitor; namespace internal { // PersistentBase always refers to the object as const object and defers to // BasicPersistent on casting to the right type as needed. class PersistentBase { protected: PersistentBase() = default; explicit PersistentBase(const void* raw) : raw_(raw) {} const void* GetValue() const { return raw_; } void SetValue(const void* value) { raw_ = value; } PersistentNode* GetNode() const { return node_; } void SetNode(PersistentNode* node) { node_ = node; } // Performs a shallow clear which assumes that internal persistent nodes are // destroyed elsewhere. void ClearFromGC() const { raw_ = nullptr; node_ = nullptr; } private: mutable const void* raw_ = nullptr; mutable PersistentNode* node_ = nullptr; friend class PersistentRegion; }; // The basic class from which all Persistent classes are generated. template class BasicPersistent final : public PersistentBase, public LocationPolicy, private WeaknessPolicy, private CheckingPolicy { public: using typename WeaknessPolicy::IsStrongPersistent; using PointeeType = T; // Null-state/sentinel constructors. BasicPersistent( // NOLINT const SourceLocation& loc = SourceLocation::Current()) : LocationPolicy(loc) {} BasicPersistent(std::nullptr_t, // NOLINT const SourceLocation& loc = SourceLocation::Current()) : LocationPolicy(loc) {} BasicPersistent( // NOLINT SentinelPointer s, const SourceLocation& loc = SourceLocation::Current()) : PersistentBase(s), LocationPolicy(loc) {} // Raw value constructors. BasicPersistent(T* raw, // NOLINT const SourceLocation& loc = SourceLocation::Current()) : PersistentBase(raw), LocationPolicy(loc) { if (!IsValid()) return; SetNode(WeaknessPolicy::GetPersistentRegion(GetValue()) .AllocateNode(this, &BasicPersistent::Trace)); this->CheckPointer(Get()); } BasicPersistent(T& raw, // NOLINT const SourceLocation& loc = SourceLocation::Current()) : BasicPersistent(&raw, loc) {} // Copy ctor. BasicPersistent(const BasicPersistent& other, const SourceLocation& loc = SourceLocation::Current()) : BasicPersistent(other.Get(), loc) {} // Heterogeneous ctor. template ::value>> BasicPersistent( // NOLINT const BasicPersistent& other, const SourceLocation& loc = SourceLocation::Current()) : BasicPersistent(other.Get(), loc) {} // Move ctor. The heterogeneous move ctor is not supported since e.g. // persistent can't reuse persistent node from weak persistent. BasicPersistent( BasicPersistent&& other, const SourceLocation& loc = SourceLocation::Current()) noexcept : PersistentBase(std::move(other)), LocationPolicy(std::move(other)) { if (!IsValid()) return; GetNode()->UpdateOwner(this); other.SetValue(nullptr); other.SetNode(nullptr); this->CheckPointer(Get()); } // Constructor from member. template ::value>> BasicPersistent(internal::BasicMember member, const SourceLocation& loc = SourceLocation::Current()) : BasicPersistent(member.Get(), loc) {} ~BasicPersistent() { Clear(); } // Copy assignment. BasicPersistent& operator=(const BasicPersistent& other) { return operator=(other.Get()); } template ::value>> BasicPersistent& operator=( const BasicPersistent& other) { return operator=(other.Get()); } // Move assignment. BasicPersistent& operator=(BasicPersistent&& other) { if (this == &other) return *this; Clear(); PersistentBase::operator=(std::move(other)); LocationPolicy::operator=(std::move(other)); if (!IsValid()) return *this; GetNode()->UpdateOwner(this); other.SetValue(nullptr); other.SetNode(nullptr); this->CheckPointer(Get()); return *this; } // Assignment from member. template ::value>> BasicPersistent& operator=( internal::BasicMember member) { return operator=(member.Get()); } BasicPersistent& operator=(T* other) { Assign(other); return *this; } BasicPersistent& operator=(std::nullptr_t) { Clear(); return *this; } BasicPersistent& operator=(SentinelPointer s) { Assign(s); return *this; } 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 { // The const_cast below removes the constness from PersistentBase storage. // The following static_cast re-adds any constness if specified through the // user-visible template parameter T. return static_cast(const_cast(GetValue())); } void Clear() { // Simplified version of `Assign()` to allow calling without a complete type // `T`. if (IsValid()) { WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); SetNode(nullptr); } SetValue(nullptr); } T* Release() { T* result = Get(); Clear(); return result; } template BasicPersistent To() const { return BasicPersistent(static_cast(Get())); } private: static void Trace(Visitor* v, const void* ptr) { const auto* persistent = static_cast(ptr); v->TraceRoot(*persistent, persistent->Location()); } bool IsValid() const { // Ideally, handling kSentinelPointer would be done by the embedder. On the // other hand, having Persistent aware of it is beneficial since no node // gets wasted. return GetValue() != nullptr && GetValue() != kSentinelPointer; } void Assign(T* ptr) { if (IsValid()) { if (ptr && ptr != kSentinelPointer) { // Simply assign the pointer reusing the existing node. SetValue(ptr); this->CheckPointer(ptr); return; } WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); SetNode(nullptr); } SetValue(ptr); if (!IsValid()) return; SetNode(WeaknessPolicy::GetPersistentRegion(GetValue()) .AllocateNode(this, &BasicPersistent::Trace)); this->CheckPointer(Get()); } void ClearFromGC() const { if (IsValid()) { WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); PersistentBase::ClearFromGC(); } } friend class cppgc::Visitor; }; template bool operator==(const BasicPersistent& p1, const BasicPersistent& p2) { return p1.Get() == p2.Get(); } template bool operator!=(const BasicPersistent& p1, const BasicPersistent& p2) { return !(p1 == p2); } template bool operator==(const BasicPersistent& p, BasicMember m) { return p.Get() == m.Get(); } template bool operator!=(const BasicPersistent& p, BasicMember m) { return !(p == m); } template bool operator==(BasicMember m, const BasicPersistent& p) { return m.Get() == p.Get(); } template bool operator!=(BasicMember m, const BasicPersistent& p) { return !(m == p); } template struct IsWeak> : std::true_type {}; } // namespace internal /** * Persistent is a way to create a strong pointer from an off-heap object to * another on-heap object. As long as the Persistent handle is alive the GC will * keep the object pointed to alive. The Persistent handle is always a GC root * from the point of view of the GC. Persistent must be constructed and * destructed in the same thread. */ template using Persistent = internal::BasicPersistent; /** * WeakPersistent is a way to create a weak pointer from an off-heap object to * an on-heap object. The pointer is automatically cleared when the pointee gets * collected. WeakPersistent must be constructed and destructed in the same * thread. */ template using WeakPersistent = internal::BasicPersistent; } // namespace cppgc #endif // INCLUDE_CPPGC_PERSISTENT_H_