1 use crate::AsContextMut;
2 use crate::component::func::{LiftContext, LowerContext};
3 use crate::component::matching::InstanceType;
4 use crate::component::resources::host::{HostResource, HostResourceType};
5 use crate::component::{ComponentType, Lift, Lower, ResourceAny, ResourceType};
6 use crate::prelude::*;
7 use core::fmt;
8 use core::mem::MaybeUninit;
9 use wasmtime_environ::component::{CanonicalAbiInfo, InterfaceType};
10 
11 /// A host-defined resource in the component model with a dynamic runtime value
12 /// representing its type.
13 ///
14 /// This type represents a host-owned resource in the same manner as
15 /// [`Resource`], and almost all of the documentation on that type is applicable
16 /// to usage of this type as well. Where the two differ is how embedders use
17 /// these types in a particular embedding.
18 ///
19 /// # Use cases for [`Resource`]
20 ///
21 /// The [`Resource`] type is intended to be used by Rust embedders and provides
22 /// a `T` type parameter to provide a layer of type safety when using the
23 /// resource. The type parameter prevents mixing up resources of the same type
24 /// by accident. The [`bindgen!`] macro, for example, uses [`Resource`] by
25 /// default to represent all resources imported by a wasm guest.
26 ///
27 /// As the documentation on [`Resource`] indicates the `T` type parameter on
28 /// [`Resource<T>`] is a hint and `T` isn't stored inside. This means that the
29 /// host can write a function which takes [`Resource<T>`], for example, and
30 /// prevent mistakes of passing a wrong-typed-resource to that function.
31 /// Typically the 32-bit value that [`Resource<T>`] wraps is an index into some
32 /// sort of table the host manages and the index points to a value of type `T`.
33 /// The `T` type parameter can assist in writing helper functions to access
34 /// these types.
35 ///
36 /// The downside of [`Resource`], however, is that all resource types must be
37 /// statically assigned at compile time. It's not possible to manufacture more
38 /// types at runtime in some more dynamic situations. That's where
39 /// [`ResourceDynamic`] comes in.
40 ///
41 /// # Use cases for [`ResourceDynamic`]
42 ///
43 /// The general idea of [`ResourceDynamic`] is very similar to [`Resource`] --
44 /// it represents a "trusted" 32-bit value that the host defines and assigns
45 /// meaning to. There is no destructor on [`ResourceDynamic`] and the host has
46 /// to know how to destroy the associated state, if any, that
47 /// [`ResourceDynamic`] references. The difference with [`Resource`] is that is
48 /// has runtime type information instead of static type information, meaning
49 /// that it's possible to mix these up at compile by accident.
50 ///
51 /// However a [`ResourceDynamic`] can be constructed dynamically at runtime with
52 /// a runtime-defined type. For example an embedding that provides generic
53 /// access to types in the host may want to take advantage of the dynamic nature
54 /// of this type. Resources of type [`ResourceDynamic`] have a type of
55 /// [`ResourceType::host_dynamic(ty)`](ResourceType::host_dynamic) where `ty` is
56 /// the value provided to the constructors of [`ResourceDynamic`].
57 ///
58 /// A [`ResourceDynamic`] implements [`Lift`] and [`Lower`] in the same manner
59 /// as [`Resource`], but the implementations may fail after type-checking unlike
60 /// with [`Resource`] (due to the dynamic nature of the type which can't be
61 /// fully-checked during type-checking).
62 ///
63 /// [`Resource`]: crate::component::Resource
64 /// [`Resource<T>`]: crate::component::Resource
65 /// [`bindgen!`]: crate::component::bindgen
66 pub struct ResourceDynamic(HostResource<Dynamic, u32>);
67 
68 struct Dynamic;
69 
70 impl HostResourceType<u32> for Dynamic {
resource_type(ty: u32) -> ResourceType71     fn resource_type(ty: u32) -> ResourceType {
72         ResourceType::host_dynamic(ty)
73     }
74 
typecheck(ty: ResourceType) -> Option<u32>75     fn typecheck(ty: ResourceType) -> Option<u32> {
76         ty.as_host_dynamic()
77     }
78 }
79 
80 impl ResourceDynamic {
81     /// Creates a new owned resource with the `rep` specified.
82     ///
83     /// This is the same as [`Resource::new_own`] except that `ty` is an extra
84     /// parameter for the host-defined type information.
85     ///
86     /// [`Resource::new_own`]: crate::component::Resource::new_own
new_own(rep: u32, ty: u32) -> ResourceDynamic87     pub fn new_own(rep: u32, ty: u32) -> ResourceDynamic {
88         ResourceDynamic(HostResource::new_own(rep, ty))
89     }
90 
91     /// Creates a new borrowed resource which isn't actually rooted in any
92     /// ownership.
93     ///
94     /// This is the same as [`Resource::new_borrow`] except that `ty` is an extra
95     /// parameter for the host-defined type information.
96     ///
97     /// [`Resource::new_borrow`]: crate::component::Resource::new_borrow
new_borrow(rep: u32, ty: u32) -> ResourceDynamic98     pub fn new_borrow(rep: u32, ty: u32) -> ResourceDynamic {
99         ResourceDynamic(HostResource::new_borrow(rep, ty))
100     }
101 
102     /// Returns the underlying 32-bit representation used to originally create
103     /// this resource.
104     ///
105     /// This is the same as [`Resource::rep`].
106     ///
107     /// [`Resource::rep`]: crate::component::Resource::rep
rep(&self) -> u32108     pub fn rep(&self) -> u32 {
109         self.0.rep()
110     }
111 
112     /// Returns the 32-bit integer indicating the type of this resource.
113     ///
114     /// This will return the same 32-bit integer provided to the
115     /// [`ResourceDynamic::new_own`] constructor. The meaning of this integer is
116     /// left to the host and this only serves as an accessor to provide the
117     /// value back to the host.
ty(&self) -> u32118     pub fn ty(&self) -> u32 {
119         self.0.ty()
120     }
121 
122     /// Returns whether this is an owned resource or not.
123     ///
124     /// This is the same as [`Resource::owned`].
125     ///
126     /// [`Resource::owned`]: crate::component::Resource::owned
owned(&self) -> bool127     pub fn owned(&self) -> bool {
128         self.0.owned()
129     }
130 
131     /// Attempts to convert a [`ResourceAny`] into [`ResourceDynamic`].
132     ///
133     /// This is the same as [`Resource::try_from_resource_any`].
134     ///
135     /// [`Resource::try_from_resource_any`]: crate::component::Resource::try_from_resource_any
try_from_resource_any(resource: ResourceAny, store: impl AsContextMut) -> Result<Self>136     pub fn try_from_resource_any(resource: ResourceAny, store: impl AsContextMut) -> Result<Self> {
137         Ok(ResourceDynamic(resource.try_into_host_resource(store)?))
138     }
139 
140     /// See [`ResourceAny::try_from_resource`]
try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny>141     pub fn try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny> {
142         self.0.try_into_resource_any(store)
143     }
144 }
145 
146 unsafe impl ComponentType for ResourceDynamic {
147     const ABI: CanonicalAbiInfo = HostResource::<Dynamic, u32>::ABI;
148     type Lower = crate::ValRaw;
149 
typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()>150     fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
151         HostResource::<Dynamic, u32>::typecheck(ty, types)
152     }
153 }
154 
155 unsafe impl Lower for ResourceDynamic {
linear_lower_to_flat<U>( &self, cx: &mut LowerContext<'_, U>, ty: InterfaceType, dst: &mut MaybeUninit<Self::Lower>, ) -> Result<()>156     fn linear_lower_to_flat<U>(
157         &self,
158         cx: &mut LowerContext<'_, U>,
159         ty: InterfaceType,
160         dst: &mut MaybeUninit<Self::Lower>,
161     ) -> Result<()> {
162         self.0.linear_lower_to_flat(cx, ty, dst)
163     }
164 
linear_lower_to_memory<U>( &self, cx: &mut LowerContext<'_, U>, ty: InterfaceType, offset: usize, ) -> Result<()>165     fn linear_lower_to_memory<U>(
166         &self,
167         cx: &mut LowerContext<'_, U>,
168         ty: InterfaceType,
169         offset: usize,
170     ) -> Result<()> {
171         self.0.linear_lower_to_memory(cx, ty, offset)
172     }
173 }
174 
175 unsafe impl Lift for ResourceDynamic {
linear_lift_from_flat( cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower, ) -> Result<Self>176     fn linear_lift_from_flat(
177         cx: &mut LiftContext<'_>,
178         ty: InterfaceType,
179         src: &Self::Lower,
180     ) -> Result<Self> {
181         let host_resource = HostResource::linear_lift_from_flat(cx, ty, src)?;
182         Ok(ResourceDynamic(host_resource))
183     }
184 
linear_lift_from_memory( cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8], ) -> Result<Self>185     fn linear_lift_from_memory(
186         cx: &mut LiftContext<'_>,
187         ty: InterfaceType,
188         bytes: &[u8],
189     ) -> Result<Self> {
190         let host_resource = HostResource::linear_lift_from_memory(cx, ty, bytes)?;
191         Ok(ResourceDynamic(host_resource))
192     }
193 }
194 
195 impl fmt::Debug for ResourceDynamic {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result196     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197         self.0.fmt(f)
198     }
199 }
200