Bug 1181799 - Allow use of Rooted with arbitrary, fully static structs; r=sfink

This commit is contained in:
Terrence Cole 2015-07-09 08:45:40 -07:00
parent dcdae36446
commit d7fa32fc54
4 changed files with 133 additions and 17 deletions

View File

@ -104,7 +104,9 @@
namespace js { namespace js {
template <typename T> template <typename T>
struct GCMethods {}; struct GCMethods {
static T initial() { return T(); }
};
template <typename T> template <typename T>
class RootedBase {}; class RootedBase {};
@ -627,6 +629,54 @@ struct GCMethods<JSFunction*>
namespace JS { namespace JS {
// To use a static class or struct as part of a Rooted/Handle/MutableHandle,
// ensure that it derives from StaticTraceable (i.e. so that automatic upcast
// via calls works) and ensure that a method |static void trace(T*, JSTracer*)|
// exists on the class.
class StaticTraceable
{
public:
static js::ThingRootKind rootKind() { return js::THING_ROOT_STATIC_TRACEABLE; }
};
} /* namespace JS */
namespace js {
template <typename T>
class DispatchWrapper
{
static_assert(mozilla::IsBaseOf<JS::StaticTraceable, T>::value,
"DispatchWrapper is intended only for usage with a StaticTraceable");
using TraceFn = void (*)(T*, JSTracer*);
TraceFn tracer;
#if JS_BITS_PER_WORD == 32
uint32_t padding; // Ensure the storage fields have CellSize alignment.
#endif
T storage;
public:
// Mimic a pointer type, so that we can drop into Rooted.
MOZ_IMPLICIT DispatchWrapper(const T& initial) : tracer(&T::trace), storage(initial) {}
T* operator &() { return &storage; }
const T* operator &() const { return &storage; }
operator T&() { return storage; }
operator const T&() const { return storage; }
// 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, JS::StaticTraceable* thingp, const char* name) {
auto wrapper = reinterpret_cast<DispatchWrapper*>(
uintptr_t(thingp) - offsetof(DispatchWrapper, storage));
wrapper->tracer(&wrapper->storage, trc);
}
};
} /* namespace js */
namespace JS {
/* /*
* Local variable of type T whose value is always rooted. This is typically * Local variable of type T whose value is always rooted. This is typically
* used for local variables, or for non-rooted values being passed to a * used for local variables, or for non-rooted values being passed to a
@ -743,10 +793,18 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
Rooted<void*>* prev; Rooted<void*>* prev;
/* /*
* |ptr| must be the last field in Rooted because the analysis treats all * For pointer types, the TraceKind for tracing is based on the list it is
* Rooted as Rooted<void*> during the analysis. See bug 829372. * in (selected via rootKind), so no additional storage is required here.
* All StaticTraceable, 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.
*/ */
T ptr; using MaybeWrapped = typename mozilla::Conditional<
mozilla::IsBaseOf<StaticTraceable, T>::value,
js::DispatchWrapper<T>,
T>::Type;
MaybeWrapped ptr;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER

View File

@ -110,6 +110,9 @@ MarkExactStackRootsAcrossTypes(T context, JSTracer* trc)
MarkExactStackRootList<Bindings, MarkBindingsRoot>(trc, context, "Bindings"); MarkExactStackRootList<Bindings, MarkBindingsRoot>(trc, context, "Bindings");
MarkExactStackRootList<JSPropertyDescriptor, MarkPropertyDescriptorRoot>( MarkExactStackRootList<JSPropertyDescriptor, MarkPropertyDescriptorRoot>(
trc, context, "JSPropertyDescriptor"); trc, context, "JSPropertyDescriptor");
MarkExactStackRootList<JS::StaticTraceable,
js::DispatchWrapper<JS::StaticTraceable>::TraceWrapped>(
trc, context, "StaticTraceable");
} }
static void static void

View File

@ -35,3 +35,57 @@ BEGIN_TEST(testGCSuppressions)
return true; return true;
} }
END_TEST(testGCSuppressions) END_TEST(testGCSuppressions)
struct MyContainer : public JS::StaticTraceable
{
RelocatablePtrObject obj;
RelocatablePtrString str;
MyContainer() : obj(nullptr), str(nullptr) {}
static void trace(MyContainer* self, JSTracer* trc) {
if (self->obj)
js::TraceEdge(trc, &self->obj, "test container");
if (self->str)
js::TraceEdge(trc, &self->str, "test container");
}
};
namespace js {
template <>
struct RootedBase<MyContainer> {
RelocatablePtrObject& obj() { return static_cast<Rooted<MyContainer>*>(this)->get().obj; }
RelocatablePtrString& str() { return static_cast<Rooted<MyContainer>*>(this)->get().str; }
};
} // namespace js
BEGIN_TEST(testGCGenericRootedInternalStackStorage)
{
JS::Rooted<MyContainer> container(cx);
container.get().obj = JS_NewObject(cx, nullptr);
container.get().str = JS_NewStringCopyZ(cx, "Hello");
JS_GC(cx->runtime());
JS_GC(cx->runtime());
JS::RootedObject obj(cx, container.get().obj);
JS::RootedValue val(cx, StringValue(container.get().str));
CHECK(JS_SetProperty(cx, obj, "foo", val));
return true;
}
END_TEST(testGCGenericRootedInternalStackStorage)
BEGIN_TEST(testGCGenericRootedInternalStackStorageAugmented)
{
JS::Rooted<MyContainer> container(cx);
container.obj() = JS_NewObject(cx, nullptr);
container.str() = JS_NewStringCopyZ(cx, "Hello");
JS_GC(cx->runtime());
JS_GC(cx->runtime());
JS::RootedObject obj(cx, container.obj());
JS::RootedValue val(cx, StringValue(container.str()));
CHECK(JS_SetProperty(cx, obj, "foo", val));
return true;
}
END_TEST(testGCGenericRootedInternalStackStorageAugmented)

View File

@ -299,6 +299,19 @@ namespace js {
class ExclusiveContext; class ExclusiveContext;
/*
* This list enumerates the different types of conceptual stacks we have in
* SpiderMonkey. In reality, they all share the C stack, but we allow different
* stack limits depending on the type of code running.
*/
enum StackKind
{
StackForSystemCode, // C++, such as the GC, running on behalf of the VM.
StackForTrustedScript, // Script running with trusted principals.
StackForUntrustedScript, // Script running with untrusted principals.
StackKindCount
};
enum ThingRootKind enum ThingRootKind
{ {
THING_ROOT_OBJECT, THING_ROOT_OBJECT,
@ -316,22 +329,10 @@ enum ThingRootKind
THING_ROOT_BINDINGS, THING_ROOT_BINDINGS,
THING_ROOT_PROPERTY_DESCRIPTOR, THING_ROOT_PROPERTY_DESCRIPTOR,
THING_ROOT_PROP_DESC, THING_ROOT_PROP_DESC,
THING_ROOT_STATIC_TRACEABLE,
THING_ROOT_LIMIT THING_ROOT_LIMIT
}; };
/*
* This list enumerates the different types of conceptual stacks we have in
* SpiderMonkey. In reality, they all share the C stack, but we allow different
* stack limits depending on the type of code running.
*/
enum StackKind
{
StackForSystemCode, // C++, such as the GC, running on behalf of the VM.
StackForTrustedScript, // Script running with trusted principals.
StackForUntrustedScript, // Script running with untrusted principals.
StackKindCount
};
template <typename T> template <typename T>
struct RootKind; struct RootKind;