On 6/1/15 2:33 PM, Second Datke wrote:
> The GC Rooting Guide said that at the end:
>
>> Use JS::PersistentRooted<T> for things that are alive until the process exits.
Ah. So in the context of Gecko, this is good "tl;dr" advice, since
pretty much any object can be referenced from JS, so you pretty much
have to assume that if you're using PersistentRooted that will keep the
target alive forever due to cycles through the JS heap keeping the
PersistentRooted alive.
If your situation is different from what I describe above, then you can
use PersistentRooted for things that don't have process lifetime, and in
fact Gecko does just that in cases when the owner of the
PersistentRooted _cannot_ be kept alive from JS.
> As you said, PersistentRooted<T> ensures objects live as long
> as the PersistentRooted<T> alive. But what if the PersistentRooted<T> is deleted?
Then the object can go away after that point, assuming nothing else is
keeping it alive.
> Would it be treated as a common GC thing as other JS GC things?
It's always treated as a common GC thing. It's just that having a
PersistentRooted pointing to it ensures that GC treats it as live.
> In fact, the "Node" in my problem is actually C++ objects, which is wrapped and exposed
> to JS with JS class. The data field acts very similar to a field of JS objects.
..
> What object is stored in the data is just free for scripts. If possible, I want it able to be undefined, numbers, strings, booleans, objects etc. Although the very most of time they
> are objects, I just want to do it gracefully and more freedom for scripting..
OK, then you fundamentally have two options.
1) Store the data in a reserved slot on your JS object. Have C++
access get it from that reserved slot (which means your C++ object needs
a way to get to the JS wrapper, and that the JS wrapper is somehow
already guaranteed to have the same lifetime as the C++ object). Don't
store the JS value in a C++ struct directly. The JS engine will handle
the GC interactions here. This option is probably better if you're
guaranteed your JS object outlives your Node C++ thing.
2) Store the data in a Heap<Value> in your C++ object. Implement a
trace JSClass hook (this is the JSTraceOp member of a JSClass, named
"trace"). This member will look something like:
void MyTraceHook(JSTracer* trc, JSObject* myObj) {
Node* myNode = GetNodePointerFromObject(myObj);
JS_CallValueTracer(trc, &myNode->myHeap,
"whatever informational string");
}
this will work as long as your wrapper JS object is alive. If it stops
being alive before the Node is destroyed.... well, what do you want to
happen with the data in that case?
That applies to cases when you want to trace things as roots. That's
not what you want here; if you did you'd just use a PersistentRooted and
be done with it.
> Besides, I also looked through the definition of JS::PersistentRooted<T>. It seems that
> it just added itself to a global list of JSRuntime. How could it hold up, if there are many
> PersistentRooted inside the list?
Hold up in what sense? That's just the list of GC roots. That list
gets walked during a GC... but so does the whole JS heap anyway.
> What's more, if the data holds a backreference to node, using JS::PersistentRooted<T> cause a leak.
Yes, indeed.
> As it's said in the comment in JSRootingAPI.h, that's because the C++ object is
> not collected. But in my case, the lifetime of Nodes is managed in C++
This part I'm no clear on. You have a JS wrapper for a Node, yes? Can
that wrapper outlive the Node it wraps? If not, why not? If it can,
what happens if the JS wrapper is accessed after the Node has been deleted?
> It seems JS::PersistentRooted<T> is not recommended.
Inside Gecko, correct, for the reasons I described above.
> But what's the difference between
> a JS::PersistentRooted<T> and a traced JS::Heap<T>?
The former stays alive a long as the PersistentRooted object is alive,
period.
The latter is alive only as long as it's reachable from some GC root.
"Tracing" the Heap<T> is just telling the GC about an edge from some
other GC thing (in my example above the Node wrapper JS object) to the
thing stored in the Heap<T>.
-Boris