Struct wasmtime::Rooted

source ·
pub struct Rooted<T: GcRef> { /* private fields */ }
Available on crate feature runtime only.
Expand description

A scoped, rooted reference to a garbage-collected T.

A Rooted<T> is a strong handle to a garbage-collected T, preventing its referent (and anything else transitively referenced) from being collected by the GC during the scope within which this Rooted<T> was created.

When the context exits this Rooted<T>’s scope, the underlying GC object is automatically unrooted and any further attempts to use access the underlying object will return errors or otherwise fail.

Rooted<T> dereferences to its underlying T, allowing you to call T’s methods.

§Example

let mut store = Store::<()>::default();

// Allocating a GC object returns a `Rooted<T>`.
let hello: Rooted<ExternRef> = ExternRef::new(&mut store, "hello")?;

// Because `Rooted<T>` derefs to `T`, we can call `T` methods on a
// `Rooted<T>`. For example, we can call the `ExternRef::data` method when we
// have a `Rooted<ExternRef>`.
let data = hello
    .data(&store)?
    .downcast_ref::<&str>()
    .ok_or_else(|| Error::msg("not a str"))?;
assert_eq!(*data, "hello");

// A `Rooted<T>` roots its underlying GC object for the duration of the
// scope of the store/caller/context that was passed to the method that created
// it. If we only want to keep a GC reference rooted and alive temporarily, we
// can introduce new scopes with `RootScope`.
{
    let mut scope = RootScope::new(&mut store);

    // This `Rooted<T>` is automatically unrooted after `scope` is dropped,
    // allowing the collector to reclaim its GC object in the next GC.
    let scoped_ref = ExternRef::new(&mut scope, "goodbye");
}

