dusk_wasmtime/runtime/component/resources.rs
1use crate::component::func::{bad_type_info, desc, LiftContext, LowerContext};
2use crate::component::matching::InstanceType;
3use crate::component::{ComponentType, Lift, Lower};
4use crate::store::{StoreId, StoreOpaque};
5use crate::{AsContextMut, StoreContextMut, Trap};
6use anyhow::{bail, ensure, Result};
7use std::any::TypeId;
8use std::fmt;
9use std::marker;
10use std::mem::MaybeUninit;
11use std::ptr::NonNull;
12use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
13use wasmtime_environ::component::{
14 CanonicalAbiInfo, ComponentTypes, DefinedResourceIndex, InterfaceType, ResourceIndex,
15 TypeResourceTableIndex,
16};
17use wasmtime_runtime::component::{ComponentInstance, InstanceFlags, ResourceTables};
18use wasmtime_runtime::{SendSyncPtr, VMFuncRef, ValRaw};
19
20/// Representation of a resource type in the component model.
21///
22/// Resources are currently always represented as 32-bit integers but they have
23/// unique types across instantiations and the host. For example instantiating
24/// the same component twice means that defined resource types in the component
25/// will all be different. Values of this type can be compared to see if
26/// resources have the same type.
27///
28/// Resource types can also be defined on the host in addition to guests. On the
29/// host resource types are tied to a `T`, an arbitrary Rust type. Two host
30/// resource types are the same if they point to the same `T`.
31#[derive(Debug, Copy, Clone, PartialEq, Eq)]
32pub struct ResourceType {
33 kind: ResourceTypeKind,
34}
35
36impl ResourceType {
37 /// Creates a new host resource type corresponding to `T`.
38 ///
39 /// Note that `T` is a mostly a phantom type parameter here. It does not
40 /// need to reflect the actual storage of the resource `T`. For example this
41 /// is valid:
42 ///
43 /// ```rust
44 /// use wasmtime::component::ResourceType;
45 ///
46 /// struct Foo;
47 ///
48 /// let ty = ResourceType::host::<Foo>();
49 /// ```
50 ///
51 /// A resource type of type `ResourceType::host::<T>()` will match the type
52 /// of the value produced by `Resource::<T>::new_{own,borrow}`.
53 pub fn host<T: 'static>() -> ResourceType {
54 ResourceType {
55 kind: ResourceTypeKind::Host(TypeId::of::<T>()),
56 }
57 }
58
59 pub(crate) fn guest(
60 store: StoreId,
61 instance: &ComponentInstance,
62 id: DefinedResourceIndex,
63 ) -> ResourceType {
64 ResourceType {
65 kind: ResourceTypeKind::Guest {
66 store,
67 instance: instance as *const _ as usize,
68 id,
69 },
70 }
71 }
72
73 pub(crate) fn uninstantiated(types: &ComponentTypes, index: ResourceIndex) -> ResourceType {
74 ResourceType {
75 kind: ResourceTypeKind::Uninstantiated {
76 component: types as *const _ as usize,
77 index,
78 },
79 }
80 }
81}
82
83#[derive(Debug, Copy, Clone, PartialEq, Eq)]
84enum ResourceTypeKind {
85 Host(TypeId),
86 Guest {
87 store: StoreId,
88 // For now this is the `*mut ComponentInstance` pointer within the store
89 // that this guest corresponds to. It's used to distinguish different
90 // instantiations of the same component within the store.
91 instance: usize,
92 id: DefinedResourceIndex,
93 },
94 Uninstantiated {
95 // Like `instance` in `Guest` above this is a pointer and is used to
96 // distinguish between two components. Technically susceptible to ABA
97 // issues but the consequence is a nonexistent resource would be equal
98 // to a new resource so there's not really any issue with that.
99 component: usize,
100 index: ResourceIndex,
101 },
102}
103
104/// A host-defined resource in the component model.
105///
106/// This type can be thought of as roughly a newtype wrapper around `u32` for
107/// use as a resource with the component model. The main guarantee that the
108/// component model provides is that the `u32` is not forgeable by guests and
109/// there are guaranteed semantics about when a `u32` may be in use by the guest
110/// and when it's guaranteed no longer needed. This means that it is safe for
111/// embedders to consider the internal `u32` representation "trusted" and use it
112/// for things like table indices with infallible accessors that panic on
113/// out-of-bounds. This should only panic for embedder bugs, not because of any
114/// possible behavior in the guest.
115///
116/// A `Resource<T>` value dynamically represents both an `(own $t)` in the
117/// component model as well as a `(borrow $t)`. It can be inspected via
118/// [`Resource::owned`] to test whether it is an owned handle. An owned handle
119/// which is not actively borrowed can be destroyed at any time as it's
120/// guaranteed that the guest does not have access to it. A borrowed handle, on
121/// the other hand, may be accessed by the guest so it's not necessarily
122/// guaranteed to be able to be destroyed.
123///
124/// Note that the "own" and "borrow" here refer to the component model, not
125/// Rust. The semantics of Rust ownership and borrowing are slightly different
126/// than the component model's (but spiritually the same) in that more dynamic
127/// tracking is employed as part of the component model. This means that it's
128/// possible to get runtime errors when using a `Resource<T>`. For example it is
129/// an error to call [`Resource::new_borrow`] and pass that to a component
130/// function expecting `(own $t)` and this is not statically disallowed.
131///
132/// The [`Resource`] type implements both the [`Lift`] and [`Lower`] trait to be
133/// used with typed functions in the component model or as part of aggregate
134/// structures and datatypes.
135///
136/// # Destruction of a resource
137///
138/// Resources in the component model are optionally defined with a destructor,
139/// but this host resource type does not specify a destructor. It is left up to
140/// the embedder to be able to determine how best to a destroy a resource when
141/// it is owned.
142///
143/// Note, though, that while [`Resource`] itself does not specify destructors
144/// it's still possible to do so via the [`Linker::resource`] definition. When a
145/// resource type is defined for a guest component a destructor can be specified
146/// which can be used to hook into resource destruction triggered by the guest.
147///
148/// This means that there are two ways that resource destruction is handled:
149///
150/// * Host resources destroyed by the guest can hook into the
151/// [`Linker::resource`] destructor closure to handle resource destruction.
152/// This could, for example, remove table entries.
153///
154/// * Host resources owned by the host itself have no automatic means of
155/// destruction. The host can make its own determination that its own resource
156/// is not lent out to the guest and at that time choose to destroy or
157/// deallocate it.
158///
159/// # Dynamic behavior of a resource
160///
161/// A host-defined [`Resource`] does not necessarily represent a static value.
162/// Its internals may change throughout its usage to track the state associated
163/// with the resource. The internal 32-bit host-defined representation never
164/// changes, however.
165///
166/// For example if there's a component model function of the form:
167///
168/// ```wasm
169/// (func (param "a" (borrow $t)) (param "b" (own $t)))
170/// ```
171///
172/// Then that can be extracted in Rust with:
173///
174/// ```rust,ignore
175/// let func = instance.get_typed_func::<(&Resource<T>, &Resource<T>), ()>(&mut store, "name")?;
176/// ```
177///
178/// Here the exact same resource can be provided as both arguments but that is
179/// not valid to do so because the same resource cannot be actively borrowed and
180/// passed by-value as the second parameter at the same time. The internal state
181/// in `Resource<T>` will track this information and provide a dynamic runtime
182/// error in this situation.
183///
184/// Mostly it's important to be aware that there is dynamic state associated
185/// with a [`Resource<T>`] to provide errors in situations that cannot be
186/// statically ruled out.
187///
188/// # Borrows and host responsibilities
189///
190/// Borrows to resources in the component model are guaranteed to be transient
191/// such that if a borrow is passed as part of a function call then when the
192/// function returns it's guaranteed that the guest no longer has access to the
193/// resource. This guarantee, however, must be manually upheld by the host when
194/// it receives its own borrow.
195///
196/// As mentioned above the [`Resource<T>`] type can represent a borrowed value
197/// in addition to an owned value. This means a guest can provide the host with
198/// a borrow, such as an argument to an imported function:
199///
200/// ```rust,ignore
201/// linker.root().func_wrap("name", |_cx, (r,): (Resource<MyType>,)| {
202/// assert!(!r.owned());
203/// // .. here `r` is a borrowed value provided by the guest and the host
204/// // shouldn't continue to access it beyond the scope of this call
205/// })?;
206/// ```
207///
208/// In this situation the host should take care to not attempt to persist the
209/// resource beyond the scope of the call. It's the host's resource so it
210/// technically can do what it wants with it but nothing is statically
211/// preventing `r` to stay pinned to the lifetime of the closure invocation.
212/// It's considered a mistake that the host performed if `r` is persisted too
213/// long and accessed at the wrong time.
214///
215/// [`Linker::resource`]: crate::component::LinkerInstance::resource
216pub struct Resource<T> {
217 /// The host-defined 32-bit representation of this resource.
218 rep: u32,
219
220 /// Dear rust please consider `T` used even though it's not actually used.
221 _marker: marker::PhantomData<fn() -> T>,
222
223 state: AtomicResourceState,
224}
225
226/// Internal dynamic state tracking for this resource. This can be one of
227/// four different states:
228///
229/// * `BORROW` / `u64::MAX` - this indicates that this is a borrowed
230/// resource. The `rep` doesn't live in the host table and this `Resource`
231/// instance is transiently available. It's the host's responsibility to
232/// discard this resource when the borrow duration has finished.
233///
234/// * `NOT_IN_TABLE` / `u64::MAX - 1` - this indicates that this is an owned
235/// resource not present in any store's table. This resource is not lent
236/// out. It can be passed as an `(own $t)` directly into a guest's table
237/// or it can be passed as a borrow to a guest which will insert it into
238/// a host store's table for dynamic borrow tracking.
239///
240/// * `TAKEN` / `u64::MAX - 2` - while the `rep` is available the resource
241/// has been dynamically moved into a guest and cannot be moved in again.
242/// This is used for example to prevent the same resource from being
243/// passed twice to a guest.
244///
245/// * All other values - any other value indicates that the value is an
246/// index into a store's table of host resources. It's guaranteed that the
247/// table entry represents a host resource and the resource may have
248/// borrow tracking associated with it. The low 32-bits of the value are
249/// the table index and the upper 32-bits are the generation.
250///
251/// Note that this is an `AtomicU64` but it's not intended to actually be
252/// used in conjunction with threads as generally a `Store<T>` lives on one
253/// thread at a time. The `AtomicU64` here is used to ensure that this type
254/// is `Send + Sync` when captured as a reference to make async programming
255/// more ergonomic.
256struct AtomicResourceState(AtomicU64);
257
258#[derive(Debug, PartialEq, Eq, Copy, Clone)]
259enum ResourceState {
260 Borrow,
261 NotInTable,
262 Taken,
263 Index(HostResourceIndex),
264}
265
266impl AtomicResourceState {
267 const BORROW: Self = Self(AtomicU64::new(ResourceState::BORROW));
268 const NOT_IN_TABLE: Self = Self(AtomicU64::new(ResourceState::NOT_IN_TABLE));
269
270 fn get(&self) -> ResourceState {
271 ResourceState::decode(self.0.load(Relaxed))
272 }
273
274 fn swap(&self, state: ResourceState) -> ResourceState {
275 ResourceState::decode(self.0.swap(state.encode(), Relaxed))
276 }
277}
278
279impl ResourceState {
280 // See comments on `state` above for info about these values.
281 const BORROW: u64 = u64::MAX;
282 const NOT_IN_TABLE: u64 = u64::MAX - 1;
283 const TAKEN: u64 = u64::MAX - 2;
284
285 fn decode(bits: u64) -> ResourceState {
286 match bits {
287 Self::BORROW => Self::Borrow,
288 Self::NOT_IN_TABLE => Self::NotInTable,
289 Self::TAKEN => Self::Taken,
290 other => Self::Index(HostResourceIndex(other)),
291 }
292 }
293
294 fn encode(&self) -> u64 {
295 match self {
296 Self::Borrow => Self::BORROW,
297 Self::NotInTable => Self::NOT_IN_TABLE,
298 Self::Taken => Self::TAKEN,
299 Self::Index(index) => index.0,
300 }
301 }
302}
303
304/// Metadata tracking the state of resources within a `Store`.
305///
306/// This is a borrowed structure created from a `Store` piecemeal from below.
307/// The `ResourceTables` type holds most of the raw information and this
308/// structure tacks on a reference to `HostResourceData` to track generation
309/// numbers of host indices.
310pub struct HostResourceTables<'a> {
311 tables: ResourceTables<'a>,
312 host_resource_data: &'a mut HostResourceData,
313}
314
315/// Metadata for host-owned resources owned within a `Store`.
316///
317/// This metadata is used to prevent the ABA problem with indices handed out as
318/// part of `Resource` and `ResourceAny`. Those structures are `Copy` meaning
319/// that it's easy to reuse them, possibly accidentally. To prevent issues in
320/// the host Wasmtime attaches both an index (within `ResourceTables`) as well
321/// as a 32-bit generation counter onto each `HostResourceIndex` which the host
322/// actually holds in `Resource` and `ResourceAny`.
323///
324/// This structure holds a list which is a parallel list to the "list of reps"
325/// that's stored within `ResourceTables` elsewhere in the `Store`. This
326/// parallel list holds the last known generation of each element in the table.
327/// The generation is then compared on access to make sure it's the same.
328///
329/// Whenever a slot in the table is allocated the `cur_generation` field is
330/// pushed at the corresponding index of `generation_of_table_slot`. Whenever
331/// a field is accessed the current value of `generation_of_table_slot` is
332/// checked against the generation of the index. Whenever a slot is deallocated
333/// the generation is incremented. Put together this means that any access of a
334/// deallocated slot should deterministically provide an error.
335#[derive(Default)]
336pub struct HostResourceData {
337 cur_generation: u32,
338 table_slot_metadata: Vec<TableSlot>,
339}
340
341#[derive(Copy, Clone)]
342struct TableSlot {
343 generation: u32,
344 flags: Option<InstanceFlags>,
345 dtor: Option<SendSyncPtr<VMFuncRef>>,
346}
347
348/// Host representation of an index into a table slot.
349///
350/// This is morally (u32, u32) but is encoded as a 64-bit integer. The low
351/// 32-bits are the table index and the upper 32-bits are the generation
352/// counter.
353#[derive(PartialEq, Eq, Debug, Copy, Clone)]
354#[repr(transparent)]
355pub struct HostResourceIndex(u64);
356
357impl HostResourceIndex {
358 fn new(idx: u32, gen: u32) -> HostResourceIndex {
359 HostResourceIndex(u64::from(idx) | (u64::from(gen) << 32))
360 }
361
362 fn index(&self) -> u32 {
363 u32::try_from(self.0 & 0xffffffff).unwrap()
364 }
365
366 fn gen(&self) -> u32 {
367 u32::try_from(self.0 >> 32).unwrap()
368 }
369}
370
371impl<'a> HostResourceTables<'a> {
372 pub fn new_host(store: &'a mut StoreOpaque) -> HostResourceTables<'_> {
373 let (calls, host_table, host_resource_data) = store.component_resource_state();
374 HostResourceTables::from_parts(
375 ResourceTables {
376 host_table: Some(host_table),
377 calls,
378 tables: None,
379 },
380 host_resource_data,
381 )
382 }
383
384 pub fn from_parts(
385 tables: ResourceTables<'a>,
386 host_resource_data: &'a mut HostResourceData,
387 ) -> Self {
388 HostResourceTables {
389 tables,
390 host_resource_data,
391 }
392 }
393
394 /// Lifts an `own` resource that resides in the host's tables at the `idx`
395 /// specified into its `rep`.
396 ///
397 /// # Errors
398 ///
399 /// Returns an error if `idx` doesn't point to a valid owned resource, or
400 /// if `idx` can't be lifted as an `own` (e.g. it has active borrows).
401 pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> {
402 let (idx, _) = self.validate_host_index(idx, true)?;
403 self.tables.resource_lift_own(None, idx)
404 }
405
406 /// See [`HostResourceTables::host_resource_lift_own`].
407 pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> {
408 let (idx, _) = self.validate_host_index(idx, false)?;
409 self.tables.resource_lift_borrow(None, idx)
410 }
411
412 /// Lowers an `own` resource to be owned by the host.
413 ///
414 /// This returns a new index into the host's set of resource tables which
415 /// will point to the `rep` specified. The returned index is suitable for
416 /// conversion into either [`Resource`] or [`ResourceAny`].
417 ///
418 /// The `dtor` and instance `flags` are specified as well to know what
419 /// destructor to run when this resource is destroyed.
420 pub fn host_resource_lower_own(
421 &mut self,
422 rep: u32,
423 dtor: Option<NonNull<VMFuncRef>>,
424 flags: Option<InstanceFlags>,
425 ) -> Result<HostResourceIndex> {
426 let idx = self.tables.resource_lower_own(None, rep)?;
427 Ok(self.new_host_index(idx, dtor, flags))
428 }
429
430 /// See [`HostResourceTables::host_resource_lower_own`].
431 pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> {
432 let idx = self.tables.resource_lower_borrow(None, rep)?;
433 Ok(self.new_host_index(idx, None, None))
434 }
435
436 /// Validates that `idx` is still valid for the host tables, notably
437 /// ensuring that the generation listed in `idx` is the same as the
438 /// last recorded generation of the slot itself.
439 ///
440 /// The `is_removal` option indicates whether or not this table access will
441 /// end up removing the element from the host table. In such a situation the
442 /// current generation number is incremented.
443 fn validate_host_index(
444 &mut self,
445 idx: HostResourceIndex,
446 is_removal: bool,
447 ) -> Result<(u32, Option<TableSlot>)> {
448 let actual = usize::try_from(idx.index())
449 .ok()
450 .and_then(|i| self.host_resource_data.table_slot_metadata.get(i).copied());
451
452 // If `idx` is out-of-bounds then skip returning an error. In such a
453 // situation the operation that this is guarding will return a more
454 // precise error, such as a lift operation.
455 if let Some(actual) = actual {
456 if actual.generation != idx.gen() {
457 bail!("host-owned resource is being used with the wrong type");
458 }
459 }
460
461 // Bump the current generation of this is a removal to ensure any
462 // future item placed in this slot can't be pointed to by the `idx`
463 // provided above.
464 if is_removal {
465 self.host_resource_data.cur_generation += 1;
466 }
467
468 Ok((idx.index(), actual))
469 }
470
471 /// Creates a new `HostResourceIndex` which will point to the raw table
472 /// slot provided by `idx`.
473 ///
474 /// This will register metadata necessary to track the current generation
475 /// in the returned `HostResourceIndex` as well.
476 fn new_host_index(
477 &mut self,
478 idx: u32,
479 dtor: Option<NonNull<VMFuncRef>>,
480 flags: Option<InstanceFlags>,
481 ) -> HostResourceIndex {
482 let list = &mut self.host_resource_data.table_slot_metadata;
483 let info = TableSlot {
484 generation: self.host_resource_data.cur_generation,
485 flags,
486 dtor: dtor.map(SendSyncPtr::new),
487 };
488 match list.get_mut(idx as usize) {
489 Some(slot) => *slot = info,
490 None => {
491 // Resource handles start at 1, not zero, so push two elements
492 // for the first resource handle.
493 if list.is_empty() {
494 assert_eq!(idx, 1);
495 list.push(TableSlot {
496 generation: 0,
497 flags: None,
498 dtor: None,
499 });
500 }
501 assert_eq!(idx as usize, list.len());
502 list.push(info);
503 }
504 }
505
506 HostResourceIndex::new(idx, info.generation)
507 }
508
509 /// Drops a host-owned resource from host tables.
510 ///
511 /// This method will attempt to interpret `idx` as pointing to either a
512 /// `borrow` or `own` resource with the `expected` type specified. This
513 /// method will then return the underlying `rep` if it points to an `own`
514 /// resource which can then be further processed for destruction.
515 ///
516 /// # Errors
517 ///
518 /// Returns an error if `idx` doesn't point to a valid resource, points to
519 /// an `own` with active borrows, or if it doesn't have the type `expected`
520 /// in the host tables.
521 fn host_resource_drop(&mut self, idx: HostResourceIndex) -> Result<Option<(u32, TableSlot)>> {
522 let (idx, slot) = self.validate_host_index(idx, true)?;
523 match self.tables.resource_drop(None, idx)? {
524 Some(rep) => Ok(Some((rep, slot.unwrap()))),
525 None => Ok(None),
526 }
527 }
528
529 /// Lowers an `own` resource into the guest, converting the `rep` specified
530 /// into a guest-local index.
531 ///
532 /// The `ty` provided is which table to put this into.
533 pub fn guest_resource_lower_own(
534 &mut self,
535 rep: u32,
536 ty: TypeResourceTableIndex,
537 ) -> Result<u32> {
538 self.tables.resource_lower_own(Some(ty), rep)
539 }
540
541 /// Lowers a `borrow` resource into the guest, converting the `rep`
542 /// specified into a guest-local index.
543 ///
544 /// The `ty` provided is which table to put this into.
545 ///
546 /// Note that this cannot be used in isolation because lowering a borrow
547 /// into a guest has a special case where `rep` is returned directly if `ty`
548 /// belongs to the component being lowered into. That property must be
549 /// handled by the caller of this function.
550 pub fn guest_resource_lower_borrow(
551 &mut self,
552 rep: u32,
553 ty: TypeResourceTableIndex,
554 ) -> Result<u32> {
555 self.tables.resource_lower_borrow(Some(ty), rep)
556 }
557
558 /// Lifts an `own` resource from the `idx` specified from the table `ty`.
559 ///
560 /// This will lookup the appropriate table in the guest and return the `rep`
561 /// corresponding to `idx` if it's valid.
562 pub fn guest_resource_lift_own(&mut self, idx: u32, ty: TypeResourceTableIndex) -> Result<u32> {
563 self.tables.resource_lift_own(Some(ty), idx)
564 }
565
566 /// Lifts a `borrow` resource from the `idx` specified from the table `ty`.
567 ///
568 /// This will lookup the appropriate table in the guest and return the `rep`
569 /// corresponding to `idx` if it's valid.
570 pub fn guest_resource_lift_borrow(
571 &mut self,
572 idx: u32,
573 ty: TypeResourceTableIndex,
574 ) -> Result<u32> {
575 self.tables.resource_lift_borrow(Some(ty), idx)
576 }
577
578 /// Begins a call into the component instance, starting recording of
579 /// metadata related to resource borrowing.
580 #[inline]
581 pub fn enter_call(&mut self) {
582 self.tables.enter_call()
583 }
584
585 /// Completes a call into the component instance, validating that it's ok to
586 /// complete by ensuring the are no remaining active borrows.
587 #[inline]
588 pub fn exit_call(&mut self) -> Result<()> {
589 self.tables.exit_call()
590 }
591}
592
593impl<T> Resource<T>
594where
595 T: 'static,
596{
597 /// Creates a new owned resource with the `rep` specified.
598 ///
599 /// The returned value is suitable for passing to a guest as either a
600 /// `(borrow $t)` or `(own $t)`.
601 pub fn new_own(rep: u32) -> Resource<T> {
602 Resource {
603 state: AtomicResourceState::NOT_IN_TABLE,
604 rep,
605 _marker: marker::PhantomData,
606 }
607 }
608
609 /// Creates a new borrowed resource which isn't actually rooted in any
610 /// ownership.
611 ///
612 /// This can be used to pass to a guest as a borrowed resource and the
613 /// embedder will know that the `rep` won't be in use by the guest
614 /// afterwards. Exactly how the lifetime of `rep` works is up to the
615 /// embedder.
616 pub fn new_borrow(rep: u32) -> Resource<T> {
617 Resource {
618 state: AtomicResourceState::BORROW,
619 rep,
620 _marker: marker::PhantomData,
621 }
622 }
623
624 /// Returns the underlying 32-bit representation used to originally create
625 /// this resource.
626 pub fn rep(&self) -> u32 {
627 self.rep
628 }
629
630 /// Returns whether this is an owned resource or not.
631 ///
632 /// Owned resources can be safely destroyed by the embedder at any time, and
633 /// borrowed resources have an owner somewhere else on the stack so can only
634 /// be accessed, not destroyed.
635 pub fn owned(&self) -> bool {
636 match self.state.get() {
637 ResourceState::Borrow => false,
638 ResourceState::Taken | ResourceState::NotInTable | ResourceState::Index(_) => true,
639 }
640 }
641
642 fn lower_to_index<U>(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result<u32> {
643 match ty {
644 InterfaceType::Own(t) => {
645 let rep = match self.state.get() {
646 // If this is a borrow resource then this is a dynamic
647 // error on behalf of the embedder.
648 ResourceState::Borrow => {
649 bail!("cannot lower a `borrow` resource into an `own`")
650 }
651
652 // If this resource does not yet live in a table then we're
653 // dynamically transferring ownership to wasm. Record that
654 // it's no longer present and then pass through the
655 // representation.
656 ResourceState::NotInTable => {
657 let prev = self.state.swap(ResourceState::Taken);
658 assert_eq!(prev, ResourceState::NotInTable);
659 self.rep
660 }
661
662 // This resource has already been moved into wasm so this is
663 // a dynamic error on behalf of the embedder.
664 ResourceState::Taken => bail!("host resource already consumed"),
665
666 // If this resource lives in a host table then try to take
667 // it out of the table, which may fail, and on success we
668 // can move the rep into the guest table.
669 ResourceState::Index(idx) => cx.host_resource_lift_own(idx)?,
670 };
671 cx.guest_resource_lower_own(t, rep)
672 }
673 InterfaceType::Borrow(t) => {
674 let rep = match self.state.get() {
675 // If this is already a borrowed resource, nothing else to
676 // do and the rep is passed through.
677 ResourceState::Borrow => self.rep,
678
679 // If this resource is already gone, that's a dynamic error
680 // for the embedder.
681 ResourceState::Taken => bail!("host resource already consumed"),
682
683 // If this resource is not currently in a table then it
684 // needs to move into a table to participate in state
685 // related to borrow tracking. Execute the
686 // `host_resource_lower_own` operation here and update our
687 // state.
688 //
689 // Afterwards this is the same as the `idx` case below.
690 //
691 // Note that flags/dtor are passed as `None` here since
692 // `Resource<T>` doesn't offer destruction support.
693 ResourceState::NotInTable => {
694 let idx = cx.host_resource_lower_own(self.rep, None, None)?;
695 let prev = self.state.swap(ResourceState::Index(idx));
696 assert_eq!(prev, ResourceState::NotInTable);
697 cx.host_resource_lift_borrow(idx)?
698 }
699
700 // If this resource lives in a table then it needs to come
701 // out of the table with borrow-tracking employed.
702 ResourceState::Index(idx) => cx.host_resource_lift_borrow(idx)?,
703 };
704 cx.guest_resource_lower_borrow(t, rep)
705 }
706 _ => bad_type_info(),
707 }
708 }
709
710 fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result<Self> {
711 let (state, rep) = match ty {
712 // Ownership is being transferred from a guest to the host, so move
713 // it from the guest table into a new `Resource`. Note that this
714 // isn't immediately inserted into the host table and that's left
715 // for the future if it's necessary to take a borrow from this owned
716 // resource.
717 InterfaceType::Own(t) => {
718 debug_assert!(cx.resource_type(t) == ResourceType::host::<T>());
719 let (rep, dtor, flags) = cx.guest_resource_lift_own(t, index)?;
720 assert!(dtor.is_some());
721 assert!(flags.is_none());
722 (AtomicResourceState::NOT_IN_TABLE, rep)
723 }
724
725 // The borrow here is lifted from the guest, but note the lack of
726 // `host_resource_lower_borrow` as it's intentional. Lowering
727 // a borrow has a special case in the canonical ABI where if the
728 // receiving module is the owner of the resource then it directly
729 // receives the `rep` and no other dynamic tracking is employed.
730 // This effectively mirrors that even though the canonical ABI
731 // isn't really all that applicable in host context here.
732 InterfaceType::Borrow(t) => {
733 debug_assert!(cx.resource_type(t) == ResourceType::host::<T>());
734 let rep = cx.guest_resource_lift_borrow(t, index)?;
735 (AtomicResourceState::BORROW, rep)
736 }
737 _ => bad_type_info(),
738 };
739 Ok(Resource {
740 state,
741 rep,
742 _marker: marker::PhantomData,
743 })
744 }
745
746 /// Attempts to convert a [`ResourceAny`] into [`Resource`].
747 ///
748 /// This method will check that `resource` has type
749 /// `ResourceType::host::<T>()` and then convert it into a typed version of
750 /// the resource.
751 ///
752 /// # Errors
753 ///
754 /// This function will return an error if `resource` does not have type
755 /// `ResourceType::host::<T>()`. This function may also return an error if
756 /// `resource` is no longer valid, for example it was previously converted.
757 ///
758 /// # Panics
759 ///
760 /// This function will panic if `resource` does not belong to the `store`
761 /// specified.
762 pub fn try_from_resource_any(
763 resource: ResourceAny,
764 mut store: impl AsContextMut,
765 ) -> Result<Self> {
766 let store = store.as_context_mut();
767 let mut tables = HostResourceTables::new_host(store.0);
768 let ResourceAny { idx, ty, owned } = resource;
769 ensure!(ty == ResourceType::host::<T>(), "resource type mismatch");
770 let (state, rep) = if owned {
771 let rep = tables.host_resource_lift_own(idx)?;
772 (AtomicResourceState::NOT_IN_TABLE, rep)
773 } else {
774 // For borrowed handles, first acquire the `rep` via lifting the
775 // borrow. Afterwards though remove any dynamic state associated
776 // with this borrow. `Resource<T>` doesn't participate in dynamic
777 // state tracking and it's assumed embedders know what they're
778 // doing, so the drop call will clear out that a borrow is active
779 //
780 // Note that the result of `drop` should always be `None` as it's a
781 // borrowed handle, so assert so.
782 let rep = tables.host_resource_lift_borrow(idx)?;
783 let res = tables.host_resource_drop(idx)?;
784 assert!(res.is_none());
785 (AtomicResourceState::BORROW, rep)
786 };
787 Ok(Resource {
788 state,
789 rep,
790 _marker: marker::PhantomData,
791 })
792 }
793
794 /// See [`ResourceAny::try_from_resource`]
795 pub fn try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny> {
796 ResourceAny::try_from_resource(self, store)
797 }
798}
799
800unsafe impl<T: 'static> ComponentType for Resource<T> {
801 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4;
802
803 type Lower = <u32 as ComponentType>::Lower;
804
805 fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
806 let resource = match ty {
807 InterfaceType::Own(t) | InterfaceType::Borrow(t) => *t,
808 other => bail!("expected `own` or `borrow`, found `{}`", desc(other)),
809 };
810 match types.resource_type(resource).kind {
811 ResourceTypeKind::Host(id) if TypeId::of::<T>() == id => {}
812 _ => bail!("resource type mismatch"),
813 }
814
815 Ok(())
816 }
817}
818
819unsafe impl<T: 'static> Lower for Resource<T> {
820 fn lower<U>(
821 &self,
822 cx: &mut LowerContext<'_, U>,
823 ty: InterfaceType,
824 dst: &mut MaybeUninit<Self::Lower>,
825 ) -> Result<()> {
826 self.lower_to_index(cx, ty)?
827 .lower(cx, InterfaceType::U32, dst)
828 }
829
830 fn store<U>(
831 &self,
832 cx: &mut LowerContext<'_, U>,
833 ty: InterfaceType,
834 offset: usize,
835 ) -> Result<()> {
836 self.lower_to_index(cx, ty)?
837 .store(cx, InterfaceType::U32, offset)
838 }
839}
840
841unsafe impl<T: 'static> Lift for Resource<T> {
842 fn lift(cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result<Self> {
843 let index = u32::lift(cx, InterfaceType::U32, src)?;
844 Resource::lift_from_index(cx, ty, index)
845 }
846
847 fn load(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Self> {
848 let index = u32::load(cx, InterfaceType::U32, bytes)?;
849 Resource::lift_from_index(cx, ty, index)
850 }
851}
852
853impl<T> fmt::Debug for Resource<T> {
854 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
855 let state = match self.state.get() {
856 ResourceState::Borrow => "borrow",
857 ResourceState::NotInTable => "own (not in table)",
858 ResourceState::Taken => "taken",
859 ResourceState::Index(_) => "own",
860 };
861 f.debug_struct("Resource")
862 .field("rep", &self.rep)
863 .field("state", &state)
864 .finish()
865 }
866}
867
868/// Representation of a resource in the component model, either a guest-defined
869/// or a host-defined resource.
870///
871/// This type is similar to [`Resource`] except that it can be used to represent
872/// any resource, either host or guest. This type cannot be directly constructed
873/// and is only available if the guest returns it to the host (e.g. a function
874/// returning a guest-defined resource) or by a conversion from [`Resource`] via
875/// [`ResourceAny::try_from_resource`].
876/// This type also does not carry a static type parameter `T` for example and
877/// does not have as much information about its type.
878/// This means that it's possible to get runtime type-errors when
879/// using this type because it cannot statically prevent mismatching resource
880/// types.
881///
882/// Like [`Resource`] this type represents either an `own` or a `borrow`
883/// resource internally. Unlike [`Resource`], however, a [`ResourceAny`] must
884/// always be explicitly destroyed with the [`ResourceAny::resource_drop`]
885/// method. This will update internal dynamic state tracking and invoke the
886/// WebAssembly-defined destructor for a resource, if any.
887///
888/// Note that it is required to call `resource_drop` for all instances of
889/// [`ResourceAny`]: even borrows. Both borrows and own handles have state
890/// associated with them that must be discarded by the time they're done being
891/// used.
892#[derive(Debug, PartialEq, Eq, Copy, Clone)]
893pub struct ResourceAny {
894 idx: HostResourceIndex,
895 ty: ResourceType,
896 owned: bool,
897}
898
899impl ResourceAny {
900 /// Attempts to convert an imported [`Resource`] into [`ResourceAny`].
901 ///
902 /// * `resource` is the resource to convert.
903 /// * `store` is the store to place the returned resource into.
904 ///
905 /// The returned `ResourceAny` will not have a destructor attached to it
906 /// meaning that if `resource_drop` is called then it will not invoked a
907 /// host-defined destructor. This is similar to how `Resource<T>` does not
908 /// have a destructor associated with it.
909 ///
910 /// # Errors
911 ///
912 /// This method will return an error if `resource` has already been "taken"
913 /// and has ownership transferred elsewhere which can happen in situations
914 /// such as when it's already lowered into a component.
915 pub fn try_from_resource<T: 'static>(
916 resource: Resource<T>,
917 mut store: impl AsContextMut,
918 ) -> Result<Self> {
919 let Resource { rep, state, .. } = resource;
920 let store = store.as_context_mut();
921
922 let mut tables = HostResourceTables::new_host(store.0);
923 let (idx, owned) = match state.get() {
924 ResourceState::Borrow => (tables.host_resource_lower_borrow(rep)?, false),
925 ResourceState::NotInTable => {
926 let idx = tables.host_resource_lower_own(rep, None, None)?;
927 (idx, true)
928 }
929 ResourceState::Taken => bail!("host resource already consumed"),
930 ResourceState::Index(idx) => (idx, true),
931 };
932 Ok(Self {
933 idx,
934 ty: ResourceType::host::<T>(),
935 owned,
936 })
937 }
938
939 /// See [`Resource::try_from_resource_any`]
940 pub fn try_into_resource<T: 'static>(self, store: impl AsContextMut) -> Result<Resource<T>> {
941 Resource::try_from_resource_any(self, store)
942 }
943
944 /// Returns the corresponding type associated with this resource, either a
945 /// host-defined type or a guest-defined type.
946 ///
947 /// This can be compared against [`ResourceType::host`] for example to see
948 /// if it's a host-resource or against a type extracted with
949 /// [`Instance::get_resource`] to see if it's a guest-defined resource.
950 ///
951 /// [`Instance::get_resource`]: crate::component::Instance::get_resource
952 pub fn ty(&self) -> ResourceType {
953 self.ty
954 }
955
956 /// Returns whether this is an owned resource, and if not it's a borrowed
957 /// resource.
958 pub fn owned(&self) -> bool {
959 self.owned
960 }
961
962 /// Destroy this resource and release any state associated with it.
963 ///
964 /// This is required to be called (or the async version) for all instances
965 /// of [`ResourceAny`] to ensure that state associated with this resource is
966 /// properly cleaned up. For owned resources this may execute the
967 /// guest-defined destructor if applicable (or the host-defined destructor
968 /// if one was specified).
969 pub fn resource_drop(self, mut store: impl AsContextMut) -> Result<()> {
970 let mut store = store.as_context_mut();
971 assert!(
972 !store.0.async_support(),
973 "must use `resource_drop_async` when async support is enabled on the config"
974 );
975 self.resource_drop_impl(&mut store.as_context_mut())
976 }
977
978 /// Same as [`ResourceAny::resource_drop`] except for use with async stores
979 /// to execute the destructor asynchronously.
980 #[cfg(feature = "async")]
981 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
982 pub async fn resource_drop_async<T>(self, mut store: impl AsContextMut<Data = T>) -> Result<()>
983 where
984 T: Send,
985 {
986 let mut store = store.as_context_mut();
987 assert!(
988 store.0.async_support(),
989 "cannot use `resource_drop_async` without enabling async support in the config"
990 );
991 store
992 .on_fiber(|store| self.resource_drop_impl(store))
993 .await?
994 }
995
996 fn resource_drop_impl<T>(self, store: &mut StoreContextMut<'_, T>) -> Result<()> {
997 // Attempt to remove `self.idx` from the host table in `store`.
998 //
999 // This could fail if the index is invalid or if this is removing an
1000 // `Own` entry which is currently being borrowed.
1001 let pair = HostResourceTables::new_host(store.0).host_resource_drop(self.idx)?;
1002
1003 let (rep, slot) = match (pair, self.owned) {
1004 (Some(pair), true) => pair,
1005
1006 // A `borrow` was removed from the table and no further
1007 // destruction, e.g. the destructor, is required so we're done.
1008 (None, false) => return Ok(()),
1009
1010 _ => unreachable!(),
1011 };
1012
1013 // Implement the reentrance check required by the canonical ABI. Note
1014 // that this happens whether or not a destructor is present.
1015 //
1016 // Note that this should be safe because the raw pointer access in
1017 // `flags` is valid due to `store` being the owner of the flags and
1018 // flags are never destroyed within the store.
1019 if let Some(flags) = slot.flags {
1020 unsafe {
1021 if !flags.may_enter() {
1022 bail!(Trap::CannotEnterComponent);
1023 }
1024 }
1025 }
1026
1027 let dtor = match slot.dtor {
1028 Some(dtor) => dtor.as_non_null(),
1029 None => return Ok(()),
1030 };
1031 let mut args = [ValRaw::u32(rep)];
1032
1033 // This should be safe because `dtor` has been checked to belong to the
1034 // `store` provided which means it's valid and still alive. Additionally
1035 // destructors have al been previously type-checked and are guaranteed
1036 // to take one i32 argument and return no results, so the parameters
1037 // here should be configured correctly.
1038 unsafe { crate::Func::call_unchecked_raw(store, dtor, args.as_mut_ptr(), args.len()) }
1039 }
1040
1041 fn lower_to_index<U>(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result<u32> {
1042 match ty {
1043 InterfaceType::Own(t) => {
1044 if cx.resource_type(t) != self.ty {
1045 bail!("mismatched resource types");
1046 }
1047 let rep = cx.host_resource_lift_own(self.idx)?;
1048 cx.guest_resource_lower_own(t, rep)
1049 }
1050 InterfaceType::Borrow(t) => {
1051 if cx.resource_type(t) != self.ty {
1052 bail!("mismatched resource types");
1053 }
1054 let rep = cx.host_resource_lift_borrow(self.idx)?;
1055 cx.guest_resource_lower_borrow(t, rep)
1056 }
1057 _ => bad_type_info(),
1058 }
1059 }
1060
1061 fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result<Self> {
1062 match ty {
1063 InterfaceType::Own(t) => {
1064 let ty = cx.resource_type(t);
1065 let (rep, dtor, flags) = cx.guest_resource_lift_own(t, index)?;
1066 let idx = cx.host_resource_lower_own(rep, dtor, flags)?;
1067 Ok(ResourceAny {
1068 idx,
1069 ty,
1070 owned: true,
1071 })
1072 }
1073 InterfaceType::Borrow(t) => {
1074 let ty = cx.resource_type(t);
1075 let rep = cx.guest_resource_lift_borrow(t, index)?;
1076 let idx = cx.host_resource_lower_borrow(rep)?;
1077 Ok(ResourceAny {
1078 idx,
1079 ty,
1080 owned: false,
1081 })
1082 }
1083 _ => bad_type_info(),
1084 }
1085 }
1086}
1087
1088unsafe impl ComponentType for ResourceAny {
1089 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4;
1090
1091 type Lower = <u32 as ComponentType>::Lower;
1092
1093 fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> {
1094 match ty {
1095 InterfaceType::Own(_) | InterfaceType::Borrow(_) => Ok(()),
1096 other => bail!("expected `own` or `borrow`, found `{}`", desc(other)),
1097 }
1098 }
1099}
1100
1101unsafe impl Lower for ResourceAny {
1102 fn lower<T>(
1103 &self,
1104 cx: &mut LowerContext<'_, T>,
1105 ty: InterfaceType,
1106 dst: &mut MaybeUninit<Self::Lower>,
1107 ) -> Result<()> {
1108 self.lower_to_index(cx, ty)?
1109 .lower(cx, InterfaceType::U32, dst)
1110 }
1111
1112 fn store<T>(
1113 &self,
1114 cx: &mut LowerContext<'_, T>,
1115 ty: InterfaceType,
1116 offset: usize,
1117 ) -> Result<()> {
1118 self.lower_to_index(cx, ty)?
1119 .store(cx, InterfaceType::U32, offset)
1120 }
1121}
1122
1123unsafe impl Lift for ResourceAny {
1124 fn lift(cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result<Self> {
1125 let index = u32::lift(cx, InterfaceType::U32, src)?;
1126 ResourceAny::lift_from_index(cx, ty, index)
1127 }
1128
1129 fn load(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Self> {
1130 let index = u32::load(cx, InterfaceType::U32, bytes)?;
1131 ResourceAny::lift_from_index(cx, ty, index)
1132 }
1133}