| // Copyright 2015 The Chromium 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 GarbageCollected_h |
| #define GarbageCollected_h |
| |
| #include "platform/heap/ThreadState.h" |
| #include "wtf/Allocator.h" |
| #include "wtf/Assertions.h" |
| #include "wtf/ListHashSet.h" |
| #include "wtf/TypeTraits.h" |
| |
| namespace blink { |
| |
| template<typename T> class GarbageCollected; |
| template<typename T, typename U, typename V, typename W, typename X> class HeapHashMap; |
| template<typename T, typename U, typename V> class HeapHashSet; |
| template<typename T, typename U, typename V> class HeapLinkedHashSet; |
| template<typename T, size_t inlineCapacity, typename U> class HeapListHashSet; |
| template<typename T, size_t inlineCapacity> class HeapVector; |
| template<typename T, size_t inlineCapacity> class HeapDeque; |
| template<typename T, typename U, typename V> class HeapHashCountedSet; |
| template<typename T> class HeapTerminatedArray; |
| template<typename T, typename Traits> class HeapVectorBacking; |
| template<typename Table> class HeapHashTableBacking; |
| template<typename ValueArg, size_t inlineCapacity> class HeapListHashSetAllocator; |
| class InlinedGlobalMarkingVisitor; |
| template<typename T> class Persistent; |
| |
| // GC_PLUGIN_IGNORE is used to make the plugin ignore a particular class or |
| // field when checking for proper usage. When using GC_PLUGIN_IGNORE |
| // a bug-number should be provided as an argument where the bug describes |
| // what needs to happen to remove the GC_PLUGIN_IGNORE again. |
| #if COMPILER(CLANG) |
| #define GC_PLUGIN_IGNORE(bug) \ |
| __attribute__((annotate("blink_gc_plugin_ignore"))) |
| #else |
| #define GC_PLUGIN_IGNORE(bug) |
| #endif |
| |
| // Template to determine if a class is a GarbageCollectedMixin by checking if it |
| // has IsGarbageCollectedMixinMarker |
| template<typename T> |
| struct IsGarbageCollectedMixin { |
| private: |
| typedef char YesType; |
| struct NoType { |
| char padding[8]; |
| }; |
| |
| template <typename U> static YesType checkMarker(typename U::IsGarbageCollectedMixinMarker*); |
| template <typename U> static NoType checkMarker(...); |
| |
| public: |
| static const bool value = sizeof(checkMarker<T>(nullptr)) == sizeof(YesType); |
| }; |
| |
| template <typename T> |
| struct IsGarbageCollectedType { |
| using TrueType = char; |
| struct FalseType { |
| char dummy[2]; |
| }; |
| |
| using NonConstType = typename WTF::RemoveConst<T>::Type; |
| using GarbageCollectedSubclass = WTF::IsSubclassOfTemplate<NonConstType, GarbageCollected>; |
| using GarbageCollectedMixinSubclass = IsGarbageCollectedMixin<NonConstType>; |
| using HeapHashSetSubclass = WTF::IsSubclassOfTemplate<NonConstType, HeapHashSet>; |
| using HeapLinkedHashSetSubclass = WTF::IsSubclassOfTemplate<NonConstType, HeapLinkedHashSet>; |
| using HeapListHashSetSubclass = WTF::IsSubclassOfTemplateTypenameSizeTypename<NonConstType, HeapListHashSet>; |
| using HeapHashMapSubclass = WTF::IsSubclassOfTemplate<NonConstType, HeapHashMap>; |
| using HeapVectorSubclass = WTF::IsSubclassOfTemplateTypenameSize<NonConstType, HeapVector>; |
| using HeapDequeSubclass = WTF::IsSubclassOfTemplateTypenameSize<NonConstType, HeapDeque>; |
| using HeapHashCountedSetSubclass = WTF::IsSubclassOfTemplate<NonConstType, HeapHashCountedSet>; |
| using HeapTerminatedArraySubclass = WTF::IsSubclassOfTemplate<NonConstType, HeapTerminatedArray>; |
| using HeapVectorBackingSubclass = WTF::IsSubclassOfTemplate<NonConstType, HeapVectorBacking>; |
| using HeapHashTableBackingSubclass = WTF::IsSubclassOfTemplate<NonConstType, HeapHashTableBacking>; |
| |
| template<typename U, size_t inlineCapacity> static TrueType listHashSetNodeIsHeapAllocated(WTF::ListHashSetNode<U, HeapListHashSetAllocator<U, inlineCapacity>>*); |
| static FalseType listHashSetNodeIsHeapAllocated(...); |
| static const bool isHeapAllocatedListHashSetNode = sizeof(TrueType) == sizeof(listHashSetNodeIsHeapAllocated(reinterpret_cast<NonConstType*>(0))); |
| |
| static_assert(sizeof(T), "T must be fully defined"); |
| |
| static const bool value = |
| GarbageCollectedSubclass::value |
| || GarbageCollectedMixinSubclass::value |
| || HeapHashSetSubclass::value |
| || HeapLinkedHashSetSubclass::value |
| || HeapListHashSetSubclass::value |
| || HeapHashMapSubclass::value |
| || HeapVectorSubclass::value |
| || HeapDequeSubclass::value |
| || HeapHashCountedSetSubclass::value |
| || HeapTerminatedArraySubclass::value |
| || HeapVectorBackingSubclass::value |
| || HeapHashTableBackingSubclass::value |
| || isHeapAllocatedListHashSetNode; |
| }; |
| |
| // The GarbageCollectedMixin interface and helper macro |
| // USING_GARBAGE_COLLECTED_MIXIN can be used to automatically define |
| // TraceTrait/ObjectAliveTrait on non-leftmost deriving classes |
| // which need to be garbage collected. |
| // |
| // Consider the following case: |
| // class B {}; |
| // class A : public GarbageCollected, public B {}; |
| // |
| // We can't correctly handle "Member<B> p = &a" as we can't compute addr of |
| // object header statically. This can be solved by using GarbageCollectedMixin: |
| // class B : public GarbageCollectedMixin {}; |
| // class A : public GarbageCollected, public B { |
| // USING_GARBAGE_COLLECTED_MIXIN(A) |
| // }; |
| // |
| // With the helper, as long as we are using Member<B>, TypeTrait<B> will |
| // dispatch adjustAndMark dynamically to find collect addr of the object header. |
| // Note that this is only enabled for Member<B>. For Member<A> which we can |
| // compute the object header addr statically, this dynamic dispatch is not used. |
| class PLATFORM_EXPORT GarbageCollectedMixin { |
| public: |
| typedef int IsGarbageCollectedMixinMarker; |
| virtual void adjustAndMark(Visitor*) const = 0; |
| virtual void trace(Visitor*) { } |
| virtual void adjustAndMark(InlinedGlobalMarkingVisitor) const = 0; |
| virtual void trace(InlinedGlobalMarkingVisitor); |
| virtual bool isHeapObjectAlive() const = 0; |
| }; |
| |
| #define DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(VISITOR, TYPE) \ |
| public: \ |
| void adjustAndMark(VISITOR visitor) const override \ |
| { \ |
| typedef WTF::IsSubclassOfTemplate<typename WTF::RemoveConst<TYPE>::Type, blink::GarbageCollected> IsSubclassOfGarbageCollected; \ |
| static_assert(IsSubclassOfGarbageCollected::value, "only garbage collected objects can have garbage collected mixins"); \ |
| if (TraceEagerlyTrait<TYPE>::value) { \ |
| if (visitor->ensureMarked(static_cast<const TYPE*>(this))) \ |
| TraceTrait<TYPE>::trace(visitor, const_cast<TYPE*>(this)); \ |
| return; \ |
| } \ |
| visitor->mark(static_cast<const TYPE*>(this), &blink::TraceTrait<TYPE>::trace); \ |
| } \ |
| private: |
| |
| // A C++ object's vptr will be initialized to its leftmost base's vtable after |
| // the constructors of all its subclasses have run, so if a subclass constructor |
| // tries to access any of the vtbl entries of its leftmost base prematurely, |
| // it'll find an as-yet incorrect vptr and fail. Which is exactly what a |
| // garbage collector will try to do if it tries to access the leftmost base |
| // while one of the subclass constructors of a GC mixin object triggers a GC. |
| // It is consequently not safe to allow any GCs while these objects are under |
| // (sub constructor) construction. |
| // |
| // To prevent GCs in that restricted window of a mixin object's construction: |
| // |
| // - The initial allocation of the mixin object will enter a no GC scope. |
| // This is done by overriding 'operator new' for mixin instances. |
| // - When the constructor for the mixin is invoked, after all the |
| // derived constructors have run, it will invoke the constructor |
| // for a field whose only purpose is to leave the GC scope. |
| // GarbageCollectedMixinConstructorMarker's constructor takes care of |
| // this and the field is declared by way of USING_GARBAGE_COLLECTED_MIXIN(). |
| |
| #define DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE) \ |
| public: \ |
| GC_PLUGIN_IGNORE("crbug.com/456823") NO_SANITIZE_UNRELATED_CAST \ |
| void* operator new(size_t size) \ |
| { \ |
| void* object = TYPE::allocateObject(size, IsEagerlyFinalizedType<TYPE>::value); \ |
| ThreadState* state = ThreadStateFor<ThreadingTrait<TYPE>::Affinity>::state(); \ |
| state->enterGCForbiddenScopeIfNeeded(&(reinterpret_cast<TYPE*>(object)->m_mixinConstructorMarker)); \ |
| return object; \ |
| } \ |
| GarbageCollectedMixinConstructorMarker m_mixinConstructorMarker; \ |
| private: |
| |
| // Mixins that wrap/nest others requires extra handling: |
| // |
| // class A : public GarbageCollected<A>, public GarbageCollectedMixin { |
| // USING_GARBAGE_COLLECTED_MIXIN(A); |
| // ... |
| // }' |
| // public B final : public A, public SomeOtherMixinInterface { |
| // USING_GARBAGE_COLLECTED_MIXIN(B); |
| // ... |
| // }; |
| // |
| // The "operator new" for B will enter the forbidden GC scope, but |
| // upon construction, two GarbageCollectedMixinConstructorMarker constructors |
| // will run -- one for A (first) and another for B (secondly). Only |
| // the second one should leave the forbidden GC scope. This is realized by |
| // recording the address of B's GarbageCollectedMixinConstructorMarker |
| // when the "operator new" for B runs, and leaving the forbidden GC scope |
| // when the constructor of the recorded GarbageCollectedMixinConstructorMarker |
| // runs. |
| #define USING_GARBAGE_COLLECTED_MIXIN(TYPE) \ |
| DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::Visitor*, TYPE) \ |
| DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::InlinedGlobalMarkingVisitor, TYPE) \ |
| DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE) \ |
| public: \ |
| bool isHeapObjectAlive() const override \ |
| { \ |
| return Heap::isHeapObjectAlive(this); \ |
| } \ |
| private: |
| |
| #if ENABLE(OILPAN) |
| #define WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(TYPE) USING_GARBAGE_COLLECTED_MIXIN(TYPE) |
| #else |
| #define WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(TYPE) |
| #endif |
| |
| // An empty class with a constructor that's arranged invoked when all derived constructors |
| // of a mixin instance have completed and it is safe to allow GCs again. See |
| // AllocateObjectTrait<> comment for more. |
| // |
| // USING_GARBAGE_COLLECTED_MIXIN() declares a GarbageCollectedMixinConstructorMarker<> private |
| // field. By following Blink convention of using the macro at the top of a class declaration, |
| // its constructor will run first. |
| class GarbageCollectedMixinConstructorMarker { |
| public: |
| GarbageCollectedMixinConstructorMarker() |
| { |
| // FIXME: if prompt conservative GCs are needed, forced GCs that |
| // were denied while within this scope, could now be performed. |
| // For now, assume the next out-of-line allocation request will |
| // happen soon enough and take care of it. Mixin objects aren't |
| // overly common. |
| ThreadState* state = ThreadState::current(); |
| state->leaveGCForbiddenScopeIfNeeded(this); |
| } |
| }; |
| |
| // Base class for objects allocated in the Blink garbage-collected heap. |
| // |
| // Defines a 'new' operator that allocates the memory in the heap. 'delete' |
| // should not be called on objects that inherit from GarbageCollected. |
| // |
| // Instances of GarbageCollected will *NOT* get finalized. Their destructor |
| // will not be called. Therefore, only classes that have trivial destructors |
| // with no semantic meaning (including all their subclasses) should inherit from |
| // GarbageCollected. If there are non-trival destructors in a given class or |
| // any of its subclasses, GarbageCollectedFinalized should be used which |
| // guarantees that the destructor is called on an instance when the garbage |
| // collector determines that it is no longer reachable. |
| template<typename T> class GarbageCollected; |
| |
| // Base class for objects allocated in the Blink garbage-collected heap. |
| // |
| // Defines a 'new' operator that allocates the memory in the heap. 'delete' |
| // should not be called on objects that inherit from GarbageCollected. |
| // |
| // Instances of GarbageCollectedFinalized will have their destructor called when |
| // the garbage collector determines that the object is no longer reachable. |
| template<typename T> |
| class GarbageCollectedFinalized : public GarbageCollected<T> { |
| WTF_MAKE_NONCOPYABLE(GarbageCollectedFinalized); |
| |
| protected: |
| // finalizeGarbageCollectedObject is called when the object is freed from |
| // the heap. By default finalization means calling the destructor on the |
| // object. finalizeGarbageCollectedObject can be overridden to support |
| // calling the destructor of a subclass. This is useful for objects without |
| // vtables that require explicit dispatching. The name is intentionally a |
| // bit long to make name conflicts less likely. |
| void finalizeGarbageCollectedObject() |
| { |
| static_cast<T*>(this)->~T(); |
| } |
| |
| GarbageCollectedFinalized() { } |
| ~GarbageCollectedFinalized() { } |
| |
| template<typename U> friend struct HasFinalizer; |
| template<typename U, bool> friend struct FinalizerTraitImpl; |
| }; |
| |
| // Base class for objects that are in the Blink garbage-collected heap |
| // and are still reference counted. |
| // |
| // This class should be used sparingly and only to gradually move |
| // objects from being reference counted to being managed by the blink |
| // garbage collector. |
| // |
| // While the current reference counting keeps one of these objects |
| // alive it will have a Persistent handle to itself allocated so we |
| // will not reclaim the memory. When the reference count reaches 0 the |
| // persistent handle will be deleted. When the garbage collector |
| // determines that there are no other references to the object it will |
| // be reclaimed and the destructor of the reclaimed object will be |
| // called at that time. |
| template<typename T> |
| class RefCountedGarbageCollected : public GarbageCollectedFinalized<T> { |
| WTF_MAKE_NONCOPYABLE(RefCountedGarbageCollected); |
| |
| public: |
| RefCountedGarbageCollected() |
| : m_refCount(0) |
| { |
| } |
| |
| // Implement method to increase reference count for use with RefPtrs. |
| // |
| // In contrast to the normal WTF::RefCounted, the reference count can reach |
| // 0 and increase again. This happens in the following scenario: |
| // |
| // (1) The reference count becomes 0, but members, persistents, or |
| // on-stack pointers keep references to the object. |
| // |
| // (2) The pointer is assigned to a RefPtr again and the reference |
| // count becomes 1. |
| // |
| // In this case, we have to resurrect m_keepAlive. |
| void ref() |
| { |
| if (UNLIKELY(!m_refCount)) { |
| ASSERT(ThreadState::current()->findPageFromAddress(reinterpret_cast<Address>(this))); |
| makeKeepAlive(); |
| } |
| ++m_refCount; |
| } |
| |
| // Implement method to decrease reference count for use with RefPtrs. |
| // |
| // In contrast to the normal WTF::RefCounted implementation, the |
| // object itself is not deleted when the reference count reaches |
| // 0. Instead, the keep-alive persistent handle is deallocated so |
| // that the object can be reclaimed when the garbage collector |
| // determines that there are no other references to the object. |
| void deref() |
| { |
| ASSERT(m_refCount > 0); |
| if (!--m_refCount) { |
| delete m_keepAlive; |
| m_keepAlive = 0; |
| } |
| } |
| |
| bool hasOneRef() |
| { |
| return m_refCount == 1; |
| } |
| |
| protected: |
| ~RefCountedGarbageCollected() { } |
| |
| private: |
| void makeKeepAlive() |
| { |
| ASSERT(!m_keepAlive); |
| m_keepAlive = new Persistent<T>(static_cast<T*>(this)); |
| } |
| |
| int m_refCount; |
| Persistent<T>* m_keepAlive; |
| }; |
| |
| template<typename T, bool = WTF::IsSubclassOfTemplate<typename WTF::RemoveConst<T>::Type, GarbageCollected>::value> class NeedsAdjustAndMark; |
| |
| template<typename T> |
| class NeedsAdjustAndMark<T, true> { |
| static_assert(sizeof(T), "T must be fully defined"); |
| public: |
| static const bool value = false; |
| }; |
| template <typename T> const bool NeedsAdjustAndMark<T, true>::value; |
| |
| template<typename T> |
| class NeedsAdjustAndMark<T, false> { |
| static_assert(sizeof(T), "T must be fully defined"); |
| public: |
| static const bool value = IsGarbageCollectedMixin<typename WTF::RemoveConst<T>::Type>::value; |
| }; |
| template <typename T> const bool NeedsAdjustAndMark<T, false>::value; |
| |
| // TODO(sof): migrate to wtf/TypeTraits.h |
| template<typename T> |
| class IsFullyDefined { |
| using TrueType = char; |
| struct FalseType { |
| char dummy[2]; |
| }; |
| |
| template<typename U, size_t sz = sizeof(U)> static TrueType isSizeofKnown(U*); |
| static FalseType isSizeofKnown(...); |
| static T& t; |
| public: |
| static const bool value = sizeof(TrueType) == sizeof(isSizeofKnown(&t)); |
| }; |
| |
| } // namespace blink |
| |
| #endif |