let module = Module::new(store.engine(), r#"
    (module
        (global (export "global") (mut externref) (ref.null extern))
        (table (export "table") 10 externref)
        (func (export "func") (param externref) (result externref)
            local.get 0
        )
    )
"#)?;
let instance = Instance::new(&mut store, &module, &[])?;

// GC references returned from calls into Wasm also return (optional, if the
// Wasm type is nullable) `Rooted<T>`s.
let result: Option<Rooted<_>> = instance
    .get_typed_func::<Option<Rooted<ExternRef>>, Option<Rooted<ExternRef>>>(&mut store, "func")?
    .call(&mut store, Some(hello))?;

// Similarly, getting a GC reference from a Wasm instance's exported global
// or table yields a `Rooted<T>`.

let global = instance
    .get_global(&mut store, "global")
    .ok_or_else(|| Error::msg("missing `global` export"))?;
let global_val = global.get(&mut store);
let global_ref: Option<&Rooted<_>> = global_val
    .externref()
    .ok_or_else(|| Error::msg("not an externref"))?;

let table = instance.get_table(&mut store, "table").unwrap();
let table_elem = table
    .get(&mut store, 3)
    .ok_or_else(|| Error::msg("table out of bounds"))?;
let table_elem_ref: Option<&Rooted<_>> = table_elem
    .as_extern()
    .ok_or_else(|| Error::msg("not an externref"))?;

§Differences Between Rooted<T> and ManuallyRooted<T>

While Rooted<T> is automatically unrooted when its scope is exited, this means that Rooted<T> is only valid for strictly last-in-first-out (LIFO, aka stack order) lifetimes. This is in contrast to ManuallyRooted<T>, which supports rooting GC objects for arbitrary lifetimes, but requires manual unrooting.

TypeSupported LifetimesUnrooting
Rooted<T>Strictly LIFO / stack orderAutomatic
ManuallyRooted<T>ArbitraryManual

Rooted<T> should suffice for most use cases, and provides better ergonomics, but ManuallyRooted<T> exists as a fully-general escape hatch.

§Scopes

Wasmtime automatically creates two kinds of scopes:

  1. A Store is the outermost rooting scope. Creating a Root<T> directly inside a Store permanently roots the underlying object, similar to dropping a ManuallyRooted<T> without unrooting it.

  2. A Caller provides a rooting scope for the duration of a call from Wasm into a host function. Any objects rooted in a Caller will be unrooted after the host function returns. Note that there can be nested Caller scopes in the case where Wasm calls a host function, creating the first Caller and its rooting scope , and then the host function calls a Wasm function which then calls another host function, creating a second Caller and a second rooting scope. This nesting can be arbitrarily deep.

Additionally, if you would like to define finer-grained rooting scopes, Wasmtime provides the RootScope type.

Scopes are always nested in a last-in-first-out (LIFO) order. An outer scope is never exited (and the Rooted<T>s defined within it are never automatically unrooted) while an inner scope is still active. All inner scopes are exited before their outer scopes.

The following diagram illustrates various rooting scopes over time, how they nest, and when their Rooted<T>s are automatically unrooted:

----- new Store
  |
  |
  | let a: Rooted<T> = ...;
  |
  |
  | ----- call into Wasm
  |   |
  |   |
  |   | ----- Wasm calls host function F
  |   |   |
  |   |   |
  |   |   | let b: Rooted<T> = ...;
  |   |   |
  |   |   |
  |   |   | ----- F calls into Wasm
  |   |   |   |
  |   |   |   |
  |   |   |   | ----- Wasm call host function G
  |   |   |   |   |
  |   |   |   |   |
  |   |   |   |   | let c: Rooted<T> = ...;
  |   |   |   |   |
  |   |   |   |   |
  |   |   |   | ----- return to Wasm from host function G (unroots `c`)
  |   |   |   |
  |   |   |   |
  |   |   | ----- Wasm returns to F
  |   |   |
  |   |   |
  |   | ----- return from host function F (unroots `b`)
  |   |
  |   |
  | ----- return from Wasm
  |
  |
  | ----- let scope1 = RootScope::new(...);
  |   |
  |   |
  |   | let d: Rooted<T> = ...;
  |   |
  |   |
  |   | ----- let scope2 = RootScope::new(...);
  |   |   |
  |   |   |
  |   |   | let e: Rooted<T> = ...;
  |   |   |
  |   |   |
  |   | ----- drop `scope2` (unroots `e`)
  |   |
  |   |
  | ----- drop `scope1` (unroots `d`)
  |
  |
----- drop Store (unroots `a`)

A Rooted<T> can be used successfully as long as it is still rooted so, in the above diagram, d is valid inside scope2 because scope2 is wholly contained within the scope d was rooted within (scope1).

See also the documentation for RootScope.

Implementations§

source§

impl<T: GcRef> Rooted<T>

source

pub fn to_manually_rooted( &self, store: impl AsContextMut ) -> Result<ManuallyRooted<T>>

Create a ManuallyRooted<T> holding onto the same GC object as self.

Returns None if self is used outside of its scope and has therefore been unrooted.

This does not unroot self, and self remains valid until its associated scope is exited.

§Panics

Panics if this object is not associate with the given store.

§Example
let mut store = Store::<()>::default();

let y: ManuallyRooted<_> = {
    // Create a nested rooting scope.
    let mut scope = RootScope::new(&mut store);

    // `x` is only rooted within this nested scope.
    let x: Rooted<_> = ExternRef::new(&mut scope, "hello!")?;

    // Extend `x`'s rooting past its scope's lifetime by converting it
    // to a `ManuallyRooted`.
    x.to_manually_rooted(&mut scope)?
};

// Now we can still access the reference outside the scope it was
// originally defined within.
let data = y.data(&store)?;
let data = data.downcast_ref::<&str>().unwrap();
assert_eq!(*data, "hello!");

// But we have to manually unroot `y`.
y.unroot(&mut store);
source

pub fn rooted_eq(a: Self, b: Self) -> bool

Are these two Rooted<T>s the same GC root?

Note that this function can return false even when a and b are rooting the same underlying GC object, but the object was rooted multiple times (for example in different scopes). Use Rooted::ref_eq to test whether these are references to the same underlying GC object or not.

§Example
let mut store = Store::<()>::default();

let a = ExternRef::new(&mut store, "hello")?;
let b = a;

// `a` and `b` are the same GC root.
assert!(Rooted::rooted_eq(a, b));

{
    let mut scope = RootScope::new(&mut store);

    // `c` is a different GC root, in a different scope, even though it
    // is rooting the same object.
    let c = a.to_manually_rooted(&mut scope)?.into_rooted(&mut scope);
    assert!(!Rooted::rooted_eq(a, c));
}

let x = ExternRef::new(&mut store, "goodbye")?;

// `a` and `x` are different GC roots, rooting different objects.
assert!(!Rooted::rooted_eq(a, x));
source

pub fn ref_eq( store: impl AsContext, a: &impl RootedGcRef<T>, b: &impl RootedGcRef<T> ) -> Result<bool>

Are these two GC roots referencing the same underlying GC object?

This function will return true even when a and b are different GC roots (for example because they were rooted in different scopes) if they are rooting the same underlying GC object. To only test whether they are the same GC root, and not whether they are rooting the same GC object, use Rooted::rooted_eq.

Returns an error if either a or b has been unrooted, for example because the scope it was rooted within has been exited.

Because this method takes any impl RootedGcRef<T> arguments, it can be used to compare, for example, a Rooted<T> and a ManuallyRooted<T>.

§Panics

Panics if either a or b is not associated with the given store.

§Example
let mut store = Store::<()>::default();

let a = ExternRef::new(&mut store, "hello")?;
let b = a;

// `a` and `b` are rooting the same object.
assert!(Rooted::ref_eq(&store, &a, &b)?);

{
    let mut scope = RootScope::new(&mut store);

    // `c` is a different GC root, in a different scope, but still
    // rooting the same object.
    let c = a.to_manually_rooted(&mut scope)?.into_rooted(&mut scope);
    assert!(!Rooted::ref_eq(&scope, &a, &c)?);
}

let x = ExternRef::new(&mut store, "goodbye")?;

// `a` and `x` are rooting different objects.
assert!(!Rooted::ref_eq(&store, &a, &x)?);

// You can also compare `Rooted<T>`s and `ManuallyRooted<T>`s with this
// function.
let d = a.to_manually_rooted(&mut store)?;
assert!(Rooted::ref_eq(&store, &a, &d)?);

d.unroot(&mut store);
source

pub fn rooted_hash<H>(&self, state: &mut H)
where H: Hasher,

Hash this root.

Note that, similar to Rooted::rooted_eq, this only operates on the root and not the underlying GC reference. That means that two different rootings of the same object will hash to different values (modulo hash collisions). If this is undesirable, use the ref_hash method instead.

source

pub fn ref_hash<H>(&self, store: impl AsContext, state: &mut H) -> Result<()>
where H: Hasher,

Hash the underlying rooted object reference.

Note that, similar to Rooted::ref_eq, and operates on the underlying rooted GC object reference, not the root. That means that two different rootings of the same object will hash to the same value. If this is undesirable, use the rooted_hash method instead.

Trait Implementations§

source§

impl<T: GcRef> Clone for Rooted<T>

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<T: GcRef> Debug for Rooted<T>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<T: GcRef> Deref for Rooted<T>

§

type Target = T

The resulting type after dereferencing.
source§

fn deref(&self) -> &Self::Target

Dereferences the value.
source§

impl From<Rooted<AnyRef>> for Ref

source§

fn from(e: Rooted<AnyRef>) -> Ref

Converts to this type from the input type.
source§

impl From<Rooted<AnyRef>> for Val

source§

fn from(val: Rooted<AnyRef>) -> Val

Converts to this type from the input type.
source§

impl From<Rooted<ExternRef>> for Ref

source§

fn from(e: Rooted<ExternRef>) -> Ref

Converts to this type from the input type.
source§

impl From<Rooted<ExternRef>> for Val

source§

fn from(val: Rooted<ExternRef>) -> Val

Converts to this type from the input type.
source§

impl<T: GcRef> Copy for Rooted<T>

source§

impl WasmTy for Rooted<AnyRef>

source§

impl WasmTy for Rooted<ExternRef>

Auto Trait Implementations§

§

impl<T> Freeze for Rooted<T>

§

impl<T> RefUnwindSafe for Rooted<T>
where T: RefUnwindSafe,

§

impl<T> Send for Rooted<T>
where T: Send,

§

impl<T> Sync for Rooted<T>
where T: Sync,

§

impl<T> Unpin for Rooted<T>
where T: Unpin,

§

impl<T> UnwindSafe for Rooted<T>
where T: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> WasmParams for T
where T: WasmTy,

§

type Abi = <(T,) as WasmParams>::Abi

source§

fn typecheck( engine: &Engine, params: impl ExactSizeIterator<Item = ValType>, position: TypeCheckPosition ) -> Result<(), Error>

source§

fn non_i31_gc_refs_count(&self) -> usize

source§

fn into_abi( self, store: &mut AutoAssertNoGc<'_>, func_ty: &FuncType ) -> Result<<T as WasmParams>::Abi, Error>

source§

unsafe fn invoke<R>( func: NonNull<VMNativeCallFunction>, vmctx1: *mut VMOpaqueContext, vmctx2: *mut VMContext, abi: <T as WasmParams>::Abi ) -> <R as WasmResults>::ResultAbi
where R: WasmResults,

source§

impl<T> WasmResults for T
where T: WasmTy, (<T as WasmTy>::Abi,): HostAbi,

§

type ResultAbi = <(T,) as WasmResults>::ResultAbi

source§

unsafe fn from_abi( store: &mut AutoAssertNoGc<'_>, abi: <T as WasmResults>::ResultAbi ) -> T

source§

impl<T> WasmRet for T
where T: WasmTy,

§

type Abi = <T as WasmTy>::Abi

§

type Retptr = ()

§

type Fallible = Result<T, Error>

source§

fn compatible_with_store(&self, store: &StoreOpaque) -> bool

source§

unsafe fn into_abi_for_ret( self, store: &mut AutoAssertNoGc<'_>, _retptr: () ) -> Result<<T as WasmRet>::Abi, Error>

source§

fn func_type(engine: &Engine, params: impl Iterator<Item = ValType>) -> FuncType

source§

unsafe fn wrap_trampoline( ptr: *mut ValRaw, f: impl FnOnce(<T as WasmRet>::Retptr) -> <T as WasmRet>::Abi )

source§

fn into_fallible(self) -> Result<T, Error>

source§

fn fallible_from_error(error: Error) -> Result<T, Error>

source§

impl<T, U> RootedGcRef<T> for U
where T: GcRef, U: RootedGcRefImpl<T> + Deref<Target = T>,