📝Servo’s GC types are only safe because GC is conservative
Servo’s GC types are safe (servo/js.rs at 1c0e51015fc1a5ba0e189f114e35019af27d68ca) but this only works because GC is conservative (does not move object referenced on the stack).
Rust wrappers (Temporary
, Root
, and RootCollection
) store pointer to JSObject (reflector). Storing it in Temporary
and Root
ensures that conservative GC finds the pointer on the stack.
Root
contains a JSRef
that can be used to get the Rust value. JSRef
is lifetime-bound to Root
. However, when Root
is rooted, only the reflector pointer is copied into roots—the address of the pointer (inside Root
and JSRef
) is not stored. Therefore, GC cannot update it (i.e., GC must be non-moving).
However, Conservative GC can relocate objects if their pointer does not occur on stack.
TODO: Why RootCollection
is even needed if GC can find values on the stack? maybe js_ptr
on the stack is not exactly the same pointer as inner
but roots it.
Yes, js_prt
is a reflector—a JSObject that stores a reference to the given (DOM/Rust) value:
/// Get the DOM object from the given reflector.
pub unsafe fn unwrap<T>(obj: *mut JSObject) -> *const T {
let slot = dom_object_slot(obj);
let val = JS_GetReservedSlot(obj, slot);
val.to_private() as *const T
}
/// Returns the index of the slot wherein a pointer to the reflected DOM object
/// is stored.
///
/// Fails if `obj` is not a DOM object.
pub unsafe fn dom_object_slot(obj: *mut JSObject) -> u32 {
let clasp = JS_GetClass(obj);
if is_dom_class(&*clasp) {
DOM_OBJECT_SLOT as u32
} else {
assert!(is_dom_proxy(obj));
DOM_PROXY_OBJECT_SLOT as u32
}
}