Bug 1220310 - Generalize GC container trace function dispatch as GCPolicy; r=sfink

This commit is contained in:
Terrence Cole 2015-11-03 07:08:05 -08:00
parent 34ef947e98
commit bca82601d0
6 changed files with 121 additions and 92 deletions

View File

@ -13,15 +13,23 @@
namespace js {
// Define a reasonable default GC policy for GC-aware Maps.
template <typename Key, typename Value>
struct DefaultMapGCPolicy {
using KeyPolicy = DefaultGCPolicy<Key>;
using ValuePolicy = DefaultGCPolicy<Value>;
};
// A TraceableHashMap is a HashMap with an additional trace method that knows
// how to visit all keys and values in the table. HashMaps that contain GC
// pointers that must be traced to be kept alive will generally want to use
// this TraceableHashMap specializeation in lieu of HashMap.
//
// Most types of GC pointers as keys and values can be traced with no extra
// infrastructure. For structs and non-gc-pointer members, ensure that there
// is a specialization of DefaultTracer<T> with an appropriate trace method
// available to handle the custom type.
// infrastructure. For structs and non-gc-pointer members, ensure that there
// is a specialization of DefaultGCPolicy<T> with an appropriate trace method
// available to handle the custom type. Generic helpers can be found in
// js/public/TracingAPI.h.
//
// Note that although this HashMap's trace will deal correctly with moved keys,
// it does not itself know when to barrier or trace keys. To function properly
@ -30,8 +38,7 @@ template <typename Key,
typename Value,
typename HashPolicy = DefaultHasher<Key>,
typename AllocPolicy = TempAllocPolicy,
typename KeyTraceFunc = DefaultTracer<Key>,
typename ValueTraceFunc = DefaultTracer<Value>>
typename GCPolicy = DefaultMapGCPolicy<Key, Value>>
class TraceableHashMap : public HashMap<Key, Value, HashPolicy, AllocPolicy>,
public JS::Traceable
{
@ -45,9 +52,9 @@ class TraceableHashMap : public HashMap<Key, Value, HashPolicy, AllocPolicy>,
if (!this->initialized())
return;
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
ValueTraceFunc::trace(trc, &e.front().value(), "hashmap value");
GCPolicy::ValuePolicy::trace(trc, &e.front().value(), "hashmap value");
Key key = e.front().key();
KeyTraceFunc::trace(trc, &key, "hashmap key");
GCPolicy::KeyPolicy::trace(trc, &key, "hashmap key");
if (key != e.front().key())
e.rekeyFront(key);
}
@ -137,40 +144,40 @@ class MutableTraceableHashMapOperations
}
};
template <typename A, typename B, typename C, typename D, typename E, typename F>
class RootedBase<TraceableHashMap<A,B,C,D,E,F>>
: public MutableTraceableHashMapOperations<JS::Rooted<TraceableHashMap<A,B,C,D,E,F>>, A,B,C,D,E,F>
template <typename A, typename B, typename C, typename D, typename E>
class RootedBase<TraceableHashMap<A,B,C,D,E>>
: public MutableTraceableHashMapOperations<JS::Rooted<TraceableHashMap<A,B,C,D,E>>, A,B,C,D,E>
{};
template <typename A, typename B, typename C, typename D, typename E, typename F>
class MutableHandleBase<TraceableHashMap<A,B,C,D,E,F>>
: public MutableTraceableHashMapOperations<JS::MutableHandle<TraceableHashMap<A,B,C,D,E,F>>,
A,B,C,D,E,F>
template <typename A, typename B, typename C, typename D, typename E>
class MutableHandleBase<TraceableHashMap<A,B,C,D,E>>
: public MutableTraceableHashMapOperations<JS::MutableHandle<TraceableHashMap<A,B,C,D,E>>,
A,B,C,D,E>
{};
template <typename A, typename B, typename C, typename D, typename E, typename F>
class HandleBase<TraceableHashMap<A,B,C,D,E,F>>
: public TraceableHashMapOperations<JS::Handle<TraceableHashMap<A,B,C,D,E,F>>, A,B,C,D,E,F>
template <typename A, typename B, typename C, typename D, typename E>
class HandleBase<TraceableHashMap<A,B,C,D,E>>
: public TraceableHashMapOperations<JS::Handle<TraceableHashMap<A,B,C,D,E>>, A,B,C,D,E>
{};
// A TraceableHashSet is a HashSet with an additional trace method that knows
// how to visit all set element. HashSets that contain GC pointers that must
// how to visit all set elements. HashSets that contain GC pointers that must
// be traced to be kept alive will generally want to use this TraceableHashSet
// specializeation in lieu of HashSet.
//
// Most types of GC pointers can be traced with no extra infrastructure. For
// Most types of GC pointers can be traced with no extra infrastructure. For
// structs and non-gc-pointer members, ensure that there is a specialization of
// DefaultTracer<T> with an appropriate trace method available to handle the
// custom type.
// DefaultGCPolicy<T> with an appropriate trace method available to handle the
// custom type. Generic helpers can be found in js/public/TracingAPI.h.
//
// Note that although this HashSet's trace will deal correctly with moved
// elements, it does not itself know when to barrier or trace elements. To
// elements, it does not itself know when to barrier or trace elements. To
// function properly it must either be used with Rooted or barriered and traced
// manually.
template <typename T,
typename HashPolicy = DefaultHasher<T>,
typename AllocPolicy = TempAllocPolicy,
typename ElemTraceFunc = DefaultTracer<T>>
typename GCPolicy = DefaultGCPolicy<T>>
class TraceableHashSet : public HashSet<T, HashPolicy, AllocPolicy>,
public JS::Traceable
{
@ -185,7 +192,7 @@ class TraceableHashSet : public HashSet<T, HashPolicy, AllocPolicy>,
return;
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
T elem = e.front();
ElemTraceFunc::trace(trc, &elem, "hashset element");
GCPolicy::trace(trc, &elem, "hashset element");
if (elem != e.front())
e.rekeyFront(elem);
}
@ -273,41 +280,42 @@ class MutableTraceableHashSetOperations
}
};
template <typename T, typename HP, typename AP, typename TF>
class RootedBase<TraceableHashSet<T, HP, AP, TF>>
: public MutableTraceableHashSetOperations<JS::Rooted<TraceableHashSet<T, HP, AP, TF>>, T, HP, AP, TF>
template <typename T, typename HP, typename AP, typename GP>
class RootedBase<TraceableHashSet<T, HP, AP, GP>>
: public MutableTraceableHashSetOperations<JS::Rooted<TraceableHashSet<T, HP, AP, GP>>,
T, HP, AP, GP>
{
using Set = TraceableHashSet<T, HP, AP, TF>;
using Set = TraceableHashSet<T, HP, AP, GP>;
friend class TraceableHashSetOperations<JS::Rooted<Set>, T, HP, AP, TF>;
friend class TraceableHashSetOperations<JS::Rooted<Set>, T, HP, AP, GP>;
const Set& extract() const { return *static_cast<const JS::Rooted<Set>*>(this)->address(); }
friend class MutableTraceableHashSetOperations<JS::Rooted<Set>, T, HP, AP, TF>;
friend class MutableTraceableHashSetOperations<JS::Rooted<Set>, T, HP, AP, GP>;
Set& extract() { return *static_cast<JS::Rooted<Set>*>(this)->address(); }
};
template <typename T, typename HP, typename AP, typename TF>
class MutableHandleBase<TraceableHashSet<T, HP, AP, TF>>
: public MutableTraceableHashSetOperations<JS::MutableHandle<TraceableHashSet<T, HP, AP, TF>>,
T, HP, AP, TF>
template <typename T, typename HP, typename AP, typename GP>
class MutableHandleBase<TraceableHashSet<T, HP, AP, GP>>
: public MutableTraceableHashSetOperations<JS::MutableHandle<TraceableHashSet<T, HP, AP, GP>>,
T, HP, AP, GP>
{
using Set = TraceableHashSet<T, HP, AP, TF>;
using Set = TraceableHashSet<T, HP, AP, GP>;
friend class TraceableHashSetOperations<JS::MutableHandle<Set>, T, HP, AP, TF>;
friend class TraceableHashSetOperations<JS::MutableHandle<Set>, T, HP, AP, GP>;
const Set& extract() const {
return *static_cast<const JS::MutableHandle<Set>*>(this)->address();
}
friend class MutableTraceableHashSetOperations<JS::MutableHandle<Set>, T, HP, AP, TF>;
friend class MutableTraceableHashSetOperations<JS::MutableHandle<Set>, T, HP, AP, GP>;
Set& extract() { return *static_cast<JS::MutableHandle<Set>*>(this)->address(); }
};
template <typename T, typename HP, typename AP, typename TF>
class HandleBase<TraceableHashSet<T, HP, AP, TF>>
: public TraceableHashSetOperations<JS::Handle<TraceableHashSet<T, HP, AP, TF>>, T, HP, AP, TF>
template <typename T, typename HP, typename AP, typename GP>
class HandleBase<TraceableHashSet<T, HP, AP, GP>>
: public TraceableHashSetOperations<JS::Handle<TraceableHashSet<T, HP, AP, GP>>, T, HP, AP, GP>
{
using Set = TraceableHashSet<T, HP, AP, TF>;
friend class TraceableHashSetOperations<JS::Handle<Set>, T, HP, AP, TF>;
using Set = TraceableHashSet<T, HP, AP, GP>;
friend class TraceableHashSetOperations<JS::Handle<Set>, T, HP, AP, GP>;
const Set& extract() const { return *static_cast<const JS::Handle<Set>*>(this)->address(); }
};

