3.2 KiB
The design of Garbage collected DOM
These are how Servo provides an object graph to SpiderMonkey's Garbage Collection.
Construct
When Servo creates a Rusty DOM object, the binding code creates a wrapper JSObject
with SpiderMonkey, is correspond to each Rusty DOM Object. It’s produced and set to the Rusty object in FooBinding::Wrap
.
In FooBinding::Wrap
, the wrapper JSObject gets the pointer for Rusty Object to itself. And the same time, the wrapper JSObject
are set to the Rusty Object’s Reflector
field (All Rusty DOM objects have dom::bindings::utils::Reflector
in their most basis field). These step are the “binding” work to create the relationship of both objects.
Trace object graph from SpiderMonkey GC.
This is very tricky and magically mechanism helped by Rust Compiler. The outline is:
- SpiderMonkey's GC calls
JSClass.trace
defined inFooBinding
when marking phase. This JSClass is basis of each wrapper JSObject. JSClass.trace
callsFoo::trace()
(an implementation ofJSTraceable
). This is typically derived via a #[jstraceable] annotation- For all fields,
Foo::trace()
callstrace()
on the field. For example, for fields of typeJS<T>
,JS<T>::trace()
callstrace_reflector()
. Non-JS-managed types have an empty inlinetrace()
method, achieved viauntraceable!
or similar. trace_reflector()
fetches the reflector that is reachable from a Rust object, and notifies it to the GC with using JSTracer.- This operation continues to the end of the graph.
- Finally, GC gets whether Rust object lives or not from JSObjects which is hold by Rust object.
Destruct
When destructing DOM objects (wrapper JSObjects) by SpiderMonkey, SpiderMonkey calls the JSClass.finalize()
which is basis of each wrapper JSObject
s. This method refers each FooBinding::_finalize()
.
In this function, the pointer of Rusty DOM Object that is contained in the wrapper JSObject is unwrapped, it cast to Rust owned pointer, and we assign its owned pointer to the empty local variable of FooBinding::_finalize()
. Thus we can destruct the Rusty Object after we left from it.
Interact with Exact GC’s rooting
For supporting SpiderMonkey’s exact GC rooting, we introduce some types:
JS<T>
is used for the DOM typed field in a DOM type structure. GC can trace them recursively while enclosing DOM object (maybe root) is alive.Temporary<T>
is used as a return value of functions returning DOM type. They are rooted while they are alive. But a retun value gets moved around. It’s breakable for the LIFO ordering constraint. Thus we need introduceRoot<T>
.Root<T>
contains the pointer toJSObject
which the represented DOM type has. SpiderMonkey's conservative stack scanner scans its pointer and mark a pointedJSObject
as GC root.JSRef
is just a reference to the value rooted byRoot<T>
.RootCollection
is used for dynamic checking about rooting satisfies LIFO ordering, because SpiderMonkey GC requres LIFO order (See also: Exact Stack Rooting - Storing a GCPointer on the CStack).