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