wasmtime/runtime/component/resources/
any.rs

1//! This module defines the `ResourceAny` type in the public API of Wasmtime,
2//! which represents a dynamically typed resource handle that could either be
3//! owned by the guest or the host.
4//!
5//! This is in contrast with `Resource<T>`, for example, and `ResourceAny` has
6//! more "state" behind it. Specifically a `ResourceAny` has a type and a
7//! `HostResourceIndex` which points inside of a `HostResourceData` structure
8//! inside of a store. The `ResourceAny::resource_drop` method, or a conversion
9//! to `Resource<T>`, is required to be called to avoid leaking data within a
10//! store.
11
12use crate::component::func::{LiftContext, LowerContext, bad_type_info, desc};
13use crate::component::matching::InstanceType;
14use crate::component::resources::host::{HostResource, HostResourceType};
15use crate::component::resources::{HostResourceIndex, HostResourceTables};
16use crate::component::{ComponentType, Lift, Lower, Resource, ResourceDynamic, ResourceType};
17use crate::prelude::*;
18use crate::runtime::vm::ValRaw;
19use crate::{AsContextMut, StoreContextMut, Trap};
20use core::mem::MaybeUninit;
21use core::ptr::NonNull;
22use wasmtime_environ::component::{CanonicalAbiInfo, InterfaceType};
23
24/// Representation of a resource in the component model, either a guest-defined
25/// or a host-defined resource.
26///
27/// This type is similar to [`Resource`] except that it can be used to represent
28/// any resource, either host or guest. This type cannot be directly constructed
29/// and is only available if the guest returns it to the host (e.g. a function
30/// returning a guest-defined resource) or by a conversion from [`Resource`] via
31/// [`ResourceAny::try_from_resource`].
32/// This type also does not carry a static type parameter `T` for example and
33/// does not have as much information about its type.
34/// This means that it's possible to get runtime type-errors when
35/// using this type because it cannot statically prevent mismatching resource
36/// types.
37///
38/// Like [`Resource`] this type represents either an `own` or a `borrow`
39/// resource internally. Unlike [`Resource`], however, a [`ResourceAny`] must
40/// always be explicitly destroyed with the [`ResourceAny::resource_drop`]
41/// method. This will update internal dynamic state tracking and invoke the
42/// WebAssembly-defined destructor for a resource, if any.
43///
44/// Note that it is required to call `resource_drop` for all instances of
45/// [`ResourceAny`]: even borrows. Both borrows and own handles have state
46/// associated with them that must be discarded by the time they're done being
47/// used.
48#[derive(Debug, PartialEq, Eq, Copy, Clone)]
49pub struct ResourceAny {
50    idx: HostResourceIndex,
51    ty: ResourceType,
52    owned: bool,
53}
54
55impl ResourceAny {
56    pub(crate) fn new(idx: HostResourceIndex, ty: ResourceType, owned: bool) -> ResourceAny {
57        ResourceAny { idx, ty, owned }
58    }
59
60    /// Attempts to convert an imported [`Resource`] into [`ResourceAny`].
61    ///
62    /// * `resource` is the resource to convert.
63    /// * `store` is the store to place the returned resource into.
64    ///
65    /// The returned `ResourceAny` will not have a destructor attached to it
66    /// meaning that if `resource_drop` is called then it will not invoked a
67    /// host-defined destructor. This is similar to how `Resource<T>` does not
68    /// have a destructor associated with it.
69    ///
70    /// # Errors
71    ///
72    /// This method will return an error if `resource` has already been "taken"
73    /// and has ownership transferred elsewhere which can happen in situations
74    /// such as when it's already lowered into a component.
75    pub fn try_from_resource<T: 'static>(
76        resource: Resource<T>,
77        store: impl AsContextMut,
78    ) -> Result<Self> {
79        resource.try_into_resource_any(store)
80    }
81
82    /// See [`Resource::try_from_resource_any`]
83    pub fn try_into_resource<T: 'static>(self, store: impl AsContextMut) -> Result<Resource<T>> {
84        Resource::try_from_resource_any(self, store)
85    }
86
87    /// See [`ResourceDynamic::try_from_resource_any`]
88    pub fn try_into_resource_dynamic(self, store: impl AsContextMut) -> Result<ResourceDynamic> {
89        ResourceDynamic::try_from_resource_any(self, store)
90    }
91
92    /// See [`Resource::try_from_resource_any`]
93    pub(crate) fn try_into_host_resource<T, D>(
94        self,
95        mut store: impl AsContextMut,
96    ) -> Result<HostResource<T, D>>
97    where
98        T: HostResourceType<D>,
99        D: PartialEq + Send + Sync + Copy + 'static,
100    {
101        let store = store.as_context_mut();
102        let mut tables = HostResourceTables::new_host(store.0);
103        let ResourceAny { idx, ty, owned } = self;
104        let ty = T::typecheck(ty).ok_or_else(|| anyhow::anyhow!("resource type mismatch"))?;
105        if owned {
106            let rep = tables.host_resource_lift_own(idx)?;
107            Ok(HostResource::new_own(rep, ty))
108        } else {
109            // For borrowed handles, first acquire the `rep` via lifting the
110            // borrow. Afterwards though remove any dynamic state associated
111            // with this borrow. `Resource<T>` doesn't participate in dynamic
112            // state tracking and it's assumed embedders know what they're
113            // doing, so the drop call will clear out that a borrow is active
114            //
115            // Note that the result of `drop` should always be `None` as it's a
116            // borrowed handle, so assert so.
117            let rep = tables.host_resource_lift_borrow(idx)?;
118            let res = tables.host_resource_drop(idx)?;
119            assert!(res.is_none());
120            Ok(HostResource::new_borrow(rep, ty))
121        }
122    }
123
124    /// Returns the corresponding type associated with this resource, either a
125    /// host-defined type or a guest-defined type.
126    ///
127    /// This can be compared against [`ResourceType::host`] for example to see
128    /// if it's a host-resource or against a type extracted with
129    /// [`Instance::get_resource`] to see if it's a guest-defined resource.
130    ///
131    /// [`Instance::get_resource`]: crate::component::Instance::get_resource
132    pub fn ty(&self) -> ResourceType {
133        self.ty
134    }
135
136    /// Returns whether this is an owned resource, and if not it's a borrowed
137    /// resource.
138    pub fn owned(&self) -> bool {
139        self.owned
140    }
141
142    /// Destroy this resource and release any state associated with it.
143    ///
144    /// This is required to be called (or the async version) for all instances
145    /// of [`ResourceAny`] to ensure that state associated with this resource is
146    /// properly cleaned up. For owned resources this may execute the
147    /// guest-defined destructor if applicable (or the host-defined destructor
148    /// if one was specified).
149    pub fn resource_drop(self, mut store: impl AsContextMut) -> Result<()> {
150        let mut store = store.as_context_mut();
151        assert!(
152            !store.0.async_support(),
153            "must use `resource_drop_async` when async support is enabled on the config"
154        );
155        self.resource_drop_impl(&mut store.as_context_mut())
156    }
157
158    /// Same as [`ResourceAny::resource_drop`] except for use with async stores
159    /// to execute the destructor asynchronously.
160    #[cfg(feature = "async")]
161    pub async fn resource_drop_async<T>(
162        self,
163        mut store: impl AsContextMut<Data: Send>,
164    ) -> Result<()> {
165        let mut store = store.as_context_mut();
166        assert!(
167            store.0.async_support(),
168            "cannot use `resource_drop_async` without enabling async support in the config"
169        );
170        store
171            .on_fiber(|store| self.resource_drop_impl(store))
172            .await?
173    }
174
175    fn resource_drop_impl<T: 'static>(self, store: &mut StoreContextMut<'_, T>) -> Result<()> {
176        // Attempt to remove `self.idx` from the host table in `store`.
177        //
178        // This could fail if the index is invalid or if this is removing an
179        // `Own` entry which is currently being borrowed.
180        let pair = HostResourceTables::new_host(store.0).host_resource_drop(self.idx)?;
181
182        let (rep, slot) = match (pair, self.owned) {
183            (Some(pair), true) => pair,
184
185            // A `borrow` was removed from the table and no further
186            // destruction, e.g. the destructor, is required so we're done.
187            (None, false) => return Ok(()),
188
189            _ => unreachable!(),
190        };
191
192        // Implement the reentrance check required by the canonical ABI. Note
193        // that this happens whether or not a destructor is present.
194        //
195        // Note that this should be safe because the raw pointer access in
196        // `flags` is valid due to `store` being the owner of the flags and
197        // flags are never destroyed within the store.
198        if let Some(flags) = slot.flags {
199            unsafe {
200                if !flags.may_enter() {
201                    bail!(Trap::CannotEnterComponent);
202                }
203            }
204        }
205
206        let dtor = match slot.dtor {
207            Some(dtor) => dtor.as_non_null(),
208            None => return Ok(()),
209        };
210        let mut args = [ValRaw::u32(rep)];
211
212        // This should be safe because `dtor` has been checked to belong to the
213        // `store` provided which means it's valid and still alive. Additionally
214        // destructors have al been previously type-checked and are guaranteed
215        // to take one i32 argument and return no results, so the parameters
216        // here should be configured correctly.
217        unsafe { crate::Func::call_unchecked_raw(store, dtor, NonNull::from(&mut args)) }
218    }
219
220    fn lower_to_index<U>(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result<u32> {
221        match ty {
222            InterfaceType::Own(t) => {
223                if cx.resource_type(t) != self.ty {
224                    bail!("mismatched resource types");
225                }
226                let rep = cx.host_resource_lift_own(self.idx)?;
227                cx.guest_resource_lower_own(t, rep)
228            }
229            InterfaceType::Borrow(t) => {
230                if cx.resource_type(t) != self.ty {
231                    bail!("mismatched resource types");
232                }
233                let rep = cx.host_resource_lift_borrow(self.idx)?;
234                cx.guest_resource_lower_borrow(t, rep)
235            }
236            _ => bad_type_info(),
237        }
238    }
239
240    fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result<Self> {
241        match ty {
242            InterfaceType::Own(t) => {
243                let ty = cx.resource_type(t);
244                let (rep, dtor, flags) = cx.guest_resource_lift_own(t, index)?;
245                let idx = cx.host_resource_lower_own(rep, dtor, flags)?;
246                Ok(ResourceAny {
247                    idx,
248                    ty,
249                    owned: true,
250                })
251            }
252            InterfaceType::Borrow(t) => {
253                let ty = cx.resource_type(t);
254                let rep = cx.guest_resource_lift_borrow(t, index)?;
255                let idx = cx.host_resource_lower_borrow(rep)?;
256                Ok(ResourceAny {
257                    idx,
258                    ty,
259                    owned: false,
260                })
261            }
262            _ => bad_type_info(),
263        }
264    }
265}
266
267unsafe impl ComponentType for ResourceAny {
268    const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4;
269
270    type Lower = <u32 as ComponentType>::Lower;
271
272    fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> {
273        match ty {
274            InterfaceType::Own(_) | InterfaceType::Borrow(_) => Ok(()),
275            other => bail!("expected `own` or `borrow`, found `{}`", desc(other)),
276        }
277    }
278}
279
280unsafe impl Lower for ResourceAny {
281    fn linear_lower_to_flat<T>(
282        &self,
283        cx: &mut LowerContext<'_, T>,
284        ty: InterfaceType,
285        dst: &mut MaybeUninit<Self::Lower>,
286    ) -> Result<()> {
287        self.lower_to_index(cx, ty)?
288            .linear_lower_to_flat(cx, InterfaceType::U32, dst)
289    }
290
291    fn linear_lower_to_memory<T>(
292        &self,
293        cx: &mut LowerContext<'_, T>,
294        ty: InterfaceType,
295        offset: usize,
296    ) -> Result<()> {
297        self.lower_to_index(cx, ty)?
298            .linear_lower_to_memory(cx, InterfaceType::U32, offset)
299    }
300}
301
302unsafe impl Lift for ResourceAny {
303    fn linear_lift_from_flat(
304        cx: &mut LiftContext<'_>,
305        ty: InterfaceType,
306        src: &Self::Lower,
307    ) -> Result<Self> {
308        let index = u32::linear_lift_from_flat(cx, InterfaceType::U32, src)?;
309        ResourceAny::lift_from_index(cx, ty, index)
310    }
311
312    fn linear_lift_from_memory(
313        cx: &mut LiftContext<'_>,
314        ty: InterfaceType,
315        bytes: &[u8],
316    ) -> Result<Self> {
317        let index = u32::linear_lift_from_memory(cx, InterfaceType::U32, bytes)?;
318        ResourceAny::lift_from_index(cx, ty, index)
319    }
320}