📝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 {
        DOM_PROXY_OBJECT_SLOT as u32


Want to receive my 🖋 posts as I publish them?