Index: Source/platform/heap/Visitor.h |
diff --git a/Source/platform/heap/Visitor.h b/Source/platform/heap/Visitor.h |
index f0b9ed70b31598dee872e40a39293c0055631ad8..274312bb1707c15c156e47a122c01bda19c67504 100644 |
--- a/Source/platform/heap/Visitor.h |
+++ b/Source/platform/heap/Visitor.h |
@@ -155,8 +155,12 @@ public: |
template <typename T> const bool NeedsAdjustAndMark<T, false>::value; |
+template <typename T, bool = WTF::HasTraceNewMethod<T>::value> struct TraceGenericCompatibilityAdaptor; |
+ |
template<typename T, bool = NeedsAdjustAndMark<T>::value> class DefaultTraceTrait; |
+class InlinedGlobalMarkingVisitor; |
+ |
// The TraceTrait is used to specify how to mark an object pointer and |
// how to trace all of the pointers in the object. |
// |
@@ -173,15 +177,13 @@ class TraceTrait { |
public: |
// Default implementation of TraceTrait<T>::trace just statically |
// dispatches to the trace method of the class T. |
- static void trace(Visitor* visitor, void* self) |
+ template<typename VisitorDispatcher> |
+ static void trace(VisitorDispatcher visitor, void* self) |
{ |
- static_cast<T*>(self)->trace(visitor); |
+ TraceGenericCompatibilityAdaptor<T>::trace(visitor, static_cast<T*>(self)); |
} |
- static void mark(Visitor* visitor, const T* t) |
- { |
- DefaultTraceTrait<T>::mark(visitor, t); |
- } |
+ template<typename VisitorDispatcher> static void mark(VisitorDispatcher visitor, const T*); |
#if ENABLE(ASSERT) |
static void checkGCInfo(Visitor* visitor, const T* t) |
@@ -527,13 +529,24 @@ public: |
} |
#endif |
+ virtual bool isGlobalMarkingVisitor() { return false; } |
+ |
+#if 0 |
inline bool canTraceEagerly() const { return m_traceDepth < kMaxEagerTraceDepth; } |
inline void incrementTraceDepth() { m_traceDepth++; } |
inline void decrementTraceDepth() { ASSERT(m_traceDepth > 0); m_traceDepth--; } |
+#else |
+ inline bool canTraceEagerly() const { return true; } |
+ inline void incrementTraceDepth() { } |
+ inline void decrementTraceDepth() { } |
+#endif |
+ |
+ // This should be only used from InlinedGlobalMarkingVisitor |
+ virtual void pushTraceCallback(void*, TraceCallback) { ASSERT_NOT_REACHED(); } |
protected: |
Visitor() |
- : m_traceDepth(0) |
+ // : m_traceDepth(0) |
{ |
} |
@@ -554,11 +567,298 @@ private: |
// The maximum depth of eager, unrolled trace() calls that is |
// considered safe and allowed. |
- const int kMaxEagerTraceDepth = 100; |
+ // const int kMaxEagerTraceDepth = 100; |
- int m_traceDepth; |
+ // int m_traceDepth; |
}; |
+template<typename T> |
+inline T&& forward(typename WTF::Identity<T>::type&& x) { return x; } |
+ |
+class InlinedGlobalMarkingVisitor { |
+public: |
+ InlinedGlobalMarkingVisitor(Visitor* visitor) |
+ : m_visitor(visitor) |
+ { |
+ ASSERT(visitor->isGlobalMarkingVisitor()); |
+ } |
+ |
+ // Hack to allow visitor->trace() |
+ InlinedGlobalMarkingVisitor* operator->() { return this; } |
+ |
+ // One-argument templated mark method. This uses the static type of |
+ // the argument to get the TraceTrait. By default, the mark method |
+ // of the TraceTrait just calls the virtual two-argument mark method on this |
+ // visitor, where the second argument is the static trace method of the trait. |
+ template<typename T> void mark(T* t); |
+ |
+ // Member version of the one-argument templated trace method. |
+ template<typename T> |
+ void trace(const Member<T>& t) |
+ { |
+ mark(t.get()); |
+ } |
+ |
+ // Fallback method used only when we need to trace raw pointers of T. |
+ // This is the case when a member is a union where we do not support members. |
+ template<typename T> |
+ void trace(const T* t) |
+ { |
+ mark(const_cast<T*>(t)); |
+ } |
+ |
+ template<typename T> |
+ void trace(T* t) |
+ { |
+ mark(t); |
+ } |
+ |
+ // WeakMember version of the templated trace method. It doesn't keep |
+ // the traced thing alive, but will write null to the WeakMember later |
+ // if the pointed-to object is dead. It's lying for this to be const, |
+ // but the overloading resolver prioritizes constness too high when |
+ // picking the correct overload, so all these trace methods have to have |
+ // the same constness on their argument to allow the type to decide. |
+ template<typename T> |
+ void trace(const WeakMember<T>& t) |
+ { |
+ // Check that we actually know the definition of T when tracing. |
+ COMPILE_ASSERT(sizeof(T), WeNeedToKnowTheDefinitionOfTheTypeWeAreTracing); |
+ registerWeakCell(const_cast<WeakMember<T>&>(t).cell()); |
+ COMPILE_ASSERT_IS_GARBAGE_COLLECTED(T, AttemptedToWeakTraceNonGarbageCollectedObject); |
+ } |
+ |
+ template<typename T> |
+ void traceInCollection(T& t, WTF::ShouldWeakPointersBeMarkedStrongly strongify) |
+ { |
+ HashTraits<T>::traceInCollection(m_visitor, t, strongify); |
+ } |
+ |
+ // Fallback trace method for part objects to allow individual trace methods |
+ // to trace through a part object with visitor->trace(m_partObject). This |
+ // takes a const argument, because otherwise it will match too eagerly: a |
+ // non-const argument would match a non-const Vector<T>& argument better |
+ // than the specialization that takes const Vector<T>&. For a similar reason, |
+ // the other specializations take a const argument even though they are |
+ // usually used with non-const arguments, otherwise this function would match |
+ // too well. |
+ template<typename T> |
+ void trace(const T& t) |
+ { |
+ if (WTF::IsPolymorphic<T>::value) { |
+ intptr_t vtable = *reinterpret_cast<const intptr_t*>(&t); |
+ if (!vtable) |
+ return; |
+ } |
+ const_cast<T&>(t).trace(*this); |
+ } |
+ |
+ // The following trace methods are for off-heap collections. |
+ template<typename T, size_t inlineCapacity> |
+ void trace(const Vector<T, inlineCapacity>& vector) |
+ { |
+ OffHeapCollectionTraceTrait<Vector<T, inlineCapacity, WTF::DefaultAllocator> >::trace(m_visitor, vector); |
+ } |
+ |
+ template<typename T, size_t N> |
+ void trace(const Deque<T, N>& deque) |
+ { |
+ OffHeapCollectionTraceTrait<Deque<T, N> >::trace(m_visitor, deque); |
+ } |
+ |
+#if !ENABLE(OILPAN) |
+ // These trace methods are needed to allow compiling and calling trace on |
+ // transition types. We need to support calls in the non-oilpan build |
+ // because a fully transitioned type (which will have its trace method |
+ // called) might trace a field that is in transition. Once transition types |
+ // are removed these can be removed. |
+ template<typename T> void trace(const OwnPtr<T>&) { } |
+ template<typename T> void trace(const RefPtr<T>&) { } |
+ template<typename T> void trace(const RawPtr<T>&) { } |
+ template<typename T> void trace(const WeakPtr<T>&) { } |
+#endif |
+ |
+ // This method marks an object and adds it to the set of objects |
+ // that should have their trace method called. Since not all |
+ // objects have vtables we have to have the callback as an |
+ // explicit argument, but we can use the templated one-argument |
+ // mark method above to automatically provide the callback |
+ // function. |
+ void mark(const void* o, TraceCallback callback); |
+ |
+ template<typename T> void markNoTracing(const T* pointer) { mark(pointer, reinterpret_cast<TraceCallback>(0)); } |
+ void markNoTracing(const void* pointer) { mark(pointer, reinterpret_cast<TraceCallback>(0)); } |
+ void markNoTracing(HeapObjectHeader* header) { mark(header, reinterpret_cast<TraceCallback>(0)); } |
+ void markNoTracing(GeneralHeapObjectHeader* header) { mark(header, reinterpret_cast<TraceCallback>(0)); } |
+ |
+ // If the object calls this during the regular trace callback, then the |
+ // WeakPointerCallback argument may be called later, when the strong roots |
+ // have all been found. The WeakPointerCallback will normally use isAlive |
+ // to find out whether some pointers are pointing to dying objects. When |
+ // the WeakPointerCallback is done the object must have purged all pointers |
+ // to objects where isAlive returned false. In the weak callback it is not |
+ // allowed to touch other objects (except using isAlive) or to allocate on |
+ // the GC heap. Note that even removing things from HeapHashSet or |
+ // HeapHashMap can cause an allocation if the backing store resizes, but |
+ // these collections know to remove WeakMember elements safely. |
+ // |
+ // The weak pointer callbacks are run on the thread that owns the |
+ // object and other threads are not stopped during the |
+ // callbacks. Since isAlive is used in the callback to determine |
+ // if objects pointed to are alive it is crucial that the object |
+ // pointed to belong to the same thread as the object receiving |
+ // the weak callback. Since other threads have been resumed the |
+ // mark bits are not valid for objects from other threads. |
+ void registerWeakMembers(const void* object, WeakPointerCallback callback) { m_visitor->registerWeakMembers(object, object, callback); } |
+ void registerWeakMembers(const void* a , const void* b, WeakPointerCallback callback) { m_visitor->registerWeakMembers(a, b, callback); } |
+ |
+ template<typename T, void (T::*method)(Visitor*)> |
+ void registerWeakMembers(const T* obj) |
+ { |
+ registerWeakMembers(obj, &TraceMethodDelegate<T, method>::trampoline); |
+ } |
+ |
+#if 0 |
+ // For simple cases where you just want to zero out a cell when the thing |
+ // it is pointing at is garbage, you can use this. This will register a |
+ // callback for each cell that needs to be zeroed, so if you have a lot of |
+ // weak cells in your object you should still consider using |
+ // registerWeakMembers above. |
+ // |
+ // In contrast to registerWeakMembers, the weak cell callbacks are |
+ // run on the thread performing garbage collection. Therefore, all |
+ // threads are stopped during weak cell callbacks. |
+ template<typename T> |
+ void registerWeakCell(T** cell) |
+ { |
+ registerWeakCell(reinterpret_cast<void**>(cell), &handleWeakCell<T>); |
+ } |
+ |
+ virtual void registerWeakTable(const void*, EphemeronCallback, EphemeronCallback) = 0; |
+#if ENABLE(ASSERT) |
+ virtual bool weakTableRegistered(const void*) = 0; |
+#endif |
+#endif |
+ |
+ bool isMarked(const void* obj); |
+ bool ensureMarked(const void* obj); |
+ |
+ template<typename T> inline bool isAlive(T* obj) |
+ { |
+ // Check that we actually know the definition of T when tracing. |
+ COMPILE_ASSERT(sizeof(T), WeNeedToKnowTheDefinitionOfTheTypeWeAreTracing); |
+ // The strongification of collections relies on the fact that once a |
+ // collection has been strongified, there is no way that it can contain |
+ // non-live entries, so no entries will be removed. Since you can't set |
+ // the mark bit on a null pointer, that means that null pointers are |
+ // always 'alive'. |
+ if (!obj) |
+ return true; |
+ return ObjectAliveTrait<T>::isHeapObjectAlive(this, obj); |
+ } |
+ template<typename T> inline bool isAlive(const Member<T>& member) |
+ { |
+ return isAlive(member.get()); |
+ } |
+ template<typename T> inline bool isAlive(RawPtr<T> ptr) |
+ { |
+ return isAlive(ptr.get()); |
+ } |
+ |
+#if ENABLE(ASSERT) |
+ void checkGCInfo(const void*, const GCInfo*); |
+#endif |
+ |
+ // Macro to declare methods needed for each typed heap. |
+#define DECLARE_VISITOR_METHODS(Type) \ |
+ DEBUG_ONLY(void checkGCInfo(const Type*, const GCInfo*);) \ |
+ void mark(const Type* t, TraceCallback callback) { m_visitor->mark(t, callback); } \ |
+ bool isMarked(const Type* t) { return m_visitor->isMarked(t); }\ |
+ bool ensureMarked(const Type* t) { return m_visitor->ensureMarked(t); } |
+ |
+ FOR_EACH_TYPED_HEAP(DECLARE_VISITOR_METHODS) |
+#undef DECLARE_VISITOR_METHODS |
+ |
+#if ENABLE(GC_PROFILE_MARKING) |
+ void setHostInfo(void* object, const String& name) |
+ { |
+ m_visitor->setHostInfo(object, name); |
+ } |
+#endif |
+ |
+ inline bool canTraceEagerly() const { return true; } |
+ inline void incrementTraceDepth() { } |
+ inline void decrementTraceDepth() { } |
+ |
+ Visitor* getUninlined() { return m_visitor; } |
+ |
+private: |
+ void visitHeader(HeapObjectHeader* header, const void* objectPointer, TraceCallback callback); |
+ |
+ Visitor* m_visitor; |
+}; |
+ |
+template<typename T> |
+void InlinedGlobalMarkingVisitor::mark(T* t) |
+{ |
+ if (!t) |
+ return; |
+#if ENABLE(ASSERT) |
+ TraceTrait<T>::checkGCInfo(m_visitor, t); |
+#endif |
+ TraceTrait<T>::mark(InlinedGlobalMarkingVisitor(*this), t); |
+ |
+ COMPILE_ASSERT_IS_GARBAGE_COLLECTED(T, AttemptedToMarkNonGarbageCollectedObject); |
+} |
+ |
+template <typename T> |
+struct TraceGenericCompatibilityAdaptor<T, true> { |
+ static inline void trace(Visitor* visitor, T* t) |
+ { |
+ t->trace(visitor); |
+ } |
+ |
+ static inline void trace(InlinedGlobalMarkingVisitor visitor, T* t) |
+ { |
+ t->trace(InlinedGlobalMarkingVisitor(visitor)); |
+ } |
+}; |
+ |
+template <typename T> |
+struct TraceGenericCompatibilityAdaptor<T, false> { |
+ static inline void trace(Visitor* visitor, T* t) |
+ { |
+ // Revert to old trace |
+ t->trace(visitor); |
+ } |
+ |
+ static inline void trace(InlinedGlobalMarkingVisitor visitor, T* t) |
+ { |
+ // visiting was inlined to here, but the object we are going to trace doesn't support inlined trace |
+ t->trace(visitor.getUninlined()); |
+ } |
+}; |
+ |
+#define DECLARE_TRACE(virt, ovr) \ |
+ public: \ |
+ virt void trace(Visitor*) ovr; \ |
+ virt void trace(InlinedGlobalMarkingVisitor) ovr; \ |
+ private: \ |
+ template<typename VisitorDispatcher> void traceImpl(VisitorDispatcher); \ |
+ public: |
+ |
+#define DEFINE_TRACE(CLASS) \ |
+ void CLASS::trace(Visitor* visitor) { traceImpl(visitor); } \ |
+ void CLASS::trace(InlinedGlobalMarkingVisitor visitor) { traceImpl(visitor); } \ |
+ template<typename VisitorDispatcher> \ |
+ ALWAYS_INLINE void CLASS::traceImpl(VisitorDispatcher visitor) \ |
+ |
+#define DEFINE_INLINE_TRACE(virt, ovr) \ |
+ virt void trace(Visitor* visitor) ovr { traceImpl(visitor); } \ |
+ virt void trace(InlinedGlobalMarkingVisitor visitor) ovr { traceImpl(visitor); } \ |
+ template<typename VisitorDispatcher> \ |
+ inline void traceImpl(VisitorDispatcher visitor) |
+ |
// We trace vectors by using the trace trait on each element, which means you |
// can have vectors of general objects (not just pointers to objects) that can |
// be traced. |
@@ -598,6 +898,13 @@ public: |
}; |
template<typename T> |
+template<typename VisitorDispatcher> |
+inline void TraceTrait<T>::mark(VisitorDispatcher visitor, const T* t) |
+{ |
+ DefaultTraceTrait<T>::mark(visitor, t); |
+} |
+ |
+template<typename T> |
class DefaultTraceTrait<T, false> { |
public: |
static void mark(Visitor* visitor, const T* t) |
@@ -620,7 +927,10 @@ public: |
if (LIKELY(visitor->canTraceEagerly())) { |
if (visitor->ensureMarked(t)) { |
visitor->incrementTraceDepth(); |
- TraceTrait<T>::trace(visitor, const_cast<T*>(t)); |
+ if (LIKELY(visitor->isGlobalMarkingVisitor())) |
+ TraceTrait<T>::trace(InlinedGlobalMarkingVisitor(visitor), const_cast<T*>(t)); |
+ else |
+ TraceTrait<T>::trace(visitor, const_cast<T*>(t)); |
visitor->decrementTraceDepth(); |
} |
return; |
@@ -629,6 +939,28 @@ public: |
visitor->mark(const_cast<T*>(t), &TraceTrait<T>::trace); |
} |
+ static void mark(InlinedGlobalMarkingVisitor visitor, const T* t) |
+ { |
+ if (TraceEagerlyTrait<T>::value) { |
+ // Protect against too deep trace call chains, and the |
+ // unbounded system stack usage they can bring about. |
+ // |
+ // Assert against deep stacks so as to flush them out, |
+ // but test and appropriately handle them should they occur |
+ // in release builds. |
+ ASSERT(visitor.canTraceEagerly()); |
+ if (LIKELY(visitor.canTraceEagerly())) { |
+ if (visitor.ensureMarked(t)) { |
+ visitor.incrementTraceDepth(); |
+ TraceTrait<T>::trace(visitor, const_cast<T*>(t)); |
+ visitor.decrementTraceDepth(); |
+ } |
+ return; |
+ } |
+ } |
+ visitor.mark(const_cast<T*>(t), &TraceTrait<T>::trace); |
+ } |
+ |
#if ENABLE(ASSERT) |
static void checkGCInfo(Visitor* visitor, const T* t) |
{ |