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
OwnedRooted<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§
Source§impl ExternRef
impl ExternRef
Sourcepub fn new<T>(
store: impl AsContextMut,
value: T,
) -> Result<Rooted<ExternRef>, Error>
pub fn new<T>( store: impl AsContextMut, value: T, ) -> Result<Rooted<ExternRef>, Error>
Synchronously allocates a new ExternRef wrapping the given value.
The resulting value is automatically unrooted when the given context’s
scope is exited. If you need to hold the reference past the context’s
scope, convert the result into an
OwnedRooted<T>. See the documentation for
Rooted<T> and
OwnedRooted<T> for more details.
§Automatic Garbage Collection
If the GC heap is at capacity, and there isn’t room for allocating a new
externref, this method will automatically trigger a synchronous
collection in an attempt to free up space in the GC heap.
§Errors
If the allocation cannot be satisfied because the GC heap is currently
out of memory, then a GcHeapOutOfMemory<T>
error is returned. The allocation might succeed on a second attempt if
you drop some rooted GC references and try again.
The GcHeapOutOfMemory<T> error contains
the host value that the externref would have wrapped. You can extract
that value from the error and reuse it when attempting to allocate an
externref again after dropping rooted GC references and then
performing a collection or otherwise do with it whatever you see fit.
§Example
let mut store = Store::<()>::default();
{
let mut scope = RootScope::new(&mut store);
// Allocate an `externref` wrapping a `String`.
let externref = match ExternRef::new(&mut scope, "hello!".to_string()) {
// The allocation succeeded.
Ok(x) => x,
// The allocation failed.
Err(e) => match e.downcast::<GcHeapOutOfMemory<String>>() {
// The allocation failed because the GC heap does not have
// capacity for this allocation.
Ok(oom) => {
// Take back ownership of our `String`.
let s = oom.into_inner();
// Drop some rooted GC refs from our system to potentially
// free up space for Wasmtime to make this allocation.
drop_some_gc_refs();
// Finally, try to allocate again, reusing the original
// string.
ExternRef::new(&mut scope, s)?
}
Err(e) => return Err(e),
},
};
// Use the `externref`, pass it to Wasm, etc...
}
// The `externref` is automatically unrooted when we exit the scope.§Panics
Panics if the context is configured for async; use
ExternRef::new_async to perform
asynchronous allocation instead.
Sourcepub async fn new_async<T>(
store: impl AsContextMut,
value: T,
) -> Result<Rooted<ExternRef>, Error>
pub async fn new_async<T>( store: impl AsContextMut, value: T, ) -> Result<Rooted<ExternRef>, Error>
Asynchronously allocates a new ExternRef wrapping the given value.
The resulting value is automatically unrooted when the given context’s
scope is exited. If you need to hold the reference past the context’s
scope, convert the result into an
OwnedRooted<T>. See the documentation for
Rooted<T> and
OwnedRooted<T> for more details.
§Automatic Garbage Collection
If the GC heap is at capacity, and there isn’t room for allocating a new
externref, this method will automatically trigger an asynchronous
collection in an attempt to free up space in the GC heap.
§Errors
If the allocation cannot be satisfied because the GC heap is currently
out of memory, then a GcHeapOutOfMemory<T>
error is returned. The allocation might succeed on a second attempt if
you drop some rooted GC references and try again.
The GcHeapOutOfMemory<T> error contains
the host value that the externref would have wrapped. You can extract
that value from the error and reuse it when attempting to allocate an
externref again after dropping rooted GC references and then
performing a collection or otherwise do with it whatever you see fit.
§Example
use wasmtime::*;
let mut store = Store::<()>::default();
{
let mut scope = RootScope::new(&mut store);
// Create an `externref` wrapping a `String`.
let externref = match ExternRef::new_async(&mut scope, "hello!".to_string()).await {
// The allocation succeeded.
Ok(x) => x,
// The allocation failed.
Err(e) => match e.downcast::<GcHeapOutOfMemory<String>>() {
// The allocation failed because the GC heap does not have
// capacity for this allocation.
Ok(oom) => {
// Take back ownership of our `String`.
let s = oom.into_inner();
// Drop some rooted GC refs from our system to potentially
// free up space for Wasmtime to make this allocation.
drop_some_gc_refs();
// Finally, try to allocate again, reusing the original
// string.
ExternRef::new_async(&mut scope, s).await?
}
Err(e) => return Err(e),
},
};
// Use the `externref`, pass it to Wasm, etc...
}
// The `externref` is automatically unrooted when we exit the scope.§Panics
Panics if the context is not configured for async; use
ExternRef::new to perform synchronous
allocation instead.
Sourcepub fn convert_any(
context: impl AsContextMut,
anyref: Rooted<AnyRef>,
) -> Result<Rooted<ExternRef>, Error>
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);Sourcepub fn data<'a, T>(
&self,
store: impl Into<StoreContext<'a, T>>,
) -> Result<Option<&'a (dyn Any + Sync + Send + 'static)>, Error>where
T: 'static,
pub fn data<'a, T>(
&self,
store: impl Into<StoreContext<'a, T>>,
) -> Result<Option<&'a (dyn Any + Sync + Send + 'static)>, Error>where
T: 'static,
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");Sourcepub fn data_mut<'a, T>(
&self,
store: impl Into<StoreContextMut<'a, T>>,
) -> Result<Option<&'a mut (dyn Any + Sync + Send + 'static)>, Error>where
T: 'static,
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: 'static,
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;Sourcepub fn from_raw(store: impl AsContextMut, raw: u32) -> Option<Rooted<ExternRef>>
pub 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.
§Correctness
This function is tricky to get right because raw not only must be a
valid externref value produced prior by ExternRef::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 correct 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 Rust APIs of
TypedFunc and friends. Note though that this function is not
unsafe as any value can be passed in. Incorrect values can result in
runtime panics, however, so care must still be taken with this method.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for ExternRef
impl RefUnwindSafe for ExternRef
impl Send for ExternRef
impl Sync for ExternRef
impl Unpin for ExternRef
impl UnwindSafe for ExternRef
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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