pub struct ExternRef { /* private fields */ }
gc
and runtime
only.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 externref
s other than put them in tables, globals,
and locals or pass them to other functions (such as imported functions from
the host). Unlike anyref
s, Wasm guests cannot directly allocate new
externref
s; 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§
Source§impl ExternRef
impl ExternRef
Sourcepub fn new<T>(context: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>>
pub fn new<T>(context: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>>
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 a
ManuallyRooted<T>
. See the documentation for
Rooted<T>
and
ManuallyRooted<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>(
context: impl AsContextMut,
value: T,
) -> Result<Rooted<ExternRef>>
Available on crate feature async
only.
pub async fn new_async<T>( context: impl AsContextMut, value: T, ) -> Result<Rooted<ExternRef>>
async
only.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 a
ManuallyRooted<T>
. See the documentation for
Rooted<T>
and
ManuallyRooted<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>>
pub fn convert_any( context: impl AsContextMut, anyref: Rooted<AnyRef>, ) -> Result<Rooted<ExternRef>>
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 + Send + Sync)>>where
T: 'a,
pub fn data<'a, T>(
&self,
store: impl Into<StoreContext<'a, T>>,
) -> Result<Option<&'a (dyn Any + Send + Sync)>>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");
Sourcepub fn data_mut<'a, T>(
&self,
store: impl Into<StoreContextMut<'a, T>>,
) -> Result<Option<&'a mut (dyn Any + Send + Sync)>>where
T: 'a,
pub fn data_mut<'a, T>(
&self,
store: impl Into<StoreContextMut<'a, T>>,
) -> Result<Option<&'a mut (dyn Any + Send + Sync)>>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;
Sourcepub unsafe fn from_raw(
store: impl AsContextMut,
raw: u32,
) -> Option<Rooted<ExternRef>>
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.
Sourcepub unsafe fn to_raw(&self, store: impl AsContextMut) -> Result<u32>
pub unsafe fn to_raw(&self, store: impl AsContextMut) -> Result<u32>
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,
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