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 
11 use crate::AsContextMut;
12 use crate::component::func::{LiftContext, LowerContext};
13 use crate::component::matching::InstanceType;
14 use crate::component::resources::host::{HostResource, HostResourceType};
15 use crate::component::{ComponentType, Lift, Lower, ResourceAny, ResourceType};
16 use crate::prelude::*;
17 use core::fmt;
18 use core::mem::MaybeUninit;
19 use 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
140 pub struct Resource<T: 'static>(HostResource<Static<T>, ()>);
141 
142 struct Static<T>(T);
143 
144 impl<T: 'static> HostResourceType<()> for Static<T> {
resource_type((): ()) -> ResourceType145     fn resource_type((): ()) -> ResourceType {
146         ResourceType::host::<T>()
147     }
148 
typecheck(ty: ResourceType) -> Option<()>149     fn typecheck(ty: ResourceType) -> Option<()> {
150         if ty.is_host::<T>() { Some(()) } else { None }
151     }
152 }
153 
154 impl<T> Resource<T>
155 where
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)`.
new_own(rep: u32) -> Resource<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.
new_borrow(rep: u32) -> Resource<T>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.
rep(&self) -> u32179     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.
owned(&self) -> bool188     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.
try_from_resource_any(resource: ResourceAny, store: impl AsContextMut) -> Result<Self>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`]
try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny>213     pub fn try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny> {
214         self.0.try_into_resource_any(store)
215     }
216 }
217 
218 unsafe impl<T: 'static> ComponentType for Resource<T> {
219     const ABI: CanonicalAbiInfo = HostResource::<Static<T>, ()>::ABI;
220     type Lower = crate::ValRaw;
221 
typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()>222     fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
223         HostResource::<Static<T>, ()>::typecheck(ty, types)
224     }
225 }
226 
227 unsafe impl<T: 'static> Lower for Resource<T> {
linear_lower_to_flat<U>( &self, cx: &mut LowerContext<'_, U>, ty: InterfaceType, dst: &mut MaybeUninit<Self::Lower>, ) -> Result<()>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 
linear_lower_to_memory<U>( &self, cx: &mut LowerContext<'_, U>, ty: InterfaceType, offset: usize, ) -> Result<()>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 
247 unsafe impl<T: 'static> Lift for Resource<T> {
linear_lift_from_flat( cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower, ) -> Result<Self>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 
linear_lift_from_memory( cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8], ) -> Result<Self>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 
267 impl<T> fmt::Debug for Resource<T> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result268     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269         self.0.fmt(f)
270     }
271 }
272