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