Bug 1634459 - Simplify the wrapper used for rooting non-GC thing types r=jandem

Previously this stored a function pointer to do the tracing along with the rooted thing. The patch changes this to use a virtual method for the tracing. Calling the method on the base class means we don't need to do address arithmetic to find wher the trace function is stored and we don't need the alignment restrictions, because the virtual trace method knows the layout of its class.

I had to add a traits class, mainly to get the address of the rooted thing inside the wrapper since we need to be able to get the address of the wrapper itself now for tracing.

The wrapper class is renamed from DispatchWrapper to RootedTraceable.

Differential Revision: https://phabricator.services.mozilla.com/D73421
This commit is contained in:
Jon Coppeard 2020-05-04 16:53:56 +00:00
parent fd4b8f30fb
commit 4c9a0dab80
4 changed files with 99 additions and 85 deletions

View File

@ -874,43 +874,43 @@ struct FallibleHashMethods<js::MovableCellHasher<T>> {
namespace js {
// The alignment must be set because the Rooted and PersistentRooted ptr fields
// may be accessed through reinterpret_cast<Rooted<ConcreteTraceable>*>, and
// the compiler may choose a different alignment for the ptr field when it
// knows the actual type stored in DispatchWrapper<T>.
//
// It would make more sense to align only those specific fields of type
// DispatchWrapper, rather than DispatchWrapper itself, but that causes MSVC to
// fail when Rooted is used in an IsConvertible test.
struct VirtualTraceable {
virtual ~VirtualTraceable() = default;
virtual void trace(JSTracer* trc, const char* name) = 0;
};
template <typename T>
class alignas(8) DispatchWrapper {
struct RootedTraceable final : public VirtualTraceable {
static_assert(JS::MapTypeToRootKind<T>::kind == JS::RootKind::Traceable,
"DispatchWrapper is intended only for usage with a Traceable");
"RootedTraceable is intended only for usage with a Traceable");
using TraceFn = void (*)(JSTracer*, T*, const char*);
TraceFn tracer;
alignas(gc::CellAlignBytes) T storage;
T ptr;
public:
template <typename U>
MOZ_IMPLICIT DispatchWrapper(U&& initial)
: tracer(&JS::GCPolicy<T>::trace), storage(std::forward<U>(initial)) {}
MOZ_IMPLICIT RootedTraceable(U&& initial) : ptr(std::forward<U>(initial)) {}
// Mimic a pointer type, so that we can drop into Rooted.
T* operator&() { return &storage; }
const T* operator&() const { return &storage; }
operator T&() { return storage; }
operator const T&() const { return storage; }
operator T&() { return ptr; }
operator const T&() const { return ptr; }
// Trace the contained storage (of unknown type) using the trace function
// we set aside when we did know the type.
static void TraceWrapped(JSTracer* trc, T* thingp, const char* name) {
auto wrapper = reinterpret_cast<DispatchWrapper*>(
uintptr_t(thingp) - offsetof(DispatchWrapper, storage));
wrapper->tracer(trc, &wrapper->storage, name);
void trace(JSTracer* trc, const char* name) override {
JS::GCPolicy<T>::trace(trc, &ptr, name);
}
};
template <typename T>
struct RootedTraceableTraits {
static T* address(RootedTraceable<T>& self) { return &self.ptr; }
static const T* address(const RootedTraceable<T>& self) { return &self.ptr; }
static void trace(JSTracer* trc, VirtualTraceable* thingp, const char* name);
};
template <typename T>
struct RootedGCThingTraits {
static T* address(T& self) { return &self; }
static const T* address(const T& self) { return &self; }
static void trace(JSTracer* trc, T* thingp, const char* name);
};
} /* namespace js */
namespace JS {
@ -1038,18 +1038,16 @@ class JS_PUBLIC_API AutoGCRooter {
namespace detail {
/*
* For pointer types, the TraceKind for tracing is based on the list it is
* in (selected via MapTypeToRootKind), so no additional storage is
* required here. Non-pointer types, however, share the same list, so the
* function to call for tracing is stored adjacent to the struct. Since C++
* cannot templatize on storage class, this is implemented via the wrapper
* class DispatchWrapper.
*/
template <typename T>
using MaybeWrapped =
using RootedPtr =
std::conditional_t<MapTypeToRootKind<T>::kind == JS::RootKind::Traceable,
js::DispatchWrapper<T>, T>;
js::RootedTraceable<T>, T>;
template <typename T>
using RootedPtrTraits =
std::conditional_t<MapTypeToRootKind<T>::kind == JS::RootKind::Traceable,
js::RootedTraceableTraits<T>,
js::RootedGCThingTraits<T>>;
// Dummy types to make it easier to understand template overload preference
// ordering.
@ -1069,6 +1067,9 @@ using OverloadSelector = PreferredOverload;
*/
template <typename T>
class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>> {
using Ptr = detail::RootedPtr<T>;
using PtrTraits = detail::RootedPtrTraits<T>;
inline void registerWithRootLists(RootedListHeads& roots) {
this->stack = &roots[JS::MapTypeToRootKind<T>::kind];
this->prev = *stack;
@ -1143,8 +1144,14 @@ class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>> {
DECLARE_POINTER_CONSTREF_OPS(T);
DECLARE_POINTER_ASSIGN_OPS(Rooted, T);
DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr);
T& get() { return ptr; }
const T& get() const { return ptr; }
T* address() { return PtrTraits::address(ptr); }
const T* address() const { return PtrTraits::address(ptr); }
void trace(JSTracer* trc, const char* name);
private:
/*
@ -1155,7 +1162,7 @@ class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>> {
Rooted<void*>** stack;
Rooted<void*>* prev;
detail::MaybeWrapped<T> ptr;
Ptr ptr;
Rooted(const Rooted&) = delete;
} JS_HAZ_ROOTED;
@ -1326,6 +1333,8 @@ class PersistentRooted
: public js::RootedBase<T, PersistentRooted<T>>,
private mozilla::LinkedListElement<PersistentRooted<T>> {
using ListBase = mozilla::LinkedListElement<PersistentRooted<T>>;
using Ptr = detail::RootedPtr<T>;
using PtrTraits = detail::RootedPtrTraits<T>;
friend class mozilla::LinkedList<PersistentRooted>;
friend class mozilla::LinkedListElement<PersistentRooted>;
@ -1390,7 +1399,7 @@ class PersistentRooted
const_cast<PersistentRooted&>(rhs).setNext(this);
}
bool initialized() { return ListBase::isInList(); }
bool initialized() const { return ListBase::isInList(); }
void init(RootingContext* cx) { init(cx, SafelyInitialized<T>()); }
void init(JSContext* cx) { init(RootingContext::get(cx)); }
@ -1415,19 +1424,15 @@ class PersistentRooted
DECLARE_POINTER_CONSTREF_OPS(T);
DECLARE_POINTER_ASSIGN_OPS(PersistentRooted, T);
DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
// These are the same as DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS, except
// they check that |this| is initialized in case the caller later stores
// something in |ptr|.
T& get() { return ptr; }
const T& get() const { return ptr; }
T* address() {
MOZ_ASSERT(initialized());
return &ptr;
}
T& get() {
MOZ_ASSERT(initialized());
return ptr;
return PtrTraits::address(ptr);
}
const T* address() const { return PtrTraits::address(ptr); }
template <typename U>
void set(U&& value) {
@ -1435,8 +1440,10 @@ class PersistentRooted
ptr = std::forward<U>(value);
}
void trace(JSTracer* trc, const char* name);
private:
detail::MaybeWrapped<T> ptr;
Ptr ptr;
} JS_HAZ_ROOTED;
namespace detail {

View File

@ -214,7 +214,7 @@ const WHITELIST_TYPES: &'static [&'static str] = &[
"jsid",
"JS::Compartment",
"JS::Latin1Char",
"JS::detail::MaybeWrapped",
"JS::detail::RootedPtr",
"JS::MutableHandle",
"JS::MutableHandleObject",
"JS::MutableHandleValue",

View File

@ -31,9 +31,9 @@ typedef uint32_t HashNumber;
// Replacements for types that are too difficult for rust-bindgen.
/// <div rustbindgen replaces="JS::detail::MaybeWrapped" />
/// <div rustbindgen replaces="JS::detail::RootedPtr" />
template <typename T>
using replaces_MaybeWrapped = T;
using replaces_RootedPtr = T;
/// <div rustbindgen replaces="JS::MutableHandleIdVector" />
struct MutableHandleIdVector_Simple {

View File

@ -31,47 +31,55 @@
using namespace js;
using namespace js::gc;
using mozilla::LinkedList;
using JS::AutoGCRooter;
using RootRange = RootedValueMap::Range;
using RootEntry = RootedValueMap::Entry;
using RootEnum = RootedValueMap::Enum;
template <typename T>
using TraceFunction = void (*)(JSTracer* trc, T* ref, const char* name);
// For more detail see JS::Rooted::ptr and js::DispatchWrapper.
// For more detail see JS::Rooted::root and js::RootedTraceable.
//
// The JS::RootKind::Traceable list contains a bunch of totally disparate
// types, but the instantiations of DispatchWrapper below need /something/ in
// the type field. We use the following type as a compatible stand-in. No
// actual methods from ConcreteTraceable type are actually used at runtime --
// the real trace function has been stored inline in the DispatchWrapper.
// The JS::RootKind::Traceable list contains a bunch of totally disparate types,
// but to refer to this list we need /something/ in the type field. We use the
// following type as a compatible stand-in. No actual methods from
// ConcreteTraceable type are actually used at runtime.
struct ConcreteTraceable {
ConcreteTraceable() { MOZ_CRASH("instantiation of ConcreteTraceable"); }
void trace(JSTracer*) {}
ConcreteTraceable() = delete;
void trace(JSTracer*) = delete;
};
template <typename T>
static inline void TraceStackOrPersistentRoot(JSTracer* trc, T* thingp,
const char* name) {
inline void RootedGCThingTraits<T>::trace(JSTracer* trc, T* thingp,
const char* name) {
TraceNullableRoot(trc, thingp, name);
}
template <>
inline void TraceStackOrPersistentRoot(JSTracer* trc, ConcreteTraceable* thingp,
const char* name) {
js::DispatchWrapper<ConcreteTraceable>::TraceWrapped(trc, thingp, name);
template <typename T>
inline void RootedTraceableTraits<T>::trace(JSTracer* trc,
VirtualTraceable* thingp,
const char* name) {
thingp->trace(trc, name);
}
template <typename T>
inline void JS::Rooted<T>::trace(JSTracer* trc, const char* name) {
PtrTraits::trace(trc, &ptr, name);
}
template <typename T>
inline void JS::PersistentRooted<T>::trace(JSTracer* trc, const char* name) {
PtrTraits::trace(trc, &ptr, name);
}
template <typename T>
static inline void TraceExactStackRootList(JSTracer* trc,
JS::Rooted<void*>* rooter,
JS::Rooted<void*>* listHead,
const char* name) {
while (rooter) {
T* addr = reinterpret_cast<JS::Rooted<T>*>(rooter)->address();
TraceStackOrPersistentRoot(trc, addr, name);
rooter = rooter->previous();
auto typedList = reinterpret_cast<JS::Rooted<T>*>(listHead);
for (JS::Rooted<T>* root = typedList; root; root = root->previous()) {
root->trace(trc, name);
}
}
@ -86,7 +94,7 @@ static inline void TraceStackRoots(JSTracer* trc,
TraceExactStackRootList<Value>(trc, stackRoots[JS::RootKind::Value],
"exact-value");
// ConcreteTraceable calls through a function pointer.
// RootedTraceable uses virtual dispatch.
JS::AutoSuppressGCAnalysis nogc;
TraceExactStackRootList<ConcreteTraceable>(
@ -103,11 +111,11 @@ static void TraceExactStackRoots(JSContext* cx, JSTracer* trc) {
template <typename T>
static inline void TracePersistentRootedList(
JSTracer* trc, mozilla::LinkedList<PersistentRooted<void*>>& list,
JSTracer* trc, LinkedList<PersistentRooted<void*>>& list,
const char* name) {
for (PersistentRooted<void*>* r : list) {
TraceStackOrPersistentRoot(
trc, reinterpret_cast<PersistentRooted<T>*>(r)->address(), name);
auto& typedList = reinterpret_cast<LinkedList<PersistentRooted<T>>&>(list);
for (PersistentRooted<T>* root : typedList) {
root->trace(trc, name);
}
}
@ -122,7 +130,7 @@ void JSRuntime::tracePersistentRoots(JSTracer* trc) {
TracePersistentRootedList<Value>(trc, heapRoots.ref()[JS::RootKind::Value],
"persistent-value");
// ConcreteTraceable calls through a function pointer.
// RootedTraceable uses virtual dispatch.
JS::AutoSuppressGCAnalysis nogc;
TracePersistentRootedList<ConcreteTraceable>(
@ -135,9 +143,8 @@ static void TracePersistentRooted(JSRuntime* rt, JSTracer* trc) {
template <typename T>
static void FinishPersistentRootedChain(
mozilla::LinkedList<PersistentRooted<void*>>& listArg) {
auto& list =
reinterpret_cast<mozilla::LinkedList<PersistentRooted<T>>&>(listArg);
LinkedList<PersistentRooted<void*>>& listArg) {
auto& list = reinterpret_cast<LinkedList<PersistentRooted<T>>&>(listArg);
while (!list.isEmpty()) {
list.getFirst()->reset();
}