SpiderMonkey Value Rooting

matthewd edited this page Sep 12, 2010 · 5 revisions
Clone this wiki locally

SpiderMonkey uses a mark+sweep garbage collector. It operates by searching all the properties of all objects that are in scope, recursively, thus finding everything that still needs to exist. Any object that is no longer referenced by anything is naturally able to be deallocated.

This scheme is fine for objects that are in scope for JavaScript code, or course, but falls short when it comes to native C functions; the JS engine cannot introspect their scope the same way it can for interpreted methods. To give it a helping hand, and close this gap, we have Roots.

Roots allow any C variable to be marked as being “currently reachable”. Whatever JS value is referenced therein will thus be spared the cruel fate of its unrooted brethren. Consequently, whenever we have a C variable that contains a reference to a JS value, we must mark it as a root, before GC has a chance to deallocate it.

It is equally important that we unroot this value before it goes out of scope… if we don’t, at best, the JS engine will maintain values that should no longer exist, ultimately running out of memory. Worse, once our stack frame is out of scope, it may be reused, leaving the garbage collector to follow a pointer into some completely invalid data.

This requirement to unroot everything we’ve rooted is complicated by exceptions, which can take a simple single-path function and complicate it significantly. To alleviate the burden of keeping track of all this, we have our jroot macro family.

Essentially, we have a macro to be called at the start of the function, a macro that roots a given value, and a macro that undoes all the roots that have been set. To simplify flow for the average function, a number of more specific macros are available for the end of the function — basically, you don’t want to return, and you don’t want execution to reach the end of your function.

Declares local variables to track what’s been rooted, etc. Which one you call determines how the function will behave. You must also specify the maximum number of concurrent roots your function intends to maintain.
Roots the given variable (use JROOT_PTR to root the referent of a jsval* or a JSObject**). JUNROOT unroots the given variable. As an optimisation, doing so will only return a slot to the pool if it was the last value rooted.
JCHECK Evaluates the given condition, interpreting a false value as indicating a JavaScript exception. It should generally wrap calls to functions that return a JSBool.
JPROTECT Evaluates the given VALUE-returning expression, safely dealing with any Ruby exception triggered therein.
JERROR Throws an exception with the given text as its message.
JRETURN Notes that the value currently set in the result pointer is final, and terminates function execution. It must only be used with PREPARE_JROOTS.
JRETURN_RUBY Returns the given value from this function. It should generally be passed a VALUE. It must only be used with PREPARE_RUBY_JROOTS.