wasmtime/runtime/component/resources/
host_static.rs

1//! This module defines the `Resource<T>` type in the public API of Wasmtime.
2//!
3//! The purpose of this type is to represent a typed resource on the host where
4//! the runtime representation is just a 32-bit integer plus some minor state
5//! tracking. Notably the `T` enables statically differentiating resources from
6//! one another and enables up-front type-checking where the lift/lower
7//! operations need not do any type-checking at all.
8//!
9//! The actual `T` type parameter is just a guide, no `T` value is ever needed.
10
11use crate::AsContextMut;
12use crate::component::func::{LiftContext, LowerContext};
13use crate::component::matching::InstanceType;
14use crate::component::resources::host::{HostResource, HostResourceType};
15use crate::component::{ComponentType, Lift, Lower, ResourceAny, ResourceType};
16use crate::prelude::*;
17use core::fmt;
18use core::mem::MaybeUninit;
19use wasmtime_environ::component::{CanonicalAbiInfo, InterfaceType};
20
21/// A host-defined resource in the component model with a statically ascribed
22/// type parameter.
23///
24/// This type can be thought of as roughly a newtype wrapper around `u32` for
25/// use as a resource with the component model. The main guarantee that the
26/// component model provides is that the `u32` is not forgeable by guests and
27/// there are guaranteed semantics about when a `u32` may be in use by the guest
28/// and when it's guaranteed no longer needed. This means that it is safe for
29/// embedders to consider the internal `u32` representation "trusted" and use it
30/// for things like table indices with infallible accessors that panic on
31/// out-of-bounds. This should only panic for embedder bugs, not because of any
32/// possible behavior in the guest.
33///
34/// A `Resource<T>` value dynamically represents both an `(own $t)` in the
35/// component model as well as a `(borrow $t)`. It can be inspected via
36/// [`Resource::owned`] to test whether it is an owned handle. An owned handle
37/// which is not actively borrowed can be destroyed at any time as it's
38/// guaranteed that the guest does not have access to it. A borrowed handle, on
39/// the other hand, may be accessed by the guest so it's not necessarily
40/// guaranteed to be able to be destroyed.
41///
42/// Note that the "own" and "borrow" here refer to the component model, not
43/// Rust. The semantics of Rust ownership and borrowing are slightly different
44/// than the component model's (but spiritually the same) in that more dynamic
45/// tracking is employed as part of the component model. This means that it's
46/// possible to get runtime errors when using a `Resource<T>`. For example it is
47/// an error to call [`Resource::new_borrow`] and pass that to a component
48/// function expecting `(own $t)` and this is not statically disallowed.
49///
50/// The [`Resource`] type implements both the [`Lift`] and [`Lower`] trait to be
51/// used with typed functions in the component model or as part of aggregate
52/// structures and datatypes.
53///
54/// Note that [`Resource`] and [`ResourceDynamic`] are similar and for more
55/// discussion of choosing one over the other see the documentation on
56/// [`ResourceDynamic`]
57///
58/// [`ResourceDynamic`]: crate::component::ResourceDynamic
59///
60/// # Destruction of a resource
61///
62/// Resources in the component model are optionally defined with a destructor,
63/// but this host resource type does not specify a destructor. It is left up to
64/// the embedder to be able to determine how best to a destroy a resource when
65/// it is owned.
66///
67/// Note, though, that while [`Resource`] itself does not specify destructors
68/// it's still possible to do so via the [`Linker::resource`] definition. When a
69/// resource type is defined for a guest component a destructor can be specified
70/// which can be used to hook into resource destruction triggered by the guest.
71///
72/// This means that there are two ways that resource destruction is handled:
73///
74/// * Host resources destroyed by the guest can hook into the
75///   [`Linker::resource`] destructor closure to handle resource destruction.
76///   This could, for example, remove table entries.
77///
78/// * Host resources owned by the host itself have no automatic means of
79///   destruction. The host can make its own determination that its own resource
80///   is not lent out to the guest and at that time choose to destroy or
81///   deallocate it.
82///
83/// # Dynamic behavior of a resource
84///
85/// A host-defined [`Resource`] does not necessarily represent a static value.
86/// Its internals may change throughout its usage to track the state associated
87/// with the resource. The internal 32-bit host-defined representation never
88/// changes, however.
89///
90/// For example if there's a component model function of the form:
91///
92/// ```wasm
93/// (func (param "a" (borrow $t)) (param "b" (own $t)))
94/// ```
95///
96/// Then that can be extracted in Rust with:
97///
98/// ```rust,ignore
99/// let func = instance.get_typed_func::<(&Resource<T>, &Resource<T>), ()>(&mut store, "name")?;
100/// ```
101///
102/// Here the exact same resource can be provided as both arguments but that is
103/// not valid to do so because the same resource cannot be actively borrowed and
104/// passed by-value as the second parameter at the same time. The internal state
105/// in `Resource<T>` will track this information and provide a dynamic runtime
106/// error in this situation.
107///
108/// Mostly it's important to be aware that there is dynamic state associated
109/// with a [`Resource<T>`] to provide errors in situations that cannot be
110/// statically ruled out.
111///
112/// # Borrows and host responsibilities
113///
114/// Borrows to resources in the component model are guaranteed to be transient
115/// such that if a borrow is passed as part of a function call then when the
116/// function returns it's guaranteed that the guest no longer has access to the
117/// resource. This guarantee, however, must be manually upheld by the host when
118/// it receives its own borrow.
119///
120/// As mentioned above the [`Resource<T>`] type can represent a borrowed value
121/// in addition to an owned value. This means a guest can provide the host with
122/// a borrow, such as an argument to an imported function:
123///
124/// ```rust,ignore
125/// linker.root().func_wrap("name", |_cx, (r,): (Resource<MyType>,)| {
126///     assert!(!r.owned());
127///     // .. here `r` is a borrowed value provided by the guest and the host
128///     // shouldn't continue to access it beyond the scope of this call
129/// })?;
130/// ```
131///
132/// In this situation the host should take care to not attempt to persist the
133/// resource beyond the scope of the call. It's the host's resource so it
134/// technically can do what it wants with it but nothing is statically
135/// preventing `r` to stay pinned to the lifetime of the closure invocation.
136/// It's considered a mistake that the host performed if `r` is persisted too
137/// long and accessed at the wrong time.
138///
139/// [`Linker::resource`]: crate::component::LinkerInstance::resource
140pub struct Resource<T: 'static>(HostResource<Static<T>, ()>);
141
142struct Static<T>(T);
143
144impl<T: 'static> HostResourceType<()> for Static<T> {
145    fn resource_type((): ()) -> ResourceType {
146        ResourceType::host::<T>()
147    }
148
149    fn typecheck(ty: ResourceType) -> Option<()> {
150        if ty.is_host::<T>() { Some(()) } else { None }
151    }
152}
153
154impl<T> Resource<T>
155where
156    T: 'static,
157{
158    /// Creates a new owned resource with the `rep` specified.
159    ///
160    /// The returned value is suitable for passing to a guest as either a
161    /// `(borrow $t)` or `(own $t)`.
162    pub fn new_own(rep: u32) -> Resource<T> {
163        Resource(HostResource::new_own(rep, ()))
164    }
165
166    /// Creates a new borrowed resource which isn't actually rooted in any
167    /// ownership.
168    ///
169    /// This can be used to pass to a guest as a borrowed resource and the
170    /// embedder will know that the `rep` won't be in use by the guest
171    /// afterwards. Exactly how the lifetime of `rep` works is up to the
172    /// embedder.
173    pub fn new_borrow(rep: u32) -> Resource<T> {
174        Resource(HostResource::new_borrow(rep, ()))
175    }
176
177    /// Returns the underlying 32-bit representation used to originally create
178    /// this resource.
179    pub fn rep(&self) -> u32 {
180        self.0.rep()
181    }
182
183    /// Returns whether this is an owned resource or not.
184    ///
185    /// Owned resources can be safely destroyed by the embedder at any time, and
186    /// borrowed resources have an owner somewhere else on the stack so can only
187    /// be accessed, not destroyed.
188    pub fn owned(&self) -> bool {
189        self.0.owned()
190    }
191
192    /// Attempts to convert a [`ResourceAny`] into [`Resource`].
193    ///
194    /// This method will check that `resource` has type
195    /// `ResourceType::host::<T>()` and then convert it into a typed version of
196    /// the resource.
197    ///
198    /// # Errors
199    ///
200    /// This function will return an error if `resource` does not have type
201    /// `ResourceType::host::<T>()`. This function may also return an error if
202    /// `resource` is no longer valid, for example it was previously converted.
203    ///
204    /// # Panics
205    ///
206    /// This function will panic if `resource` does not belong to the `store`
207    /// specified.
208    pub fn try_from_resource_any(resource: ResourceAny, store: impl AsContextMut) -> Result<Self> {
209        Ok(Resource(resource.try_into_host_resource(store)?))
210    }
211
212    /// See [`ResourceAny::try_from_resource`]
213    pub fn try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny> {
214        self.0.try_into_resource_any(store)
215    }
216}
217
218unsafe impl<T: 'static> ComponentType for Resource<T> {
219    const ABI: CanonicalAbiInfo = HostResource::<Static<T>, ()>::ABI;
220    type Lower = crate::ValRaw;
221
222    fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
223        HostResource::<Static<T>, ()>::typecheck(ty, types)
224    }
225}
226
227unsafe impl<T: 'static> Lower for Resource<T> {
228    fn linear_lower_to_flat<U>(
229        &self,
230        cx: &mut LowerContext<'_, U>,
231        ty: InterfaceType,
232        dst: &mut MaybeUninit<Self::Lower>,
233    ) -> Result<()> {
234        self.0.linear_lower_to_flat(cx, ty, dst)
235    }
236
237    fn linear_lower_to_memory<U>(
238        &self,
239        cx: &mut LowerContext<'_, U>,
240        ty: InterfaceType,
241        offset: usize,
242    ) -> Result<()> {
243        self.0.linear_lower_to_memory(cx, ty, offset)
244    }
245}
246
247unsafe impl<T: 'static> Lift for Resource<T> {
248    fn linear_lift_from_flat(
249        cx: &mut LiftContext<'_>,
250        ty: InterfaceType,
251        src: &Self::Lower,
252    ) -> Result<Self> {
253        let host_resource = HostResource::linear_lift_from_flat(cx, ty, src)?;
254        Ok(Resource(host_resource))
255    }
256
257    fn linear_lift_from_memory(
258        cx: &mut LiftContext<'_>,
259        ty: InterfaceType,
260        bytes: &[u8],
261    ) -> Result<Self> {
262        let host_resource = HostResource::linear_lift_from_memory(cx, ty, bytes)?;
263        Ok(Resource(host_resource))
264    }
265}
266
267impl<T> fmt::Debug for Resource<T> {
268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        self.0.fmt(f)
270    }
271}