How to create, handle, and destroy JS::Heap<T> objects in Spidermonkey?

Using Spidermonkey 24, 38, 45

Spidermonkey documentation says: "GC thing pointers on the heap must be wrapped in a JS::Heap. The only exception to this is if they are added as roots with the JS_AddRoot() functions or JS::PersistentRooted class, but don't do this unless it's really necessary. JS::Heap pointers must also continue to be traced in the normal way, which is not covered here."

What exactly does tracing mean in this case? Is the following code missing something?

struct Foo
{
   Foo(JS::HandleObject bar) : _bar(bar) {}  // Does that root bar?
   ~Foo() {_bar = nullptr;}                  // Does that release memory?

   JS::Heap<JSObject*>  _bar;
};

Foo *create(JSContext *jscontext, JSObject *parent)
{
   JS::RootedObject bar(jscontext, JS_NewObject(jscontext, &SOME_CLASS, NULL, parent));
   return new Foo(bar);
}

Do I need to add "tracing"? Do I need to root bar object when it is stored inside Foo? Should I rather use JS_AddRoot() functions to root bar instead of using Heap?

Answers:

Answer

JS::Heap<T> are used in data structures which reside on the heap. Tracing is indeed a requirement. Without it, the GC may either determine your object is unreachable or relocate it without updating your JS::Heap<T> handle.

Tracing objects in spidermonkey is simple. You need to request an extra GC root tracer using JS_AddExtraGCRootsTracer(JSRuntime* rt, JSTraceDataOp traceOp, void* data). The JSTraceDataOp is a function pointer with the interface void traceOp(JSTracer* trc, void* data). In your function, you must call the various JS_Call<T>Tracer (defined in Tracer.h) methods passing the tracer and your object. Additionally, you should call JS_TraceChildren as necessary.

Using your example, adding a tracer might look something like the following.

Foo* ref = create(cx, nullptr);

void trace_foos(JSTracer* tracer, void* data) {
   JS_CallHeapObjectTracer(tracer, &(ref->_bar), "foo");
}

JS_AddExtraGCRootsTracer(rt, trace_foos, nullptr);

Whenever GC runs, your trace callback will run and you should traverse your object graph and trace GC things as you go.

As for the questions in your comments -

   Foo(JS::HandleObject bar) : _bar(bar) {}  // Does that root bar?

bar is already rooted on the stack by the JS::RootedObject bar in Foo* create. Heap<T> handles are not roots - this is why they must be traced. As soon as create returns, the object is no longer rooted.

  ~Foo() {_bar = nullptr;}                  // Does that release memory?

No. _bar is just an instance of the JS::Heap<T> handle. The thing it points to will be garbage collected on a subsequent cycle.

Answer

Based on jwilm's answer here is an example of a Foo class implementation.

void trace_obj(JSTracer* tracer, void* data)
{
    JS_CallObjectTracer(tracer, (JS::Heap<JSObject*>*)data, "jsobj");
}

class Foo                                                                                                                                                                             
{                                                                                                                                                                                     
public:                                                                                                                                                                               
   Foo(JSRuntime *rt, JS::HandleObject bar)                                                                                                                                           
     : _rt(rt),                                                                                                                                                                        
     _bar(bar)                                                                                                                                                                      
   {                                                                                                                                                                                  
     if (_bar.get())                                                                                                                                                                  
       JS_AddExtraGCRootsTracer(_rt, trace_obj, &_bar);                                                                                                                               
   }                                                                                                                                                                                  
   Foo(Foo const &f)                                                                                                                                           
     : _rt(f._rt),                                                                                                                                                                        
     _bar(f._bar)                                                                                                                                                                      
   {                                                                                                                                                                                  
     if (_bar.get())                                                                                                                                                                  
       JS_AddExtraGCRootsTracer(_rt, trace_obj, &_bar);                                                                                                                               
   }                                                                                                                                                                                  
   ~Foo()                                                                                                                                                                             
   {                                                                                                                                                                                  
     if (_bar.get())                                                                                                    
       JS_RemoveExtraGCRootsTracer(_rt, trace_obj, &_bar);                                                                                 
   }                                                                                                                                                                                  

private:                                                                                                                
   JSRuntime *_rt;                                                                                                                                                                    
   JS::Heap<JSObject*> _bar;                                                                                                                                                          
};                                                                                                                                                                                    

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.