mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1181799 - Allow use of Rooted with arbitrary, fully static structs; r=sfink
This commit is contained in:
parent
dcdae36446
commit
d7fa32fc54
@ -104,7 +104,9 @@
|
||||
namespace js {
|
||||
|
||||
template <typename T>
|
||||
struct GCMethods {};
|
||||
struct GCMethods {
|
||||
static T initial() { return T(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class RootedBase {};
|
||||
@ -627,6 +629,54 @@ struct GCMethods<JSFunction*>
|
||||
|
||||
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
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* |ptr| must be the last field in Rooted because the analysis treats all
|
||||
* Rooted as Rooted<void*> during the analysis. See bug 829372.
|
||||
* For pointer types, the TraceKind for tracing is based on the list it is
|
||||
* 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
|
||||
|
||||
|
@ -110,6 +110,9 @@ MarkExactStackRootsAcrossTypes(T context, JSTracer* trc)
|
||||
MarkExactStackRootList<Bindings, MarkBindingsRoot>(trc, context, "Bindings");
|
||||
MarkExactStackRootList<JSPropertyDescriptor, MarkPropertyDescriptorRoot>(
|
||||
trc, context, "JSPropertyDescriptor");
|
||||
MarkExactStackRootList<JS::StaticTraceable,
|
||||
js::DispatchWrapper<JS::StaticTraceable>::TraceWrapped>(
|
||||
trc, context, "StaticTraceable");
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -35,3 +35,57 @@ BEGIN_TEST(testGCSuppressions)
|
||||
return true;
|
||||
}
|
||||
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)
|
||||
|
@ -299,6 +299,19 @@ namespace js {
|
||||
|
||||
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
|
||||
{
|
||||
THING_ROOT_OBJECT,
|
||||
@ -316,22 +329,10 @@ enum ThingRootKind
|
||||
THING_ROOT_BINDINGS,
|
||||
THING_ROOT_PROPERTY_DESCRIPTOR,
|
||||
THING_ROOT_PROP_DESC,
|
||||
THING_ROOT_STATIC_TRACEABLE,
|
||||
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>
|
||||
struct RootKind;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user