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::prelude::*;
5use crate::runtime::vm::component::{ComponentInstance, InstanceFlags, ResourceTables};
6use crate::runtime::vm::{SendSyncPtr, VMFuncRef, ValRaw};
7use crate::store::{StoreId, StoreOpaque};
8use crate::{AsContextMut, StoreContextMut, Trap};
9use core::any::TypeId;
10use core::fmt;
11use core::marker;
12use core::mem::MaybeUninit;
13use core::ptr::NonNull;
14use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
15use wasmtime_environ::component::{
16 CanonicalAbiInfo, ComponentTypes, DefinedResourceIndex, InterfaceType, ResourceIndex,
17 TypeResourceTableIndex,
18};
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 two `AtomicU32` fields but it's not intended to actually
252/// be used in conjunction with threads as generally a `Store<T>` lives on one
253/// thread at a time. The pair of `AtomicU32` here is used to ensure that this
254/// type is `Send + Sync` when captured as a reference to make async
255/// programming more ergonomic.
256///
257/// Also note that two `AtomicU32` here are used instead of `AtomicU64` to be
258/// more portable to platforms without 64-bit atomics.
259struct AtomicResourceState {
260 index: AtomicU32,
261 generation: AtomicU32,
262}
263
264#[derive(Debug, PartialEq, Eq, Copy, Clone)]
265enum ResourceState {
266 Borrow,
267 NotInTable,
268 Taken,
269 Index(HostResourceIndex),
270}
271
272impl AtomicResourceState {
273 const BORROW: Self = AtomicResourceState::new(ResourceState::Borrow);
274 const NOT_IN_TABLE: Self = AtomicResourceState::new(ResourceState::NotInTable);
275
276 const fn new(state: ResourceState) -> AtomicResourceState {
277 let (index, generation) = state.encode();
278 Self {
279 index: AtomicU32::new(index),
280 generation: AtomicU32::new(generation),
281 }
282 }
283
284 fn get(&self) -> ResourceState {
285 ResourceState::decode(self.index.load(Relaxed), self.generation.load(Relaxed))
286 }
287
288 fn swap(&self, state: ResourceState) -> ResourceState {
289 let (index, generation) = state.encode();
290 let index_prev = self.index.load(Relaxed);
291 self.index.store(index, Relaxed);
292 let generation_prev = self.generation.load(Relaxed);
293 self.generation.store(generation, Relaxed);
294 ResourceState::decode(index_prev, generation_prev)
295 }
296}
297
298impl ResourceState {
299 // See comments on `state` above for info about these values.
300 const BORROW: u32 = u32::MAX;
301 const NOT_IN_TABLE: u32 = u32::MAX - 1;
302 const TAKEN: u32 = u32::MAX - 2;
303
304 fn decode(idx: u32, generation: u32) -> ResourceState {
305 match generation {
306 Self::BORROW => Self::Borrow,
307 Self::NOT_IN_TABLE => Self::NotInTable,
308 Self::TAKEN => Self::Taken,
309 _ => Self::Index(HostResourceIndex::new(idx, generation)),
310 }
311 }
312
313 const fn encode(&self) -> (u32, u32) {
314 match self {
315 Self::Borrow => (0, Self::BORROW),
316 Self::NotInTable => (0, Self::NOT_IN_TABLE),
317 Self::Taken => (0, Self::TAKEN),
318 Self::Index(index) => (index.index(), index.generation()),
319 }
320 }
321}
322
323/// Metadata tracking the state of resources within a `Store`.
324///
325/// This is a borrowed structure created from a `Store` piecemeal from below.
326/// The `ResourceTables` type holds most of the raw information and this
327/// structure tacks on a reference to `HostResourceData` to track generation
328/// numbers of host indices.
329pub struct HostResourceTables<'a> {
330 tables: ResourceTables<'a>,
331 host_resource_data: &'a mut HostResourceData,
332}
333
334/// Metadata for host-owned resources owned within a `Store`.
335///
336/// This metadata is used to prevent the ABA problem with indices handed out as
337/// part of `Resource` and `ResourceAny`. Those structures are `Copy` meaning
338/// that it's easy to reuse them, possibly accidentally. To prevent issues in
339/// the host Wasmtime attaches both an index (within `ResourceTables`) as well
340/// as a 32-bit generation counter onto each `HostResourceIndex` which the host
341/// actually holds in `Resource` and `ResourceAny`.
342///
343/// This structure holds a list which is a parallel list to the "list of reps"
344/// that's stored within `ResourceTables` elsewhere in the `Store`. This
345/// parallel list holds the last known generation of each element in the table.
346/// The generation is then compared on access to make sure it's the same.
347///
348/// Whenever a slot in the table is allocated the `cur_generation` field is
349/// pushed at the corresponding index of `generation_of_table_slot`. Whenever
350/// a field is accessed the current value of `generation_of_table_slot` is
351/// checked against the generation of the index. Whenever a slot is deallocated
352/// the generation is incremented. Put together this means that any access of a
353/// deallocated slot should deterministically provide an error.
354#[derive(Default)]
355pub struct HostResourceData {
356 cur_generation: u32,
357 table_slot_metadata: Vec<TableSlot>,
358}
359
360#[derive(Copy, Clone)]
361struct TableSlot {
362 generation: u32,
363 flags: Option<InstanceFlags>,
364 dtor: Option<SendSyncPtr<VMFuncRef>>,
365}
366
367/// Host representation of an index into a table slot.
368///
369/// This is morally (u32, u32) but is encoded as a 64-bit integer. The low
370/// 32-bits are the table index and the upper 32-bits are the generation
371/// counter.
372#[derive(PartialEq, Eq, Debug, Copy, Clone)]
373#[repr(transparent)]
374pub struct HostResourceIndex(u64);
375
376impl HostResourceIndex {
377 fn new(idx: u32, generation: u32) -> HostResourceIndex {
378 HostResourceIndex(u64::from(idx) | (u64::from(generation) << 32))
379 }
380
381 const fn index(&self) -> u32 {
382 (self.0 & 0xffffffff) as u32
383 }
384
385 const fn generation(&self) -> u32 {
386 (self.0 >> 32) as u32
387 }
388}
389
390impl<'a> HostResourceTables<'a> {
391 pub fn new_host(store: &'a mut StoreOpaque) -> HostResourceTables<'a> {
392 let (calls, host_table, host_resource_data) = store.component_resource_state();
393 HostResourceTables::from_parts(
394 ResourceTables {
395 host_table: Some(host_table),
396 calls,
397 tables: None,
398 },
399 host_resource_data,
400 )
401 }
402
403 pub fn from_parts(
404 tables: ResourceTables<'a>,
405 host_resource_data: &'a mut HostResourceData,
406 ) -> Self {
407 HostResourceTables {
408 tables,
409 host_resource_data,
410 }
411 }
412
413 /// Lifts an `own` resource that resides in the host's tables at the `idx`
414 /// specified into its `rep`.
415 ///
416 /// # Errors
417 ///
418 /// Returns an error if `idx` doesn't point to a valid owned resource, or
419 /// if `idx` can't be lifted as an `own` (e.g. it has active borrows).
420 pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> {
421 let (idx, _) = self.validate_host_index(idx, true)?;
422 self.tables.resource_lift_own(None, idx)
423 }
424
425 /// See [`HostResourceTables::host_resource_lift_own`].
426 pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> {
427 let (idx, _) = self.validate_host_index(idx, false)?;
428 self.tables.resource_lift_borrow(None, idx)
429 }
430
431 /// Lowers an `own` resource to be owned by the host.
432 ///
433 /// This returns a new index into the host's set of resource tables which
434 /// will point to the `rep` specified. The returned index is suitable for
435 /// conversion into either [`Resource`] or [`ResourceAny`].
436 ///
437 /// The `dtor` and instance `flags` are specified as well to know what
438 /// destructor to run when this resource is destroyed.
439 pub fn host_resource_lower_own(
440 &mut self,
441 rep: u32,
442 dtor: Option<NonNull<VMFuncRef>>,
443 flags: Option<InstanceFlags>,
444 ) -> Result<HostResourceIndex> {
445 let idx = self.tables.resource_lower_own(None, rep)?;
446 Ok(self.new_host_index(idx, dtor, flags))
447 }
448
449 /// See [`HostResourceTables::host_resource_lower_own`].
450 pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> {
451 let idx = self.tables.resource_lower_borrow(None, rep)?;
452 Ok(self.new_host_index(idx, None, None))
453 }
454
455 /// Validates that `idx` is still valid for the host tables, notably
456 /// ensuring that the generation listed in `idx` is the same as the
457 /// last recorded generation of the slot itself.
458 ///
459 /// The `is_removal` option indicates whether or not this table access will
460 /// end up removing the element from the host table. In such a situation the
461 /// current generation number is incremented.
462 fn validate_host_index(
463 &mut self,
464 idx: HostResourceIndex,
465 is_removal: bool,
466 ) -> Result<(u32, Option<TableSlot>)> {
467 let actual = usize::try_from(idx.index())
468 .ok()
469 .and_then(|i| self.host_resource_data.table_slot_metadata.get(i).copied());
470
471 // If `idx` is out-of-bounds then skip returning an error. In such a
472 // situation the operation that this is guarding will return a more
473 // precise error, such as a lift operation.
474 if let Some(actual) = actual {
475 if actual.generation != idx.generation() {
476 bail!("host-owned resource is being used with the wrong type");
477 }
478 }
479
480 // Bump the current generation of this is a removal to ensure any
481 // future item placed in this slot can't be pointed to by the `idx`
482 // provided above.
483 if is_removal {
484 self.host_resource_data.cur_generation += 1;
485 }
486
487 Ok((idx.index(), actual))
488 }
489
490 /// Creates a new `HostResourceIndex` which will point to the raw table
491 /// slot provided by `idx`.
492 ///
493 /// This will register metadata necessary to track the current generation
494 /// in the returned `HostResourceIndex` as well.
495 fn new_host_index(
496 &mut self,
497 idx: u32,
498 dtor: Option<NonNull<VMFuncRef>>,
499 flags: Option<InstanceFlags>,
500 ) -> HostResourceIndex {
501 let list = &mut self.host_resource_data.table_slot_metadata;
502 let info = TableSlot {
503 generation: self.host_resource_data.cur_generation,
504 flags,
505 dtor: dtor.map(SendSyncPtr::new),
506 };
507 match list.get_mut(idx as usize) {
508 Some(slot) => *slot = info,
509 None => {
510 // Resource handles start at 1, not zero, so push two elements
511 // for the first resource handle.
512 if list.is_empty() {
513 assert_eq!(idx, 1);
514 list.push(TableSlot {
515 generation: 0,
516 flags: None,
517 dtor: None,
518 });
519 }
520 assert_eq!(idx as usize, list.len());
521 list.push(info);
522 }
523 }
524
525 HostResourceIndex::new(idx, info.generation)
526 }
527
528 /// Drops a host-owned resource from host tables.
529 ///
530 /// This method will attempt to interpret `idx` as pointing to either a
531 /// `borrow` or `own` resource with the `expected` type specified. This
532 /// method will then return the underlying `rep` if it points to an `own`
533 /// resource which can then be further processed for destruction.
534 ///
535 /// # Errors
536 ///
537 /// Returns an error if `idx` doesn't point to a valid resource, points to
538 /// an `own` with active borrows, or if it doesn't have the type `expected`
539 /// in the host tables.
540 fn host_resource_drop(&mut self, idx: HostResourceIndex) -> Result<Option<(u32, TableSlot)>> {
541 let (idx, slot) = self.validate_host_index(idx, true)?;
542 match self.tables.resource_drop(None, idx)? {
543 Some(rep) => Ok(Some((rep, slot.unwrap()))),
544 None => Ok(None),
545 }
546 }
547
548 /// Lowers an `own` resource into the guest, converting the `rep` specified
549 /// into a guest-local index.
550 ///
551 /// The `ty` provided is which table to put this into.
552 pub fn guest_resource_lower_own(
553 &mut self,
554 rep: u32,
555 ty: TypeResourceTableIndex,
556 ) -> Result<u32> {
557 self.tables.resource_lower_own(Some(ty), rep)
558 }
559
560 /// Lowers a `borrow` resource into the guest, converting the `rep`
561 /// specified into a guest-local index.
562 ///
563 /// The `ty` provided is which table to put this into.
564 ///
565 /// Note that this cannot be used in isolation because lowering a borrow
566 /// into a guest has a special case where `rep` is returned directly if `ty`
567 /// belongs to the component being lowered into. That property must be
568 /// handled by the caller of this function.
569 pub fn guest_resource_lower_borrow(
570 &mut self,
571 rep: u32,
572 ty: TypeResourceTableIndex,
573 ) -> Result<u32> {
574 self.tables.resource_lower_borrow(Some(ty), rep)
575 }
576
577 /// Lifts an `own` resource from the `idx` specified from the table `ty`.
578 ///
579 /// This will lookup the appropriate table in the guest and return the `rep`
580 /// corresponding to `idx` if it's valid.
581 pub fn guest_resource_lift_own(&mut self, idx: u32, ty: TypeResourceTableIndex) -> Result<u32> {
582 self.tables.resource_lift_own(Some(ty), idx)
583 }
584
585 /// Lifts a `borrow` resource from the `idx` specified from the table `ty`.
586 ///
587 /// This will lookup the appropriate table in the guest and return the `rep`
588 /// corresponding to `idx` if it's valid.
589 pub fn guest_resource_lift_borrow(
590 &mut self,
591 idx: u32,
592 ty: TypeResourceTableIndex,
593 ) -> Result<u32> {
594 self.tables.resource_lift_borrow(Some(ty), idx)
595 }
596
597 /// Begins a call into the component instance, starting recording of
598 /// metadata related to resource borrowing.
599 #[inline]
600 pub fn enter_call(&mut self) {
601 self.tables.enter_call()
602 }
603
604 /// Completes a call into the component instance, validating that it's ok to
605 /// complete by ensuring the are no remaining active borrows.
606 #[inline]
607 pub fn exit_call(&mut self) -> Result<()> {
608 self.tables.exit_call()
609 }
610}
611
612impl<T> Resource<T>
613where
614 T: 'static,
615{
616 /// Creates a new owned resource with the `rep` specified.
617 ///
618 /// The returned value is suitable for passing to a guest as either a
619 /// `(borrow $t)` or `(own $t)`.
620 pub fn new_own(rep: u32) -> Resource<T> {
621 Resource {
622 state: AtomicResourceState::NOT_IN_TABLE,
623 rep,
624 _marker: marker::PhantomData,
625 }
626 }
627
628 /// Creates a new borrowed resource which isn't actually rooted in any
629 /// ownership.
630 ///
631 /// This can be used to pass to a guest as a borrowed resource and the
632 /// embedder will know that the `rep` won't be in use by the guest
633 /// afterwards. Exactly how the lifetime of `rep` works is up to the
634 /// embedder.
635 pub fn new_borrow(rep: u32) -> Resource<T> {
636 Resource {
637 state: AtomicResourceState::BORROW,
638 rep,
639 _marker: marker::PhantomData,
640 }
641 }
642
643 /// Returns the underlying 32-bit representation used to originally create
644 /// this resource.
645 pub fn rep(&self) -> u32 {
646 self.rep
647 }
648
649 /// Returns whether this is an owned resource or not.
650 ///
651 /// Owned resources can be safely destroyed by the embedder at any time, and
652 /// borrowed resources have an owner somewhere else on the stack so can only
653 /// be accessed, not destroyed.
654 pub fn owned(&self) -> bool {
655 match self.state.get() {
656 ResourceState::Borrow => false,
657 ResourceState::Taken | ResourceState::NotInTable | ResourceState::Index(_) => true,
658 }
659 }
660
661 fn lower_to_index<U>(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result<u32> {
662 match ty {
663 InterfaceType::Own(t) => {
664 let rep = match self.state.get() {
665 // If this is a borrow resource then this is a dynamic
666 // error on behalf of the embedder.
667 ResourceState::Borrow => {
668 bail!("cannot lower a `borrow` resource into an `own`")
669 }
670
671 // If this resource does not yet live in a table then we're
672 // dynamically transferring ownership to wasm. Record that
673 // it's no longer present and then pass through the
674 // representation.
675 ResourceState::NotInTable => {
676 let prev = self.state.swap(ResourceState::Taken);
677 assert_eq!(prev, ResourceState::NotInTable);
678 self.rep
679 }
680
681 // This resource has already been moved into wasm so this is
682 // a dynamic error on behalf of the embedder.
683 ResourceState::Taken => bail!("host resource already consumed"),
684
685 // If this resource lives in a host table then try to take
686 // it out of the table, which may fail, and on success we
687 // can move the rep into the guest table.
688 ResourceState::Index(idx) => cx.host_resource_lift_own(idx)?,
689 };
690 cx.guest_resource_lower_own(t, rep)
691 }
692 InterfaceType::Borrow(t) => {
693 let rep = match self.state.get() {
694 // If this is already a borrowed resource, nothing else to
695 // do and the rep is passed through.
696 ResourceState::Borrow => self.rep,
697
698 // If this resource is already gone, that's a dynamic error
699 // for the embedder.
700 ResourceState::Taken => bail!("host resource already consumed"),
701
702 // If this resource is not currently in a table then it
703 // needs to move into a table to participate in state
704 // related to borrow tracking. Execute the
705 // `host_resource_lower_own` operation here and update our
706 // state.
707 //
708 // Afterwards this is the same as the `idx` case below.
709 //
710 // Note that flags/dtor are passed as `None` here since
711 // `Resource<T>` doesn't offer destruction support.
712 ResourceState::NotInTable => {
713 let idx = cx.host_resource_lower_own(self.rep, None, None)?;
714 let prev = self.state.swap(ResourceState::Index(idx));
715 assert_eq!(prev, ResourceState::NotInTable);
716 cx.host_resource_lift_borrow(idx)?
717 }
718
719 // If this resource lives in a table then it needs to come
720 // out of the table with borrow-tracking employed.
721 ResourceState::Index(idx) => cx.host_resource_lift_borrow(idx)?,
722 };
723 cx.guest_resource_lower_borrow(t, rep)
724 }
725 _ => bad_type_info(),
726 }
727 }
728
729 fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result<Self> {
730 let (state, rep) = match ty {
731 // Ownership is being transferred from a guest to the host, so move
732 // it from the guest table into a new `Resource`. Note that this
733 // isn't immediately inserted into the host table and that's left
734 // for the future if it's necessary to take a borrow from this owned
735 // resource.
736 InterfaceType::Own(t) => {
737 debug_assert!(cx.resource_type(t) == ResourceType::host::<T>());
738 let (rep, dtor, flags) = cx.guest_resource_lift_own(t, index)?;
739 assert!(dtor.is_some());
740 assert!(flags.is_none());
741 (AtomicResourceState::NOT_IN_TABLE, rep)
742 }
743
744 // The borrow here is lifted from the guest, but note the lack of
745 // `host_resource_lower_borrow` as it's intentional. Lowering
746 // a borrow has a special case in the canonical ABI where if the
747 // receiving module is the owner of the resource then it directly
748 // receives the `rep` and no other dynamic tracking is employed.
749 // This effectively mirrors that even though the canonical ABI
750 // isn't really all that applicable in host context here.
751 InterfaceType::Borrow(t) => {
752 debug_assert!(cx.resource_type(t) == ResourceType::host::<T>());
753 let rep = cx.guest_resource_lift_borrow(t, index)?;
754 (AtomicResourceState::BORROW, rep)
755 }
756 _ => bad_type_info(),
757 };
758 Ok(Resource {
759 state,
760 rep,
761 _marker: marker::PhantomData,
762 })
763 }
764
765 /// Attempts to convert a [`ResourceAny`] into [`Resource`].
766 ///
767 /// This method will check that `resource` has type
768 /// `ResourceType::host::<T>()` and then convert it into a typed version of
769 /// the resource.
770 ///
771 /// # Errors
772 ///
773 /// This function will return an error if `resource` does not have type
774 /// `ResourceType::host::<T>()`. This function may also return an error if
775 /// `resource` is no longer valid, for example it was previously converted.
776 ///
777 /// # Panics
778 ///
779 /// This function will panic if `resource` does not belong to the `store`
780 /// specified.
781 pub fn try_from_resource_any(
782 resource: ResourceAny,
783 mut store: impl AsContextMut,
784 ) -> Result<Self> {
785 let store = store.as_context_mut();
786 let mut tables = HostResourceTables::new_host(store.0);
787 let ResourceAny { idx, ty, owned } = resource;
788 ensure!(ty == ResourceType::host::<T>(), "resource type mismatch");
789 let (state, rep) = if owned {
790 let rep = tables.host_resource_lift_own(idx)?;
791 (AtomicResourceState::NOT_IN_TABLE, rep)
792 } else {
793 // For borrowed handles, first acquire the `rep` via lifting the
794 // borrow. Afterwards though remove any dynamic state associated
795 // with this borrow. `Resource<T>` doesn't participate in dynamic
796 // state tracking and it's assumed embedders know what they're
797 // doing, so the drop call will clear out that a borrow is active
798 //
799 // Note that the result of `drop` should always be `None` as it's a
800 // borrowed handle, so assert so.
801 let rep = tables.host_resource_lift_borrow(idx)?;
802 let res = tables.host_resource_drop(idx)?;
803 assert!(res.is_none());
804 (AtomicResourceState::BORROW, rep)
805 };
806 Ok(Resource {
807 state,
808 rep,
809 _marker: marker::PhantomData,
810 })
811 }
812
813 /// See [`ResourceAny::try_from_resource`]
814 pub fn try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny> {
815 ResourceAny::try_from_resource(self, store)
816 }
817}
818
819unsafe impl<T: 'static> ComponentType for Resource<T> {
820 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4;
821
822 type Lower = <u32 as ComponentType>::Lower;
823
824 fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
825 let resource = match ty {
826 InterfaceType::Own(t) | InterfaceType::Borrow(t) => *t,
827 other => bail!("expected `own` or `borrow`, found `{}`", desc(other)),
828 };
829 match types.resource_type(resource).kind {
830 ResourceTypeKind::Host(id) if TypeId::of::<T>() == id => {}
831 _ => bail!("resource type mismatch"),
832 }
833
834 Ok(())
835 }
836}
837
838unsafe impl<T: 'static> Lower for Resource<T> {
839 fn lower<U>(
840 &self,
841 cx: &mut LowerContext<'_, U>,
842 ty: InterfaceType,
843 dst: &mut MaybeUninit<Self::Lower>,
844 ) -> Result<()> {
845 self.lower_to_index(cx, ty)?
846 .lower(cx, InterfaceType::U32, dst)
847 }
848
849 fn store<U>(
850 &self,
851 cx: &mut LowerContext<'_, U>,
852 ty: InterfaceType,
853 offset: usize,
854 ) -> Result<()> {
855 self.lower_to_index(cx, ty)?
856 .store(cx, InterfaceType::U32, offset)
857 }
858}
859
860unsafe impl<T: 'static> Lift for Resource<T> {
861 fn lift(cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result<Self> {
862 let index = u32::lift(cx, InterfaceType::U32, src)?;
863 Resource::lift_from_index(cx, ty, index)
864 }
865
866 fn load(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Self> {
867 let index = u32::load(cx, InterfaceType::U32, bytes)?;
868 Resource::lift_from_index(cx, ty, index)
869 }
870}
871
872impl<T> fmt::Debug for Resource<T> {
873 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
874 let state = match self.state.get() {
875 ResourceState::Borrow => "borrow",
876 ResourceState::NotInTable => "own (not in table)",
877 ResourceState::Taken => "taken",
878 ResourceState::Index(_) => "own",
879 };
880 f.debug_struct("Resource")
881 .field("rep", &self.rep)
882 .field("state", &state)
883 .finish()
884 }
885}
886
887/// Representation of a resource in the component model, either a guest-defined
888/// or a host-defined resource.
889///
890/// This type is similar to [`Resource`] except that it can be used to represent
891/// any resource, either host or guest. This type cannot be directly constructed
892/// and is only available if the guest returns it to the host (e.g. a function
893/// returning a guest-defined resource) or by a conversion from [`Resource`] via
894/// [`ResourceAny::try_from_resource`].
895/// This type also does not carry a static type parameter `T` for example and
896/// does not have as much information about its type.
897/// This means that it's possible to get runtime type-errors when
898/// using this type because it cannot statically prevent mismatching resource
899/// types.
900///
901/// Like [`Resource`] this type represents either an `own` or a `borrow`
902/// resource internally. Unlike [`Resource`], however, a [`ResourceAny`] must
903/// always be explicitly destroyed with the [`ResourceAny::resource_drop`]
904/// method. This will update internal dynamic state tracking and invoke the
905/// WebAssembly-defined destructor for a resource, if any.
906///
907/// Note that it is required to call `resource_drop` for all instances of
908/// [`ResourceAny`]: even borrows. Both borrows and own handles have state
909/// associated with them that must be discarded by the time they're done being
910/// used.
911#[derive(Debug, PartialEq, Eq, Copy, Clone)]
912pub struct ResourceAny {
913 idx: HostResourceIndex,
914 ty: ResourceType,
915 owned: bool,
916}
917
918impl ResourceAny {
919 /// Attempts to convert an imported [`Resource`] into [`ResourceAny`].
920 ///
921 /// * `resource` is the resource to convert.
922 /// * `store` is the store to place the returned resource into.
923 ///
924 /// The returned `ResourceAny` will not have a destructor attached to it
925 /// meaning that if `resource_drop` is called then it will not invoked a
926 /// host-defined destructor. This is similar to how `Resource<T>` does not
927 /// have a destructor associated with it.
928 ///
929 /// # Errors
930 ///
931 /// This method will return an error if `resource` has already been "taken"
932 /// and has ownership transferred elsewhere which can happen in situations
933 /// such as when it's already lowered into a component.
934 pub fn try_from_resource<T: 'static>(
935 resource: Resource<T>,
936 mut store: impl AsContextMut,
937 ) -> Result<Self> {
938 let Resource { rep, state, .. } = resource;
939 let store = store.as_context_mut();
940
941 let mut tables = HostResourceTables::new_host(store.0);
942 let (idx, owned) = match state.get() {
943 ResourceState::Borrow => (tables.host_resource_lower_borrow(rep)?, false),
944 ResourceState::NotInTable => {
945 let idx = tables.host_resource_lower_own(rep, None, None)?;
946 (idx, true)
947 }
948 ResourceState::Taken => bail!("host resource already consumed"),
949 ResourceState::Index(idx) => (idx, true),
950 };
951 Ok(Self {
952 idx,
953 ty: ResourceType::host::<T>(),
954 owned,
955 })
956 }
957
958 /// See [`Resource::try_from_resource_any`]
959 pub fn try_into_resource<T: 'static>(self, store: impl AsContextMut) -> Result<Resource<T>> {
960 Resource::try_from_resource_any(self, store)
961 }
962
963 /// Returns the corresponding type associated with this resource, either a
964 /// host-defined type or a guest-defined type.
965 ///
966 /// This can be compared against [`ResourceType::host`] for example to see
967 /// if it's a host-resource or against a type extracted with
968 /// [`Instance::get_resource`] to see if it's a guest-defined resource.
969 ///
970 /// [`Instance::get_resource`]: crate::component::Instance::get_resource
971 pub fn ty(&self) -> ResourceType {
972 self.ty
973 }
974
975 /// Returns whether this is an owned resource, and if not it's a borrowed
976 /// resource.
977 pub fn owned(&self) -> bool {
978 self.owned
979 }
980
981 /// Destroy this resource and release any state associated with it.
982 ///
983 /// This is required to be called (or the async version) for all instances
984 /// of [`ResourceAny`] to ensure that state associated with this resource is
985 /// properly cleaned up. For owned resources this may execute the
986 /// guest-defined destructor if applicable (or the host-defined destructor
987 /// if one was specified).
988 pub fn resource_drop(self, mut store: impl AsContextMut) -> Result<()> {
989 let mut store = store.as_context_mut();
990 assert!(
991 !store.0.async_support(),
992 "must use `resource_drop_async` when async support is enabled on the config"
993 );
994 self.resource_drop_impl(&mut store.as_context_mut())
995 }
996
997 /// Same as [`ResourceAny::resource_drop`] except for use with async stores
998 /// to execute the destructor asynchronously.
999 #[cfg(feature = "async")]
1000 pub async fn resource_drop_async<T>(self, mut store: impl AsContextMut<Data = T>) -> Result<()>
1001 where
1002 T: Send,
1003 {
1004 let mut store = store.as_context_mut();
1005 assert!(
1006 store.0.async_support(),
1007 "cannot use `resource_drop_async` without enabling async support in the config"
1008 );
1009 store
1010 .on_fiber(|store| self.resource_drop_impl(store))
1011 .await?
1012 }
1013
1014 fn resource_drop_impl<T>(self, store: &mut StoreContextMut<'_, T>) -> Result<()> {
1015 // Attempt to remove `self.idx` from the host table in `store`.
1016 //
1017 // This could fail if the index is invalid or if this is removing an
1018 // `Own` entry which is currently being borrowed.
1019 let pair = HostResourceTables::new_host(store.0).host_resource_drop(self.idx)?;
1020
1021 let (rep, slot) = match (pair, self.owned) {
1022 (Some(pair), true) => pair,
1023
1024 // A `borrow` was removed from the table and no further
1025 // destruction, e.g. the destructor, is required so we're done.
1026 (None, false) => return Ok(()),
1027
1028 _ => unreachable!(),
1029 };
1030
1031 // Implement the reentrance check required by the canonical ABI. Note
1032 // that this happens whether or not a destructor is present.
1033 //
1034 // Note that this should be safe because the raw pointer access in
1035 // `flags` is valid due to `store` being the owner of the flags and
1036 // flags are never destroyed within the store.
1037 if let Some(flags) = slot.flags {
1038 unsafe {
1039 if !flags.may_enter() {
1040 bail!(Trap::CannotEnterComponent);
1041 }
1042 }
1043 }
1044
1045 let dtor = match slot.dtor {
1046 Some(dtor) => dtor.as_non_null(),
1047 None => return Ok(()),
1048 };
1049 let mut args = [ValRaw::u32(rep)];
1050
1051 // This should be safe because `dtor` has been checked to belong to the
1052 // `store` provided which means it's valid and still alive. Additionally
1053 // destructors have al been previously type-checked and are guaranteed
1054 // to take one i32 argument and return no results, so the parameters
1055 // here should be configured correctly.
1056 unsafe { crate::Func::call_unchecked_raw(store, dtor, NonNull::from(&mut args)) }
1057 }
1058
1059 fn lower_to_index<U>(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result<u32> {
1060 match ty {
1061 InterfaceType::Own(t) => {
1062 if cx.resource_type(t) != self.ty {
1063 bail!("mismatched resource types");
1064 }
1065 let rep = cx.host_resource_lift_own(self.idx)?;
1066 cx.guest_resource_lower_own(t, rep)
1067 }
1068 InterfaceType::Borrow(t) => {
1069 if cx.resource_type(t) != self.ty {
1070 bail!("mismatched resource types");
1071 }
1072 let rep = cx.host_resource_lift_borrow(self.idx)?;
1073 cx.guest_resource_lower_borrow(t, rep)
1074 }
1075 _ => bad_type_info(),
1076 }
1077 }
1078
1079 fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result<Self> {
1080 match ty {
1081 InterfaceType::Own(t) => {
1082 let ty = cx.resource_type(t);
1083 let (rep, dtor, flags) = cx.guest_resource_lift_own(t, index)?;
1084 let idx = cx.host_resource_lower_own(rep, dtor, flags)?;
1085 Ok(ResourceAny {
1086 idx,
1087 ty,
1088 owned: true,
1089 })
1090 }
1091 InterfaceType::Borrow(t) => {
1092 let ty = cx.resource_type(t);
1093 let rep = cx.guest_resource_lift_borrow(t, index)?;
1094 let idx = cx.host_resource_lower_borrow(rep)?;
1095 Ok(ResourceAny {
1096 idx,
1097 ty,
1098 owned: false,
1099 })
1100 }
1101 _ => bad_type_info(),
1102 }
1103 }
1104}
1105
1106unsafe impl ComponentType for ResourceAny {
1107 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4;
1108
1109 type Lower = <u32 as ComponentType>::Lower;
1110
1111 fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> {
1112 match ty {
1113 InterfaceType::Own(_) | InterfaceType::Borrow(_) => Ok(()),
1114 other => bail!("expected `own` or `borrow`, found `{}`", desc(other)),
1115 }
1116 }
1117}
1118
1119unsafe impl Lower for ResourceAny {
1120 fn lower<T>(
1121 &self,
1122 cx: &mut LowerContext<'_, T>,
1123 ty: InterfaceType,
1124 dst: &mut MaybeUninit<Self::Lower>,
1125 ) -> Result<()> {
1126 self.lower_to_index(cx, ty)?
1127 .lower(cx, InterfaceType::U32, dst)
1128 }
1129
1130 fn store<T>(
1131 &self,
1132 cx: &mut LowerContext<'_, T>,
1133 ty: InterfaceType,
1134 offset: usize,
1135 ) -> Result<()> {
1136 self.lower_to_index(cx, ty)?
1137 .store(cx, InterfaceType::U32, offset)
1138 }
1139}
1140
1141unsafe impl Lift for ResourceAny {
1142 fn lift(cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result<Self> {
1143 let index = u32::lift(cx, InterfaceType::U32, src)?;
1144 ResourceAny::lift_from_index(cx, ty, index)
1145 }
1146
1147 fn load(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Self> {
1148 let index = u32::load(cx, InterfaceType::U32, bytes)?;
1149 ResourceAny::lift_from_index(cx, ty, index)
1150 }
1151}