View File

@ -22,8 +22,9 @@ namespace js {
//
// Most types of GC pointers as keys and values can be traced with no extra
// infrastructure. For structs and non-gc-pointer members, ensure that there
// is a specialization of DefaultTracer<T> with an appropriate trace method
// available to handle the custom type.
// is a specialization of DefaultGCPolicy<T> with an appropriate trace method
// available to handle the custom type. Generic helpers can be found in
// js/public/TracingAPI.h.
//
// Note that although this Vector's trace will deal correctly with moved items,
// it does not itself know when to barrier or trace items. To function properly
@ -31,12 +32,12 @@ namespace js {
template <typename T,
size_t MinInlineCapacity = 0,
typename AllocPolicy = TempAllocPolicy,
typename TraceFunc = DefaultTracer<T>>
typename GCPolicy = DefaultGCPolicy<T>>
class TraceableVector
: public mozilla::VectorBase<T,
MinInlineCapacity,
AllocPolicy,
TraceableVector<T, MinInlineCapacity, AllocPolicy, TraceFunc>>,
TraceableVector<T, MinInlineCapacity, AllocPolicy, GCPolicy>>,
public JS::Traceable
{
using Base = mozilla::VectorBase<T, MinInlineCapacity, AllocPolicy, TraceableVector>;
@ -51,14 +52,14 @@ class TraceableVector
static void trace(TraceableVector* vec, JSTracer* trc) { vec->trace(trc); }
void trace(JSTracer* trc) {
for (size_t i = 0; i < this->length(); ++i)
TraceFunc::trace(trc, &Base::operator[](i), "vector element");
GCPolicy::trace(trc, &Base::operator[](i), "vector element");
}
};
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename TraceFunc>
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename GCPolicy>
class TraceableVectorOperations
{
using Vec = TraceableVector<T, Capacity, AllocPolicy, TraceFunc>;
using Vec = TraceableVector<T, Capacity, AllocPolicy, GCPolicy>;
const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
public:
@ -76,11 +77,11 @@ class TraceableVectorOperations
}
};
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename TraceFunc>
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename GCPolicy>
class MutableTraceableVectorOperations
: public TraceableVectorOperations<Outer, T, Capacity, AllocPolicy, TraceFunc>
: public TraceableVectorOperations<Outer, T, Capacity, AllocPolicy, GCPolicy>
{
using Vec = TraceableVector<T, Capacity, AllocPolicy, TraceFunc>;
using Vec = TraceableVector<T, Capacity, AllocPolicy, GCPolicy>;
const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
Vec& vec() { return static_cast<Outer*>(this)->get(); }
@ -141,26 +142,26 @@ class MutableTraceableVectorOperations
void erase(T* aBegin, T* aEnd) { vec().erase(aBegin, aEnd); }
};
template <typename T, size_t N, typename AP, typename TP>
class RootedBase<TraceableVector<T,N,AP,TP>>
: public MutableTraceableVectorOperations<JS::Rooted<TraceableVector<T,N,AP,TP>>, T,N,AP,TP>
template <typename T, size_t N, typename AP, typename GP>
class RootedBase<TraceableVector<T,N,AP,GP>>
: public MutableTraceableVectorOperations<JS::Rooted<TraceableVector<T,N,AP,GP>>, T,N,AP,GP>
{};
template <typename T, size_t N, typename AP, typename TP>
class MutableHandleBase<TraceableVector<T,N,AP,TP>>
: public MutableTraceableVectorOperations<JS::MutableHandle<TraceableVector<T,N,AP,TP>>,
T,N,AP,TP>
template <typename T, size_t N, typename AP, typename GP>
class MutableHandleBase<TraceableVector<T,N,AP,GP>>
: public MutableTraceableVectorOperations<JS::MutableHandle<TraceableVector<T,N,AP,GP>>,
T,N,AP,GP>
{};
template <typename T, size_t N, typename AP, typename TP>
class HandleBase<TraceableVector<T,N,AP,TP>>
: public TraceableVectorOperations<JS::Handle<TraceableVector<T,N,AP,TP>>, T,N,AP,TP>
template <typename T, size_t N, typename AP, typename GP>
class HandleBase<TraceableVector<T,N,AP,GP>>
: public TraceableVectorOperations<JS::Handle<TraceableVector<T,N,AP,GP>>, T,N,AP,GP>
{};
template <typename T, size_t N, typename AP, typename TP>
class PersistentRootedBase<TraceableVector<T,N,AP,TP>>
: public MutableTraceableVectorOperations<JS::PersistentRooted<TraceableVector<T,N,AP,TP>>,
T,N,AP,TP>
template <typename T, size_t N, typename AP, typename GP>
class PersistentRootedBase<TraceableVector<T,N,AP,GP>>
: public MutableTraceableVectorOperations<JS::PersistentRooted<TraceableVector<T,N,AP,GP>>,
T,N,AP,GP>
{};
} // namespace js

