blob: ac051c106a1917307fcd9ecaba11bde71abb7cf9 [file] [log] [blame]
// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_TRACE_TRAITS_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_TRACE_TRAITS_H_
#include "base/optional.h"
#include "third_party/blink/renderer/platform/heap/gc_info.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/heap/visitor.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/hash_counted_set.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/hash_table.h"
#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
#include "third_party/blink/renderer/platform/wtf/list_hash_set.h"
#include "third_party/blink/renderer/platform/wtf/type_traits.h"
namespace blink {
template <typename ValueArg, wtf_size_t inlineCapacity>
class HeapListHashSetAllocator;
template <typename T>
class TraceTrait;
template <typename T>
class WeakMember;
template <typename T, bool = NeedsAdjustPointer<T>::value>
class AdjustPointerTrait;
template <typename T>
class AdjustPointerTrait<T, false> {
STATIC_ONLY(AdjustPointerTrait);
public:
static TraceDescriptor GetTraceDescriptor(void* self) {
return {self, TraceTrait<T>::Trace};
}
static HeapObjectHeader* GetHeapObjectHeader(void* self) {
#if DCHECK_IS_ON()
HeapObjectHeader::CheckFromPayload(self);
#endif
return HeapObjectHeader::FromPayload(self);
}
};
template <typename T>
class AdjustPointerTrait<T, true> {
STATIC_ONLY(AdjustPointerTrait);
public:
static TraceDescriptor GetTraceDescriptor(const T* self) {
DCHECK(self);
return self->GetTraceDescriptor();
}
static HeapObjectHeader* GetHeapObjectHeader(const T* self) {
DCHECK(self);
return self->GetHeapObjectHeader();
}
};
template <typename T, bool isTraceable>
struct TraceIfEnabled;
template <typename T>
struct TraceIfEnabled<T, false> {
STATIC_ONLY(TraceIfEnabled);
template <typename VisitorDispatcher>
static void Trace(VisitorDispatcher, T&) {
static_assert(!WTF::IsTraceable<T>::value, "T should not be traced");
}
};
template <typename T>
struct TraceIfEnabled<T, true> {
STATIC_ONLY(TraceIfEnabled);
template <typename VisitorDispatcher>
static void Trace(VisitorDispatcher visitor, T& t) {
static_assert(WTF::IsTraceable<T>::value, "T should not be traced");
visitor->Trace(t);
}
};
template <WTF::WeakHandlingFlag weakness,
typename T,
typename Traits,
bool isTraceableInCollection =
WTF::IsTraceableInCollectionTrait<Traits>::value,
WTF::WeakHandlingFlag traitWeakHandling = Traits::kWeakHandlingFlag>
struct TraceCollectionIfEnabled;
template <WTF::WeakHandlingFlag weakness, typename T, typename Traits>
struct TraceCollectionIfEnabled<weakness,
T,
Traits,
false,
WTF::kNoWeakHandling> {
STATIC_ONLY(TraceCollectionIfEnabled);
static bool IsAlive(T&) { return true; }
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher, T&) {
static_assert(!WTF::IsTraceableInCollectionTrait<Traits>::value,
"T should not be traced");
return false;
}
};
template <typename T, typename Traits>
struct TraceCollectionIfEnabled<WTF::kNoWeakHandling,
T,
Traits,
false,
WTF::kWeakHandling> {
STATIC_ONLY(TraceCollectionIfEnabled);
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, T& t) {
return WTF::TraceInCollectionTrait<WTF::kNoWeakHandling, T, Traits>::Trace(
visitor, t);
}
};
template <WTF::WeakHandlingFlag weakness,
typename T,
typename Traits,
bool isTraceableInCollection,
WTF::WeakHandlingFlag traitWeakHandling>
struct TraceCollectionIfEnabled {
STATIC_ONLY(TraceCollectionIfEnabled);
static bool IsAlive(T& traceable) {
return WTF::TraceInCollectionTrait<weakness, T, Traits>::IsAlive(traceable);
}
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, T& t) {
static_assert(WTF::IsTraceableInCollectionTrait<Traits>::value ||
weakness == WTF::kWeakHandling,
"Traits should be traced");
return WTF::TraceInCollectionTrait<weakness, T, Traits>::Trace(visitor, t);
}
};
// The TraceTrait is used to specify how to trace and object for Oilpan and
// wrapper tracing.
//
//
// By default, the 'Trace' method implemented on an object itself is
// used to trace the pointers to other heap objects inside the object.
//
// However, the TraceTrait can be specialized to use a different
// implementation. A common case where a TraceTrait specialization is
// needed is when multiple inheritance leads to pointers that are not
// to the start of the object in the Blink garbage-collected heap. In
// that case the pointer has to be adjusted before marking.
template <typename T>
class TraceTrait {
STATIC_ONLY(TraceTrait);
public:
static TraceDescriptor GetTraceDescriptor(void* self) {
return AdjustPointerTrait<T>::GetTraceDescriptor(static_cast<T*>(self));
}
static HeapObjectHeader* GetHeapObjectHeader(void* self) {
return AdjustPointerTrait<T>::GetHeapObjectHeader(static_cast<T*>(self));
}
static void Trace(Visitor*, void* self);
};
template <typename T>
class TraceTrait<const T> : public TraceTrait<T> {};
template <typename T>
void TraceTrait<T>::Trace(Visitor* visitor, void* self) {
static_assert(WTF::IsTraceable<T>::value, "T should not be traced");
static_cast<T*>(self)->Trace(visitor);
}
template <typename T, typename Traits>
struct TraceTrait<HeapVectorBacking<T, Traits>> {
STATIC_ONLY(TraceTrait);
using Backing = HeapVectorBacking<T, Traits>;
static TraceDescriptor GetTraceDescriptor(void* self) {
return {self, TraceTrait<Backing>::Trace};
}
template <typename VisitorDispatcher>
static void Trace(VisitorDispatcher visitor, void* self) {
static_assert(!WTF::IsWeak<T>::value,
"Weakness is not supported in HeapVector and HeapDeque");
if (WTF::IsTraceableInCollectionTrait<Traits>::value) {
WTF::TraceInCollectionTrait<WTF::kNoWeakHandling,
HeapVectorBacking<T, Traits>,
void>::Trace(visitor, self);
}
}
};
// The trace trait for the heap hashtable backing is used when we find a
// direct pointer to the backing from the conservative stack scanner. This
// normally indicates that there is an ongoing iteration over the table, and so
// we disable weak processing of table entries. When the backing is found
// through the owning hash table we mark differently, in order to do weak
// processing.
template <typename Table>
struct TraceTrait<HeapHashTableBacking<Table>> {
STATIC_ONLY(TraceTrait);
using Backing = HeapHashTableBacking<Table>;
using Traits = typename Table::ValueTraits;
static TraceDescriptor GetTraceDescriptor(void* self) {
return {self, TraceTrait<Backing>::Trace};
}
template <typename VisitorDispatcher>
static void Trace(VisitorDispatcher visitor, void* self) {
if (WTF::IsTraceableInCollectionTrait<Traits>::value ||
Traits::kWeakHandlingFlag == WTF::kWeakHandling) {
WTF::TraceInCollectionTrait<WTF::kNoWeakHandling, Backing, void>::Trace(
visitor, self);
}
}
};
// This trace trait for std::pair will null weak members if their referent is
// collected. If you have a collection that contain weakness it does not remove
// entries from the collection that contain nulled weak members.
template <typename T, typename U>
class TraceTrait<std::pair<T, U>> {
STATIC_ONLY(TraceTrait);
public:
static const bool kFirstIsTraceable = WTF::IsTraceable<T>::value;
static const bool kSecondIsTraceable = WTF::IsTraceable<U>::value;
template <typename VisitorDispatcher>
static void Trace(VisitorDispatcher visitor, std::pair<T, U>* pair) {
TraceIfEnabled<T, kFirstIsTraceable>::Trace(visitor, pair->first);
TraceIfEnabled<U, kSecondIsTraceable>::Trace(visitor, pair->second);
}
};
// While using base::Optional<T> with garbage-collected types is generally
// disallowed by the OptionalGarbageCollected check in blink_gc_plugin,
// garbage-collected containers such as HeapVector are allowed and need to be
// traced.
template <typename T>
class TraceTrait<base::Optional<T>> {
STATIC_ONLY(TraceTrait);
public:
template <typename VisitorDispatcher>
static void Trace(VisitorDispatcher visitor, base::Optional<T>* optional) {
if (*optional != base::nullopt) {
TraceIfEnabled<T, WTF::IsTraceable<T>::value>::Trace(visitor,
optional->value());
}
}
};
template <typename T>
struct TraceIfNeeded : public TraceIfEnabled<T, WTF::IsTraceable<T>::value> {
STATIC_ONLY(TraceIfNeeded);
};
} // namespace blink
namespace WTF {
// Helper used for tracing without weak handling in collections that support
// weak handling. It dispatches to |TraceInCollection| if the provided trait
// supports weak handling, and to |Trace| otherwise.
template <typename T,
typename Traits,
WTF::WeakHandlingFlag traitsWeakness = Traits::kWeakHandlingFlag>
struct TraceNoWeakHandlingInCollectionHelper;
template <typename T, typename Traits>
struct TraceNoWeakHandlingInCollectionHelper<T, Traits, kWeakHandling> {
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, T& t) {
return Traits::TraceInCollection(visitor, t, kNoWeakHandling);
}
};
template <typename T, typename Traits>
struct TraceNoWeakHandlingInCollectionHelper<T, Traits, kNoWeakHandling> {
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, T& t) {
static_assert(IsTraceableInCollectionTrait<Traits>::value,
"T should not be traced");
visitor->Trace(t);
return false;
}
};
// Catch-all for types that have a way to trace that don't have special
// handling for weakness in collections. This means that if this type
// contains WeakMember fields, they will simply be zeroed, but the entry
// will not be removed from the collection. This always happens for
// things in vectors, which don't currently support special handling of
// weak elements.
template <typename T, typename Traits>
struct TraceInCollectionTrait<kNoWeakHandling, T, Traits> {
static bool IsAlive(T& t) { return true; }
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, T& t) {
return TraceNoWeakHandlingInCollectionHelper<T, Traits>::Trace(visitor, t);
}
};
// Catch-all for types that have HashTrait support for tracing with weakness.
template <typename T, typename Traits>
struct TraceInCollectionTrait<kWeakHandling, T, Traits> {
static bool IsAlive(T& value) { return Traits::IsAlive(value); }
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, T& t) {
return Traits::TraceInCollection(visitor, t, kWeakHandling);
}
};
// This trace method is used only for on-stack HeapVectors found in
// conservative scanning. On-heap HeapVectors are traced by Vector::trace.
template <typename T, typename Traits>
struct TraceInCollectionTrait<kNoWeakHandling,
blink::HeapVectorBacking<T, Traits>,
void> {
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, void* self) {
// HeapVectorBacking does not know the exact size of the vector
// and just knows the capacity of the vector. Due to the constraint,
// HeapVectorBacking can support only the following objects:
//
// - An object that has a vtable. In this case, HeapVectorBacking
// traces only slots that are not zeroed out. This is because if
// the object has a vtable, the zeroed slot means that it is
// an unused slot (Remember that the unused slots are guaranteed
// to be zeroed out by VectorUnusedSlotClearer).
//
// - An object that can be initialized with memset. In this case,
// HeapVectorBacking traces all slots including unused slots.
// This is fine because the fact that the object can be initialized
// with memset indicates that it is safe to treat the zerod slot
// as a valid object.
static_assert(!IsTraceableInCollectionTrait<Traits>::value ||
Traits::kCanClearUnusedSlotsWithMemset ||
std::is_polymorphic<T>::value,
"HeapVectorBacking doesn't support objects that cannot be "
"cleared as unused with memset.");
// This trace method is instantiated for vectors where
// IsTraceableInCollectionTrait<Traits>::value is false, but the trace
// method should not be called. Thus we cannot static-assert
// IsTraceableInCollectionTrait<Traits>::value but should runtime-assert it.
DCHECK(IsTraceableInCollectionTrait<Traits>::value);
T* array = reinterpret_cast<T*>(self);
blink::HeapObjectHeader* header =
blink::HeapObjectHeader::FromPayload(self);
// Use the payload size as recorded by the heap to determine how many
// elements to trace.
size_t length = header->PayloadSize() / sizeof(T);
#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
// As commented above, HeapVectorBacking can trace unused slots
// (which are already zeroed out).
ANNOTATE_CHANGE_SIZE(array, length, 0, length);
#endif
if (std::is_polymorphic<T>::value) {
char* pointer = reinterpret_cast<char*>(array);
for (unsigned i = 0; i < length; ++i) {
char* element = pointer + i * sizeof(T);
if (blink::VTableInitialized(element))
blink::TraceIfEnabled<
T, IsTraceableInCollectionTrait<Traits>::value>::Trace(visitor,
array[i]);
}
} else {
for (size_t i = 0; i < length; ++i)
blink::TraceIfEnabled<
T, IsTraceableInCollectionTrait<Traits>::value>::Trace(visitor,
array[i]);
}
return false;
}
};
// This trace method is used only for on-stack HeapHashTables found in
// conservative scanning. On-heap HeapHashTables are traced by HashTable::trace.
template <typename Table>
struct TraceInCollectionTrait<kNoWeakHandling,
blink::HeapHashTableBacking<Table>,
void> {
using Value = typename Table::ValueType;
using Traits = typename Table::ValueTraits;
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, void* self) {
static_assert(IsTraceableInCollectionTrait<Traits>::value ||
Traits::kWeakHandlingFlag == kWeakHandling,
"Table should not be traced");
Value* array = reinterpret_cast<Value*>(self);
blink::HeapObjectHeader* header =
blink::HeapObjectHeader::FromPayload(self);
// Use the payload size as recorded by the heap to determine how many
// elements to trace.
size_t length = header->PayloadSize() / sizeof(Value);
for (size_t i = 0; i < length; ++i) {
if (!HashTableHelper<Value, typename Table::ExtractorType,
typename Table::KeyTraitsType>::
IsEmptyOrDeletedBucket(array[i])) {
blink::TraceCollectionIfEnabled<kNoWeakHandling, Value, Traits>::Trace(
visitor, array[i]);
}
}
return false;
}
};
// This specialization of TraceInCollectionTrait is for the backing of
// HeapListHashSet. This is for the case that we find a reference to the
// backing from the stack. That probably means we have a GC while we are in a
// ListHashSet method since normal API use does not put pointers to the backing
// on the stack.
template <typename NodeContents,
size_t inlineCapacity,
typename T,
typename U,
typename V,
typename W,
typename X,
typename Y>
struct TraceInCollectionTrait<
kNoWeakHandling,
blink::HeapHashTableBacking<HashTable<
ListHashSetNode<NodeContents,
blink::HeapListHashSetAllocator<T, inlineCapacity>>*,
U,
V,
W,
X,
Y,
blink::HeapAllocator>>,
void> {
using Node =
ListHashSetNode<NodeContents,
blink::HeapListHashSetAllocator<T, inlineCapacity>>;
using Table = HashTable<Node*, U, V, W, X, Y, blink::HeapAllocator>;
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, void* self) {
Node** array = reinterpret_cast<Node**>(self);
blink::HeapObjectHeader* header =
blink::HeapObjectHeader::FromPayload(self);
size_t length = header->PayloadSize() / sizeof(Node*);
for (size_t i = 0; i < length; ++i) {
if (!HashTableHelper<Node*, typename Table::ExtractorType,
typename Table::KeyTraitsType>::
IsEmptyOrDeletedBucket(array[i])) {
visitor->Trace(array[i]);
}
}
return false;
}
};
// Key value pairs, as used in HashMap. To disambiguate template choice we have
// to have two versions, first the one with no special weak handling, then the
// one with weak handling.
template <typename Key, typename Value, typename Traits>
struct TraceInCollectionTrait<kNoWeakHandling,
KeyValuePair<Key, Value>,
Traits> {
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, KeyValuePair<Key, Value>& self) {
static_assert(IsTraceableInCollectionTrait<Traits>::value ||
Traits::kWeakHandlingFlag == WTF::kWeakHandling,
"T should not be traced");
blink::TraceCollectionIfEnabled<
kNoWeakHandling, Key, typename Traits::KeyTraits>::Trace(visitor,
self.key);
blink::TraceCollectionIfEnabled<
kNoWeakHandling, Value,
typename Traits::ValueTraits>::Trace(visitor, self.value);
return false;
}
};
template <typename Key, typename Value, typename Traits>
struct TraceInCollectionTrait<kWeakHandling, KeyValuePair<Key, Value>, Traits> {
static constexpr bool kKeyIsWeak =
Traits::KeyTraits::kWeakHandlingFlag == kWeakHandling;
static constexpr bool kValueIsWeak =
Traits::ValueTraits::kWeakHandlingFlag == kWeakHandling;
static const bool kKeyHasStrongRefs =
IsTraceableInCollectionTrait<typename Traits::KeyTraits>::value;
static const bool kValueHasStrongRefs =
IsTraceableInCollectionTrait<typename Traits::ValueTraits>::value;
static bool IsAlive(KeyValuePair<Key, Value>& self) {
static_assert(!kKeyIsWeak || !kValueIsWeak || !kKeyHasStrongRefs ||
!kValueHasStrongRefs,
"this configuration is disallowed to avoid unexpected leaks");
if ((kValueIsWeak && !kKeyIsWeak) ||
(kValueIsWeak && kKeyIsWeak && !kValueHasStrongRefs)) {
// Check value first.
bool value_side_alive = blink::TraceCollectionIfEnabled<
Traits::ValueTraits::kWeakHandlingFlag, Value,
typename Traits::ValueTraits>::IsAlive(self.value);
if (!value_side_alive)
return false;
return blink::TraceCollectionIfEnabled<
Traits::KeyTraits::kWeakHandlingFlag, Key,
typename Traits::KeyTraits>::IsAlive(self.key);
}
// Check key first.
bool key_side_alive = blink::TraceCollectionIfEnabled<
Traits::KeyTraits::kWeakHandlingFlag, Key,
typename Traits::KeyTraits>::IsAlive(self.key);
if (!key_side_alive)
return false;
return blink::TraceCollectionIfEnabled<
Traits::ValueTraits::kWeakHandlingFlag, Value,
typename Traits::ValueTraits>::IsAlive(self.value);
}
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, KeyValuePair<Key, Value>& self) {
// This is the core of the ephemeron-like functionality. If there is
// weakness on the key side then we first check whether there are
// dead weak pointers on that side, and if there are we don't mark the
// value side (yet). Conversely if there is weakness on the value side
// we check that first and don't mark the key side yet if we find dead
// weak pointers.
// Corner case: If there is weakness on both the key and value side,
// and there are also strong pointers on the both sides then we could
// unexpectedly leak. The scenario is that the weak pointer on the key
// side is alive, which causes the strong pointer on the key side to be
// marked. If that then results in the object pointed to by the weak
// pointer on the value side being marked live, then the whole
// key-value entry is leaked. To avoid unexpected leaking, we disallow
// this case, but if you run into this assert, please reach out to Blink
// reviewers, and we may relax it.
static_assert(!kKeyIsWeak || !kValueIsWeak || !kKeyHasStrongRefs ||
!kValueHasStrongRefs,
"this configuration is disallowed to avoid unexpected leaks");
if ((kValueIsWeak && !kKeyIsWeak) ||
(kValueIsWeak && kKeyIsWeak && !kValueHasStrongRefs)) {
// Check value first.
bool dead_weak_objects_found_on_value_side =
blink::TraceCollectionIfEnabled<
Traits::ValueTraits::kWeakHandlingFlag, Value,
typename Traits::ValueTraits>::Trace(visitor, self.value);
if (dead_weak_objects_found_on_value_side)
return true;
return blink::TraceCollectionIfEnabled<
Traits::KeyTraits::kWeakHandlingFlag, Key,
typename Traits::KeyTraits>::Trace(visitor, self.key);
}
// Check key first.
bool dead_weak_objects_found_on_key_side = blink::TraceCollectionIfEnabled<
Traits::KeyTraits::kWeakHandlingFlag, Key,
typename Traits::KeyTraits>::Trace(visitor, self.key);
if (dead_weak_objects_found_on_key_side)
return true;
return blink::TraceCollectionIfEnabled<
Traits::ValueTraits::kWeakHandlingFlag, Value,
typename Traits::ValueTraits>::Trace(visitor, self.value);
}
};
// Nodes used by LinkedHashSet. Again we need two versions to disambiguate the
// template.
template <typename Value, typename Allocator, typename Traits>
struct TraceInCollectionTrait<kNoWeakHandling,
LinkedHashSetNode<Value, Allocator>,
Traits> {
static bool IsAlive(LinkedHashSetNode<Value, Allocator>& self) {
return TraceInCollectionTrait<
kNoWeakHandling, Value,
typename Traits::ValueTraits>::IsAlive(self.value_);
}
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor,
LinkedHashSetNode<Value, Allocator>& self) {
static_assert(IsTraceableInCollectionTrait<Traits>::value ||
Traits::kWeakHandlingFlag == WTF::kWeakHandling,
"T should not be traced");
return TraceInCollectionTrait<
kNoWeakHandling, Value,
typename Traits::ValueTraits>::Trace(visitor, self.value_);
}
};
template <typename Value, typename Allocator, typename Traits>
struct TraceInCollectionTrait<kWeakHandling,
LinkedHashSetNode<Value, Allocator>,
Traits> {
static bool IsAlive(LinkedHashSetNode<Value, Allocator>& self) {
return TraceInCollectionTrait<
kWeakHandling, Value,
typename Traits::ValueTraits>::IsAlive(self.value_);
}
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor,
LinkedHashSetNode<Value, Allocator>& self) {
return TraceInCollectionTrait<
kWeakHandling, Value, typename Traits::ValueTraits>::Trace(visitor,
self.value_);
}
};
// ListHashSetNode pointers (a ListHashSet is implemented as a hash table of
// these pointers).
template <typename Value, size_t inlineCapacity, typename Traits>
struct TraceInCollectionTrait<
kNoWeakHandling,
ListHashSetNode<Value,
blink::HeapListHashSetAllocator<Value, inlineCapacity>>*,
Traits> {
using Node =
ListHashSetNode<Value,
blink::HeapListHashSetAllocator<Value, inlineCapacity>>;
template <typename VisitorDispatcher>
static bool Trace(VisitorDispatcher visitor, Node* node) {
static_assert(IsTraceableInCollectionTrait<Traits>::value ||
Traits::kWeakHandlingFlag == WTF::kWeakHandling,
"T should not be traced");
visitor->Trace(node);
return false;
}
};
} // namespace WTF
#endif