wiggle::wasmtime_crate

Struct ExternRef

pub struct ExternRef { /* private fields */ }
Expand description

An opaque, GC-managed reference to some host data that can be passed to WebAssembly.

The ExternRef type represents WebAssembly externref values. Wasm can’t do anything with the externrefs other than put them in tables, globals, and locals or pass them to other functions (such as imported functions from the host). Unlike anyrefs, Wasm guests cannot directly allocate new externrefs; only the host can.

You can use ExternRef to give access to host objects and control the operations that Wasm can perform on them via what functions you allow Wasm to import.

Like all WebAssembly references, these are opaque and unforgeable to Wasm: they cannot be faked and Wasm cannot, for example, cast the integer 0x12345678 into a reference, pretend it is a valid externref, and trick the host into dereferencing it and segfaulting or worse.

Note that you can also use Rooted<ExternRef> and ManuallyRooted<ExternRef> as a type parameter with Func::typed- and Func::wrap-style APIs.

§Example

let engine = Engine::default();
let mut store = Store::new(&engine, ());

// Define some APIs for working with host strings from Wasm via `externref`.
let mut linker = Linker::new(&engine);
linker.func_wrap(
    "host-string",
    "new",
    |caller: Caller<'_, ()>| -> Result<Rooted<ExternRef>> {
        ExternRef::new(caller, Cow::from(""))
    },
)?;
linker.func_wrap(
    "host-string",
    "concat",
    |mut caller: Caller<'_, ()>, a: Rooted<ExternRef>, b: Rooted<ExternRef>| -> Result<Rooted<ExternRef>> {
        let mut s = a
            .data(&caller)?
            .ok_or_else(|| Error::msg("externref has no host data"))?
            .downcast_ref::<Cow<str>>()
            .ok_or_else(|| Error::msg("externref was not a string"))?
            .clone()
            .into_owned();
        let b = b
            .data(&caller)?
            .ok_or_else(|| Error::msg("externref has no host data"))?
            .downcast_ref::<Cow<str>>()
            .ok_or_else(|| Error::msg("externref was not a string"))?;
        s.push_str(&b);
        ExternRef::new(&mut caller, s)
    },
)?;

// Here is a Wasm module that uses those APIs.
let module = Module::new(
    &engine,
    r#"
        (module
            (import "host-string" "concat" (func $concat (param externref externref)
                                                         (result externref)))
            (func (export "run") (param externref externref) (result externref)
                local.get 0
                local.get 1
                call $concat
            )
        )
    "#,
)?;

// Create a couple `externref`s wrapping `Cow<str>`s.
let hello = ExternRef::new(&mut store, Cow::from("Hello, "))?;
let world = ExternRef::new(&mut store, Cow::from("World!"))?;

// Instantiate the module and pass the `externref`s into it.
let instance = linker.instantiate(&mut store, &module)?;
let result = instance
    .get_typed_func::<(Rooted<ExternRef>, Rooted<ExternRef>), Rooted<ExternRef>>(&mut store, "run")?
    .call(&mut store, (hello, world))?;

// The module should have concatenated the strings together!
assert_eq!(
    result
        .data(&store)?
        .expect("externref should have host data")
        .downcast_ref::<Cow<str>>()
        .expect("host data should be a `Cow<str>`"),
    "Hello, World!"
);

Implementations§

§

impl ExternRef

pub fn new<T>( context: impl AsContextMut, value: T, ) -> Result<Rooted<ExternRef>, Error>
where T: 'static + Any + Send + Sync,

Creates a new instance of ExternRef wrapping the given value.

The resulting value is automatically unrooted when the given context’s scope is exited. See Rooted<T>’s documentation for more details.

This method will not automatically trigger a GC to free up space in the GC heap; instead it will return an error. This gives you more precise control over when collections happen and allows you to choose between performing synchronous and asynchronous collections.

§Errors

If the allocation cannot be satisfied because the GC heap is currently out of memory, but performing a garbage collection might free up space such that retrying the allocation afterwards might succeed, then a GcHeapOutOfMemory<T> error is returned.

The GcHeapOutOfMemory<T> error contains the host value that the externref would have wrapped. You can extract that value from this error and reuse it when attempting to allocate an externref again after GC or otherwise do with it whatever you see fit.

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

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

    // Create an `externref` wrapping a `str`.
    let externref = match ExternRef::new(&mut scope, "hello!") {
        Ok(x) => x,
        // If the heap is out of memory, then do a GC and try again.
        Err(e) if e.is::<GcHeapOutOfMemory<&'static str>>() => {
            // Do a GC! Note: in an async context, you'd want to do
            // `scope.as_context_mut().gc_async().await`.
            scope.as_context_mut().gc();

            // Extract the original host value from the error.
            let host_value = e
                .downcast::<GcHeapOutOfMemory<&'static str>>()
                .unwrap()
                .into_inner();

            // Try to allocate the `externref` again, now that the GC
            // has hopefully freed up some space.
            ExternRef::new(&mut scope, host_value)?
        }
        Err(e) => return Err(e),
    };

    // Use the `externref`, pass it to Wasm, etc...
}

// The `externref` is automatically unrooted when we exit the scope.

pub fn convert_any( context: impl AsContextMut, anyref: Rooted<AnyRef>, ) -> Result<Rooted<ExternRef>, Error>

Convert an anyref into an externref.

This is equivalent to the extern.convert_any instruction in Wasm.

You can get the underlying anyref again via the AnyRef::convert_extern method or the any.convert_extern Wasm instruction.

