// 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_V8_CPPGC_H_ #define INCLUDE_V8_CPPGC_H_ #include "cppgc/visitor.h" #include "v8-internal.h" // NOLINT(build/include_directory) #include "v8.h" // NOLINT(build/include_directory) namespace v8 { class Isolate; template class JSMember; namespace internal { class JSMemberBaseExtractor; class V8_EXPORT JSMemberBase { public: /** * Returns true if the reference is empty, i.e., has not been assigned * object. */ bool IsEmpty() const { return val_ == nullptr; } /** * Clears the reference. IsEmpty() will return true after this call. */ inline void Reset(); private: static internal::Address* New(v8::Isolate* isolate, internal::Address* object_slot, internal::Address** this_slot); static void Delete(internal::Address* object); static void Copy(const internal::Address* const* from_slot, internal::Address** to_slot); static void Move(internal::Address** from_slot, internal::Address** to_slot); JSMemberBase() = default; JSMemberBase(v8::Isolate* isolate, internal::Address* object_slot) : val_(New(isolate, object_slot, &val_)) {} inline JSMemberBase& CopyImpl(const JSMemberBase& other); inline JSMemberBase& MoveImpl(JSMemberBase&& other); // val_ points to a GlobalHandles node. internal::Address* val_ = nullptr; template friend class v8::JSMember; friend class v8::internal::JSMemberBaseExtractor; }; JSMemberBase& JSMemberBase::CopyImpl(const JSMemberBase& other) { if (this != &other) { Reset(); if (!other.IsEmpty()) { Copy(&other.val_, &val_); } } return *this; } JSMemberBase& JSMemberBase::MoveImpl(JSMemberBase&& other) { if (this != &other) { // No call to Reset() as Move() will conditionally reset itself when needed, // and otherwise reuse the internal meta data. Move(&other.val_, &val_); } return *this; } void JSMemberBase::Reset() { if (IsEmpty()) return; Delete(val_); val_ = nullptr; } } // namespace internal /** * A traced handle without destructor that clears the handle. The handle may * only be used in GarbageCollected objects and must be processed in a Trace() * method. */ template class V8_EXPORT JSMember : public internal::JSMemberBase { static_assert(std::is_base_of::value, "JSMember only supports references to v8::Value"); public: JSMember() = default; template ::value>> JSMember(Isolate* isolate, Local that) : internal::JSMemberBase(isolate, reinterpret_cast(*that)) {} JSMember(const JSMember& other) { CopyImpl(other); } template ::value>> JSMember(const JSMember& other) { // NOLINT CopyImpl(other); } JSMember(JSMember&& other) { MoveImpl(std::move(other)); } template ::value>> JSMember(JSMember&& other) { // NOLINT MoveImpl(std::move(other)); } JSMember& operator=(const JSMember& other) { return CopyImpl(other); } template ::value>> JSMember& operator=(const JSMember& other) { return CopyImpl(other); } JSMember& operator=(JSMember&& other) { return MoveImpl(other); } template ::value>> JSMember& operator=(JSMember&& other) { return MoveImpl(other); } T* operator->() const { return reinterpret_cast(val_); } T* operator*() const { return reinterpret_cast(val_); } using internal::JSMemberBase::Reset; template ::value>> void Set(v8::Isolate* isolate, Local that) { Reset(); val_ = New(isolate, reinterpret_cast(*that), &val_); } }; template ::value || std::is_base_of::value>> inline bool operator==(const JSMember& lhs, const JSMember& rhs) { v8::internal::Address* a = reinterpret_cast(*lhs); v8::internal::Address* b = reinterpret_cast(*rhs); if (a == nullptr) return b == nullptr; if (b == nullptr) return false; return *a == *b; } template ::value || std::is_base_of::value>> inline bool operator!=(const JSMember& lhs, const JSMember& rhs) { return !(lhs == rhs); } template ::value || std::is_base_of::value>> inline bool operator==(const JSMember& lhs, const Local& rhs) { v8::internal::Address* a = reinterpret_cast(*lhs); v8::internal::Address* b = reinterpret_cast(*rhs); if (a == nullptr) return b == nullptr; if (b == nullptr) return false; return *a == *b; } template ::value || std::is_base_of::value>> inline bool operator==(const Local& lhs, const JSMember rhs) { return rhs == lhs; } template inline bool operator!=(const JSMember& lhs, const T2& rhs) { return !(lhs == rhs); } template inline bool operator!=(const T1& lhs, const JSMember& rhs) { return !(lhs == rhs); } class JSVisitor : public cppgc::Visitor { public: explicit JSVisitor(cppgc::Visitor::Key key) : cppgc::Visitor(key) {} template void Trace(const JSMember& ref) { if (ref.IsEmpty()) return; Visit(ref); } protected: using cppgc::Visitor::Visit; virtual void Visit(const internal::JSMemberBase& ref) {} }; } // namespace v8 namespace cppgc { template struct TraceTrait> { static void Trace(Visitor* visitor, const v8::JSMember* self) { static_cast(visitor)->Trace(*self); } }; } // namespace cppgc #endif // INCLUDE_V8_CPPGC_H_