View File

@ -369,31 +369,44 @@ JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc,
namespace js {
// Automates static dispatch for tracing for TraceableContainers.
template <typename> struct DefaultTracer;
// Automates static dispatch for GC interaction with TraceableContainers.
template <typename>
struct DefaultGCPolicy;
// The default for non-pod (e.g. struct) types is to call the trace method.
// This policy dispatches GC methods to a method on the type.
template <typename T>
struct DefaultTracer {
struct StructGCPolicy {
static void trace(JSTracer* trc, T* t, const char* name) {
// This is the default GC policy for storing GC things in containers.
// If your build is failing here, it means you either need an
// implementation of DefaultTracer<T> for your type or, for container
// and structure types that contain GC pointers, a trace method.
// implementation of DefaultGCPolicy<T> for your type or, if this is
// the right policy for you, your struct or container is missing a
// trace method.
t->trace(trc);
}
};
// This policy ignores any GC interaction, e.g. for non-GC types.
template <typename T>
struct IgnoreGCPolicy {
static void trace(JSTracer* trc, uint32_t* id, const char* name) {}
};
// The default policy when no other more specific policy fits (e.g. for a
// direct GC pointer), is to assume a struct type that implements the needed
// methods.
template <typename T>
struct DefaultGCPolicy : public StructGCPolicy<T> {};
template <>
struct DefaultTracer<jsid>
struct DefaultGCPolicy<jsid>
{
static void trace(JSTracer* trc, jsid* id, const char* name) {
JS_CallUnbarrieredIdTracer(trc, id, name);
}
};
template <> struct DefaultTracer<uint32_t> {
static void trace(JSTracer* trc, uint32_t* id, const char* name) {}
};
template <> struct DefaultGCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
} // namespace js

View File

@ -9,11 +9,10 @@
#include "ds/Fifo.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
namespace js {
template <typename> struct DefaultTracer;
// A TraceableFifo is a Fifo with an additional trace method that knows how to
// visit all of the items stored in the Fifo. For Fifos that contain GC things,
// this is usually more convenient than manually iterating and marking the
@ -21,8 +20,10 @@ template <typename> struct DefaultTracer;
//
// Most types of GC pointers as keys and values can be traced with no extra
// infrastructure. For structs and non-gc-pointer members, ensure that there is
// a specialization of DefaultTracer<T> with an appropriate trace method
// available to handle the custom type.
// a specialization of DefaultGCPolicy<T> with an appropriate trace method
// available to handle the custom type. Generic helpers can be found in
// js/public/TracingAPI.h. Generic helpers can be found in
// js/public/TracingAPI.h.
//
// Note that although this Fifo's trace will deal correctly with moved items, it
// does not itself know when to barrier or trace items. To function properly it
@ -30,7 +31,7 @@ template <typename> struct DefaultTracer;
template <typename T,
size_t MinInlineCapacity = 0,
typename AllocPolicy = TempAllocPolicy,
typename TraceFunc = DefaultTracer<T>>
typename GCPolicy = DefaultGCPolicy<T>>
class TraceableFifo
: public js::Fifo<T, MinInlineCapacity, AllocPolicy>,
public JS::Traceable
@ -48,16 +49,16 @@ class TraceableFifo
static void trace(TraceableFifo* tf, JSTracer* trc) {
for (size_t i = 0; i < tf->front_.length(); ++i)
TraceFunc::trace(trc, &tf->front_[i], "fifo element");
GCPolicy::trace(trc, &tf->front_[i], "fifo element");
for (size_t i = 0; i < tf->rear_.length(); ++i)
TraceFunc::trace(trc, &tf->rear_[i], "fifo element");
GCPolicy::trace(trc, &tf->rear_[i], "fifo element");
}
};
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename TraceFunc>
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename GCPolicy>
class TraceableFifoOperations
{
using TF = TraceableFifo<T, Capacity, AllocPolicy, TraceFunc>;
using TF = TraceableFifo<T, Capacity, AllocPolicy, GCPolicy>;
const TF& fifo() const { return static_cast<const Outer*>(this)->extract(); }
public:
@ -66,11 +67,11 @@ class TraceableFifoOperations
const T& front() const { return fifo().front(); }
};
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename TraceFunc>
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename GCPolicy>
class MutableTraceableFifoOperations
: public TraceableFifoOperations<Outer, T, Capacity, AllocPolicy, TraceFunc>
: public TraceableFifoOperations<Outer, T, Capacity, AllocPolicy, GCPolicy>
{
using TF = TraceableFifo<T, Capacity, AllocPolicy, TraceFunc>;
using TF = TraceableFifo<T, Capacity, AllocPolicy, GCPolicy>;
TF& fifo() { return static_cast<Outer*>(this)->extract(); }
public:

View File

@ -138,16 +138,22 @@ TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group);
} // namespace gc
// Define a default Policy for all pointer types. This may fail to link if this
// policy gets used on a non-GC typed pointer by accident.
template <typename T>
struct DefaultTracer<T*>
struct DefaultGCPolicy<T*>
{
static void trace(JSTracer* trc, T** t, const char* name) {
// If linking is failing here, it likely means that you need to define
// or use a non-default GC policy for your non-gc-pointer type.
TraceManuallyBarrieredEdge(trc, t, name);
}
};
// RelocatablePtr is only defined for GC pointer types, so this default policy
// should work in all cases.
template <typename T>
struct DefaultTracer<RelocatablePtr<T*>>
struct DefaultGCPolicy<RelocatablePtr<T*>>
{
static void trace(JSTracer* trc, RelocatablePtr<T*> t, const char* name) {
TraceEdge(trc, t, name);

View File

@ -943,14 +943,14 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
};
template<>
struct DefaultTracer<Debugger::TenurePromotionsLogEntry> {
struct DefaultGCPolicy<Debugger::TenurePromotionsLogEntry> {
static void trace(JSTracer* trc, Debugger::TenurePromotionsLogEntry* e, const char*) {
Debugger::TenurePromotionsLogEntry::trace(e, trc);
}
};
template<>
struct DefaultTracer<Debugger::AllocationsLogEntry> {
struct DefaultGCPolicy<Debugger::AllocationsLogEntry> {
static void trace(JSTracer* trc, Debugger::AllocationsLogEntry* e, const char*) {
Debugger::AllocationsLogEntry::trace(e, trc);
}