The resulting ExternRef will not have any host data associated with it, so ExternRef::data and ExternRef::data_mut will both return None.

Returns an error if the anyref GC reference has been unrooted (eg if you attempt to use a Rooted<AnyRef> after exiting the scope it was rooted within). See the documentation for Rooted<T> for more details.

§Example
use wasmtime::*;
let engine = Engine::default();
let mut store = Store::new(&engine, ());

// Create an `anyref`.
let i31 = I31::wrapping_u32(0x1234);
let anyref = AnyRef::from_i31(&mut store, i31);

// Convert that `anyref` into an `externref`.
let externref = ExternRef::convert_any(&mut store, anyref)?;

// This `externref` doesn't have any associated host data.
assert!(externref.data(&store)?.is_none());

// We can convert it back to an `anyref` and get its underlying `i31`
// data.
let anyref = AnyRef::convert_extern(&mut store, externref)?;
assert_eq!(anyref.unwrap_i31(&store)?.get_u32(), 0x1234);

pub fn new_manually_rooted<T>( store: impl AsContextMut, value: T, ) -> Result<ManuallyRooted<ExternRef>, Error>
where T: 'static + Any + Send + Sync,

Creates a new, manually-rooted instance of ExternRef wrapping the given value.

The resulting value must be manually unrooted, or else it will leak for the entire duration of the store’s lifetime. See ManuallyRooted<T>’s documentation for more details.

§Errors

This function returns the same errors in the same scenarios as ExternRef::new.

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

// Create a manually-rooted `externref` wrapping a `str`.
let externref = ExternRef::new_manually_rooted(&mut store, "hello!")?;

// Use `externref` a bunch, pass it to Wasm, etc...

// Don't forget to explicitly unroot the `externref` when you're done
// using it!
externref.unroot(&mut store);

pub fn data<'a, T>( &self, store: impl Into<StoreContext<'a, T>>, ) -> Result<Option<&'a (dyn Any + Sync + Send + 'static)>, Error>
where T: 'a,

Get a shared borrow of the underlying data for this ExternRef.

Returns None if this is an externref wrapper of an anyref created by the extern.convert_any instruction or the ExternRef::convert_any method.

Returns an error if this externref GC reference has been unrooted (eg if you attempt to use a Rooted<ExternRef> after exiting the scope it was rooted within). See the documentation for Rooted<T> for more details.

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

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

// Access the `externref`'s host data.
let data = externref.data(&store)?.ok_or_else(|| Error::msg("no host data"))?;
// Dowcast it to a `&str`.
let data = data.downcast_ref::<&str>().ok_or_else(|| Error::msg("not a str"))?;
// We should have got the data we created the `externref` with!
assert_eq!(*data, "hello");

pub fn data_mut<'a, T>( &self, store: impl Into<StoreContextMut<'a, T>>, ) -> Result<Option<&'a mut (dyn Any + Sync + Send + 'static)>, Error>
where T: 'a,

Get an exclusive borrow of the underlying data for this ExternRef.

Returns None if this is an externref wrapper of an anyref created by the extern.convert_any instruction or the ExternRef::convert_any constructor.

Returns an error if this externref GC reference has been unrooted (eg if you attempt to use a Rooted<ExternRef> after exiting the scope it was rooted within). See the documentation for Rooted<T> for more details.

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

let externref = ExternRef::new::<usize>(&mut store, 0)?;

// Access the `externref`'s host data.
let data = externref.data_mut(&mut store)?.ok_or_else(|| Error::msg("no host data"))?;
// Dowcast it to a `usize`.
let data = data.downcast_mut::<usize>().ok_or_else(|| Error::msg("not a usize"))?;
// We initialized to zero.
assert_eq!(*data, 0);
// And we can mutate the value!
*data += 10;

pub unsafe fn from_raw( store: impl AsContextMut, raw: u32, ) -> Option<Rooted<ExternRef>>

Creates a new strongly-owned ExternRef from the raw value provided.

This is intended to be used in conjunction with Func::new_unchecked, Func::call_unchecked, and ValRaw with its externref field.

This function assumes that raw is an externref value which is currently rooted within the Store.

§Unsafety

This function is particularly unsafe because raw not only must be a valid externref value produced prior by to_raw but it must also be correctly rooted within the store. When arguments are provided to a callback with Func::new_unchecked, for example, or returned via Func::call_unchecked, if a GC is performed within the store then floating externref values are not rooted and will be GC’d, meaning that this function will no longer be safe to call with the values cleaned up. This function must be invoked before possible GC operations can happen (such as calling wasm).

When in doubt try to not use this. Instead use the safe Rust APIs of TypedFunc and friends.

pub unsafe fn to_raw(&self, store: impl AsContextMut) -> Result<u32, Error>

Converts this ExternRef to a raw value suitable to store within a ValRaw.

Returns an error if this externref has been unrooted.

§Unsafety

Produces a raw value which is only safe to pass into a store if a GC doesn’t happen between when the value is produce and when it’s passed into the store.

Trait Implementations§

§

impl Clone for ExternRef

§

fn clone(&self) -> ExternRef

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
§

impl Debug for ExternRef

§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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.

source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

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> Pointee for T

source§

type Pointer = u32

source§

fn debug( pointer: <T as Pointee>::Pointer, f: &mut Formatter<'_>, ) -> Result<(), Error>

source§

impl<T> Same for T

source§

type Output = T

Should always be Self
source§

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

source§

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>,

source§

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>,

source§

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> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
§

impl<T> GcRef for T
where T: GcRefImpl,