1 //! This module defines the `Resource<T>` type in the public API of Wasmtime. 2 //! 3 //! The purpose of this type is to represent a typed resource on the host where 4 //! the runtime representation is just a 32-bit integer plus some minor state 5 //! tracking. Notably the `T` enables statically differentiating resources from 6 //! one another and enables up-front type-checking where the lift/lower 7 //! operations need not do any type-checking at all. 8 //! 9 //! The actual `T` type parameter is just a guide, no `T` value is ever needed. 10 11 use crate::AsContextMut; 12 use crate::component::func::{LiftContext, LowerContext}; 13 use crate::component::matching::InstanceType; 14 use crate::component::resources::host::{HostResource, HostResourceType}; 15 use crate::component::{ComponentType, Lift, Lower, ResourceAny, ResourceType}; 16 use crate::prelude::*; 17 use core::fmt; 18 use core::mem::MaybeUninit; 19 use wasmtime_environ::component::{CanonicalAbiInfo, InterfaceType}; 20 21 /// A host-defined resource in the component model with a statically ascribed 22 /// type parameter. 23 /// 24 /// This type can be thought of as roughly a newtype wrapper around `u32` for 25 /// use as a resource with the component model. The main guarantee that the 26 /// component model provides is that the `u32` is not forgeable by guests and 27 /// there are guaranteed semantics about when a `u32` may be in use by the guest 28 /// and when it's guaranteed no longer needed. This means that it is safe for 29 /// embedders to consider the internal `u32` representation "trusted" and use it 30 /// for things like table indices with infallible accessors that panic on 31 /// out-of-bounds. This should only panic for embedder bugs, not because of any 32 /// possible behavior in the guest. 33 /// 34 /// A `Resource<T>` value dynamically represents both an `(own $t)` in the 35 /// component model as well as a `(borrow $t)`. It can be inspected via 36 /// [`Resource::owned`] to test whether it is an owned handle. An owned handle 37 /// which is not actively borrowed can be destroyed at any time as it's 38 /// guaranteed that the guest does not have access to it. A borrowed handle, on 39 /// the other hand, may be accessed by the guest so it's not necessarily 40 /// guaranteed to be able to be destroyed. 41 /// 42 /// Note that the "own" and "borrow" here refer to the component model, not 43 /// Rust. The semantics of Rust ownership and borrowing are slightly different 44 /// than the component model's (but spiritually the same) in that more dynamic 45 /// tracking is employed as part of the component model. This means that it's 46 /// possible to get runtime errors when using a `Resource<T>`. For example it is 47 /// an error to call [`Resource::new_borrow`] and pass that to a component 48 /// function expecting `(own $t)` and this is not statically disallowed. 49 /// 50 /// The [`Resource`] type implements both the [`Lift`] and [`Lower`] trait to be 51 /// used with typed functions in the component model or as part of aggregate 52 /// structures and datatypes. 53 /// 54 /// Note that [`Resource`] and [`ResourceDynamic`] are similar and for more 55 /// discussion of choosing one over the other see the documentation on 56 /// [`ResourceDynamic`] 57 /// 58 /// [`ResourceDynamic`]: crate::component::ResourceDynamic 59 /// 60 /// # Destruction of a resource 61 /// 62 /// Resources in the component model are optionally defined with a destructor, 63 /// but this host resource type does not specify a destructor. It is left up to 64 /// the embedder to be able to determine how best to a destroy a resource when 65 /// it is owned. 66 /// 67 /// Note, though, that while [`Resource`] itself does not specify destructors 68 /// it's still possible to do so via the [`Linker::resource`] definition. When a 69 /// resource type is defined for a guest component a destructor can be specified 70 /// which can be used to hook into resource destruction triggered by the guest. 71 /// 72 /// This means that there are two ways that resource destruction is handled: 73 /// 74 /// * Host resources destroyed by the guest can hook into the 75 /// [`Linker::resource`] destructor closure to handle resource destruction. 76 /// This could, for example, remove table entries. 77 /// 78 /// * Host resources owned by the host itself have no automatic means of 79 /// destruction. The host can make its own determination that its own resource 80 /// is not lent out to the guest and at that time choose to destroy or 81 /// deallocate it. 82 /// 83 /// # Dynamic behavior of a resource 84 /// 85 /// A host-defined [`Resource`] does not necessarily represent a static value. 86 /// Its internals may change throughout its usage to track the state associated 87 /// with the resource. The internal 32-bit host-defined representation never 88 /// changes, however. 89 /// 90 /// For example if there's a component model function of the form: 91 /// 92 /// ```wasm 93 /// (func (param "a" (borrow $t)) (param "b" (own $t))) 94 /// ``` 95 /// 96 /// Then that can be extracted in Rust with: 97 /// 98 /// ```rust,ignore 99 /// let func = instance.get_typed_func::<(&Resource<T>, &Resource<T>), ()>(&mut store, "name")?; 100 /// ``` 101 /// 102 /// Here the exact same resource can be provided as both arguments but that is 103 /// not valid to do so because the same resource cannot be actively borrowed and 104 /// passed by-value as the second parameter at the same time. The internal state 105 /// in `Resource<T>` will track this information and provide a dynamic runtime 106 /// error in this situation. 107 /// 108 /// Mostly it's important to be aware that there is dynamic state associated 109 /// with a [`Resource<T>`] to provide errors in situations that cannot be 110 /// statically ruled out. 111 /// 112 /// # Borrows and host responsibilities 113 /// 114 /// Borrows to resources in the component model are guaranteed to be transient 115 /// such that if a borrow is passed as part of a function call then when the 116 /// function returns it's guaranteed that the guest no longer has access to the 117 /// resource. This guarantee, however, must be manually upheld by the host when 118 /// it receives its own borrow. 119 /// 120 /// As mentioned above the [`Resource<T>`] type can represent a borrowed value 121 /// in addition to an owned value. This means a guest can provide the host with 122 /// a borrow, such as an argument to an imported function: 123 /// 124 /// ```rust,ignore 125 /// linker.root().func_wrap("name", |_cx, (r,): (Resource<MyType>,)| { 126 /// assert!(!r.owned()); 127 /// // .. here `r` is a borrowed value provided by the guest and the host 128 /// // shouldn't continue to access it beyond the scope of this call 129 /// })?; 130 /// ``` 131 /// 132 /// In this situation the host should take care to not attempt to persist the 133 /// resource beyond the scope of the call. It's the host's resource so it 134 /// technically can do what it wants with it but nothing is statically 135 /// preventing `r` to stay pinned to the lifetime of the closure invocation. 136 /// It's considered a mistake that the host performed if `r` is persisted too 137 /// long and accessed at the wrong time. 138 /// 139 /// [`Linker::resource`]: crate::component::LinkerInstance::resource 140 pub struct Resource<T: 'static>(HostResource<Static<T>, ()>); 141 142 struct Static<T>(T); 143 144 impl<T: 'static> HostResourceType<()> for Static<T> { resource_type((): ()) -> ResourceType145 fn resource_type((): ()) -> ResourceType { 146 ResourceType::host::<T>() 147 } 148 typecheck(ty: ResourceType) -> Option<()>149 fn typecheck(ty: ResourceType) -> Option<()> { 150 if ty.is_host::<T>() { Some(()) } else { None } 151 } 152 } 153 154 impl<T> Resource<T> 155 where 156 T: 'static, 157 { 158 /// Creates a new owned resource with the `rep` specified. 159 /// 160 /// The returned value is suitable for passing to a guest as either a 161 /// `(borrow $t)` or `(own $t)`. new_own(rep: u32) -> Resource<T>162 pub fn new_own(rep: u32) -> Resource<T> { 163 Resource(HostResource::new_own(rep, ())) 164 } 165 166 /// Creates a new borrowed resource which isn't actually rooted in any 167 /// ownership. 168 /// 169 /// This can be used to pass to a guest as a borrowed resource and the 170 /// embedder will know that the `rep` won't be in use by the guest 171 /// afterwards. Exactly how the lifetime of `rep` works is up to the 172 /// embedder. new_borrow(rep: u32) -> Resource<T>173 pub fn new_borrow(rep: u32) -> Resource<T> { 174 Resource(HostResource::new_borrow(rep, ())) 175 } 176 177 /// Returns the underlying 32-bit representation used to originally create 178 /// this resource. rep(&self) -> u32179 pub fn rep(&self) -> u32 { 180 self.0.rep() 181 } 182 183 /// Returns whether this is an owned resource or not. 184 /// 185 /// Owned resources can be safely destroyed by the embedder at any time, and 186 /// borrowed resources have an owner somewhere else on the stack so can only 187 /// be accessed, not destroyed. owned(&self) -> bool188 pub fn owned(&self) -> bool { 189 self.0.owned() 190 } 191 192 /// Attempts to convert a [`ResourceAny`] into [`Resource`]. 193 /// 194 /// This method will check that `resource` has type 195 /// `ResourceType::host::<T>()` and then convert it into a typed version of 196 /// the resource. 197 /// 198 /// # Errors 199 /// 200 /// This function will return an error if `resource` does not have type 201 /// `ResourceType::host::<T>()`. This function may also return an error if 202 /// `resource` is no longer valid, for example it was previously converted. 203 /// 204 /// # Panics 205 /// 206 /// This function will panic if `resource` does not belong to the `store` 207 /// specified. try_from_resource_any(resource: ResourceAny, store: impl AsContextMut) -> Result<Self>208 pub fn try_from_resource_any(resource: ResourceAny, store: impl AsContextMut) -> Result<Self> { 209 Ok(Resource(resource.try_into_host_resource(store)?)) 210 } 211 212 /// See [`ResourceAny::try_from_resource`] try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny>213 pub fn try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny> { 214 self.0.try_into_resource_any(store) 215 } 216 } 217 218 unsafe impl<T: 'static> ComponentType for Resource<T> { 219 const ABI: CanonicalAbiInfo = HostResource::<Static<T>, ()>::ABI; 220 type Lower = crate::ValRaw; 221 typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()>222 fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> { 223 HostResource::<Static<T>, ()>::typecheck(ty, types) 224 } 225 } 226 227 unsafe impl<T: 'static> Lower for Resource<T> { linear_lower_to_flat<U>( &self, cx: &mut LowerContext<'_, U>, ty: InterfaceType, dst: &mut MaybeUninit<Self::Lower>, ) -> Result<()>228 fn linear_lower_to_flat<U>( 229 &self, 230 cx: &mut LowerContext<'_, U>, 231 ty: InterfaceType, 232 dst: &mut MaybeUninit<Self::Lower>, 233 ) -> Result<()> { 234 self.0.linear_lower_to_flat(cx, ty, dst) 235 } 236 linear_lower_to_memory<U>( &self, cx: &mut LowerContext<'_, U>, ty: InterfaceType, offset: usize, ) -> Result<()>237 fn linear_lower_to_memory<U>( 238 &self, 239 cx: &mut LowerContext<'_, U>, 240 ty: InterfaceType, 241 offset: usize, 242 ) -> Result<()> { 243 self.0.linear_lower_to_memory(cx, ty, offset) 244 } 245 } 246 247 unsafe impl<T: 'static> Lift for Resource<T> { linear_lift_from_flat( cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower, ) -> Result<Self>248 fn linear_lift_from_flat( 249 cx: &mut LiftContext<'_>, 250 ty: InterfaceType, 251 src: &Self::Lower, 252 ) -> Result<Self> { 253 let host_resource = HostResource::linear_lift_from_flat(cx, ty, src)?; 254 Ok(Resource(host_resource)) 255 } 256 linear_lift_from_memory( cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8], ) -> Result<Self>257 fn linear_lift_from_memory( 258 cx: &mut LiftContext<'_>, 259 ty: InterfaceType, 260 bytes: &[u8], 261 ) -> Result<Self> { 262 let host_resource = HostResource::linear_lift_from_memory(cx, ty, bytes)?; 263 Ok(Resource(host_resource)) 264 } 265 } 266 267 impl<T> fmt::Debug for Resource<T> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 269 self.0.fmt(f) 270 } 271 } 272