Bug 1107639 - Allow two phase construction of PersistentRooted r=terrence

This commit is contained in:
Jon Coppeard 2015-01-23 10:23:56 +00:00
parent 7e4dfb99c1
commit 3451f6624a
3 changed files with 108 additions and 25 deletions

View File

@ -1070,7 +1070,9 @@ MutableHandle<T>::MutableHandle(PersistentRooted<T> *root)
* These roots can be used in heap-allocated data structures, so they are not
* associated with any particular JSContext or stack. They are registered with
* the JSRuntime itself, without locking, so they require a full JSContext to be
* constructed, not one of its more restricted superclasses.
* initialized, not one of its more restricted superclasses. Initialization may
* take place on construction, or in two phases if the no-argument constructor
* is called followed by init().
*
* Note that you must not use an PersistentRooted in an object owned by a JS
* object:
@ -1096,40 +1098,43 @@ MutableHandle<T>::MutableHandle(PersistentRooted<T> *root)
* marked when the object itself is marked.
*/
template<typename T>
class PersistentRooted : private mozilla::LinkedListElement<PersistentRooted<T> > {
class PersistentRooted : private mozilla::LinkedListElement<PersistentRooted<T>> {
typedef mozilla::LinkedListElement<PersistentRooted<T>> Base;
friend class mozilla::LinkedList<PersistentRooted>;
friend class mozilla::LinkedListElement<PersistentRooted>;
friend struct js::gc::PersistentRootedMarker<T>;
friend void js::gc::FinishPersistentRootedChains(JSRuntime *rt);
void registerWithRuntime(JSRuntime *rt) {
MOZ_ASSERT(!initialized());
JS::shadow::Runtime *srt = JS::shadow::Runtime::asShadowRuntime(rt);
srt->getPersistentRootedList<T>().insertBack(this);
}
public:
explicit PersistentRooted(JSContext *cx) : ptr(js::GCMethods<T>::initial())
{
registerWithRuntime(js::GetRuntime(cx));
PersistentRooted() : ptr(js::GCMethods<T>::initial()) {}
explicit PersistentRooted(JSContext *cx) {
init(cx);
}
PersistentRooted(JSContext *cx, T initial) : ptr(initial)
{
registerWithRuntime(js::GetRuntime(cx));
PersistentRooted(JSContext *cx, T initial) {
init(cx, initial);
}
explicit PersistentRooted(JSRuntime *rt) : ptr(js::GCMethods<T>::initial())
{
registerWithRuntime(rt);
explicit PersistentRooted(JSRuntime *rt) {
init(rt);
}
PersistentRooted(JSRuntime *rt, T initial) : ptr(initial)
{
registerWithRuntime(rt);
PersistentRooted(JSRuntime *rt, T initial) {
init(rt, initial);
}
PersistentRooted(const PersistentRooted &rhs)
: mozilla::LinkedListElement<PersistentRooted<T> >(),
: mozilla::LinkedListElement<PersistentRooted<T>>(),
ptr(rhs.ptr)
{
/*
@ -1143,6 +1148,37 @@ class PersistentRooted : private mozilla::LinkedListElement<PersistentRooted<T>
const_cast<PersistentRooted &>(rhs).setNext(this);
}
bool initialized() {
return Base::isInList();
}
void init(JSContext *cx) {
init(cx, js::GCMethods<T>::initial());
}
void init(JSContext *cx, T initial)
{
ptr = initial;
registerWithRuntime(js::GetRuntime(cx));
}
void init(JSRuntime *rt) {
init(rt, js::GCMethods<T>::initial());
}
void init(JSRuntime *rt, T initial)
{
ptr = initial;
registerWithRuntime(rt);
}
void reset() {
if (initialized()) {
set(js::GCMethods<T>::initial());
Base::remove();
}
}
/*
* Important: Return a reference here so passing a Rooted<T> to
* something that takes a |const T&| is not a GC hazard.
@ -1155,17 +1191,17 @@ class PersistentRooted : private mozilla::LinkedListElement<PersistentRooted<T>
const T &get() const { return ptr; }
T &operator=(T value) {
MOZ_ASSERT(!js::GCMethods<T>::poisoned(value));
ptr = value;
set(value);
return ptr;
}
T &operator=(const PersistentRooted &value) {
ptr = value;
T &operator=(const PersistentRooted &other) {
set(other.ptr);
return ptr;
}
void set(T value) {
MOZ_ASSERT(initialized());
MOZ_ASSERT(!js::GCMethods<T>::poisoned(value));
ptr = value;
}

View File

@ -38,8 +38,15 @@ const JSClass BarkWhenTracedClass::class_ = {
struct Kennel {
PersistentRootedObject obj;
Kennel() { }
explicit Kennel(JSContext *cx) : obj(cx) { }
Kennel(JSContext *cx, const HandleObject &woof) : obj(cx, woof) { }
void init(JSContext *cx, const HandleObject &woof) {
obj.init(cx, woof);
}
void clear() {
obj = nullptr;
}
};
// A function for allocating a Kennel and a barker. Only allocating
@ -178,3 +185,35 @@ BEGIN_TEST(test_PersistentRootedAssign)
return true;
}
END_TEST(test_PersistentRootedAssign)
static PersistentRootedObject gGlobalRoot;
// PersistentRooted instances can initialized in a separate step to allow for global PersistentRooteds.
BEGIN_TEST(test_GlobalPersistentRooted)
{
BarkWhenTracedClass::reset();
CHECK(!gGlobalRoot.initialized());
{
RootedObject barker(cx, JS_NewObject(cx, &BarkWhenTracedClass::class_, JS::NullPtr(), JS::NullPtr()));
CHECK(barker);
gGlobalRoot.init(cx, barker);
}
CHECK(gGlobalRoot.initialized());
// GC should be able to find our barker.
CHECK(GCFinalizesNBarkers(cx, 0));
gGlobalRoot.reset();
CHECK(!gGlobalRoot.initialized());
// Now GC should not be able to find the barker.
JS_GC(JS_GetRuntime(cx));
CHECK(BarkWhenTracedClass::finalizeCount == 1);
return true;
}
END_TEST(test_GlobalPersistentRooted)

View File

@ -1325,16 +1325,24 @@ GCRuntime::finish()
FinishTrace();
}
template <typename T>
static void
FinishPersistentRootedChain(mozilla::LinkedList<PersistentRooted<T>>& list)
{
while (!list.isEmpty())
list.getFirst()->reset();
}
void
js::gc::FinishPersistentRootedChains(JSRuntime *rt)
{
/* The lists of persistent roots are stored on the shadow runtime. */
rt->functionPersistentRooteds.clear();
rt->idPersistentRooteds.clear();
rt->objectPersistentRooteds.clear();
rt->scriptPersistentRooteds.clear();
rt->stringPersistentRooteds.clear();
rt->valuePersistentRooteds.clear();
FinishPersistentRootedChain(rt->functionPersistentRooteds);
FinishPersistentRootedChain(rt->idPersistentRooteds);
FinishPersistentRootedChain(rt->objectPersistentRooteds);
FinishPersistentRootedChain(rt->scriptPersistentRooteds);
FinishPersistentRootedChain(rt->stringPersistentRooteds);
FinishPersistentRootedChain(rt->valuePersistentRooteds);
}
void