1 //! Implementation of the canonical-ABI related intrinsics for resources in the 2 //! component model. 3 //! 4 //! This module contains all the relevant gory details of the 5 //! component model related to lifting and lowering resources. For example 6 //! intrinsics like `resource.new` will bottom out in calling this file, and 7 //! this is where resource tables are actually defined and modified. 8 //! 9 //! The main types in this file are: 10 //! 11 //! * `ResourceTables` - the "here's everything" context which is required to 12 //! perform canonical ABI operations. 13 //! 14 //! * `CallContext` - per-task information about active calls and borrows 15 //! and runtime state tracking that to ensure that everything is handled 16 //! correctly. 17 //! 18 //! Individual operations are exposed through methods on `ResourceTables` for 19 //! lifting/lowering/etc. This does mean though that some other fiddly bits 20 //! about ABI details can be found in lifting/lowering throughout Wasmtime, 21 //! namely in the `Resource<T>` and `ResourceAny` types. 22 23 use super::{HandleTable, InstanceState, RemovedResource}; 24 use crate::component::store::ComponentTaskState; 25 use crate::prelude::*; 26 use core::error::Error; 27 use core::fmt; 28 use core::mem; 29 use wasmtime_environ::PrimaryMap; 30 use wasmtime_environ::component::{ 31 ComponentTypes, RuntimeComponentInstanceIndex, TypeResourceTableIndex, 32 }; 33 34 /// Contextual state necessary to perform resource-related operations. 35 /// 36 /// This state a bit odd since it has a few optional bits, but the idea is that 37 /// whenever this is constructed the bits required to perform operations are 38 /// always `Some`. For example: 39 /// 40 /// * During lifting and lowering both `guest_table` and `host_table` are 41 /// `Some`. 42 /// * During wasm's own intrinsics only `guest_table` is `Some`. 43 /// * During embedder-invoked resource destruction calls only `host_table` is 44 /// `Some`. 45 /// 46 /// This is all packaged up into one state though to make it easier to operate 47 /// on and to centralize handling of the state related to resources due to how 48 /// critical it is for correctness. 49 pub struct ResourceTables<'a> { 50 /// Runtime state for all resources defined in a component. 51 /// 52 /// This is required whenever a `TypeResourceTableIndex`, for example, is 53 /// provided as it's the lookup where that happens. Not present during 54 /// embedder-originating operations though such as 55 /// `ResourceAny::resource_drop` which won't consult this table as it's 56 /// only operating over the host table. 57 pub guest: Option<( 58 &'a mut PrimaryMap<RuntimeComponentInstanceIndex, InstanceState>, 59 &'a ComponentTypes, 60 )>, 61 62 /// Runtime state for resources currently owned by the host. 63 /// 64 /// This is the single table used by the host stored within `Store<T>`. Host 65 /// resources will point into this and effectively have the same semantics 66 /// as-if they're in-component resources. The major distinction though is 67 /// that this is a heterogeneous table instead of only containing a single 68 /// type. 69 pub host_table: &'a mut HandleTable, 70 71 /// Task information about calls actively in use to track information such 72 /// as borrow counts. 73 pub task_state: &'a mut ComponentTaskState, 74 } 75 76 /// Typed representation of a "rep" for a resource. 77 /// 78 /// All resources in the component model are stored in a single heterogeneous 79 /// table so this type is used to disambiguate what everything is. This is the 80 /// representation of a resource stored at-rest in memory. 81 #[derive(Debug)] 82 pub enum TypedResource { 83 /// A resource defined by the host. 84 /// 85 /// The meaning of the 32-bit integer here is up to the embedder, it 86 /// otherwise is not used within the runtime here. 87 Host(u32), 88 89 /// A resource defined within a component. 90 Component { 91 /// This is an integer supplied by the component itself when this 92 /// resource was created. Typically this is a pointer into linear 93 /// memory for a component. 94 rep: u32, 95 96 /// The type of this component resource. 97 /// 98 /// This is used, within the context of a component, to keep track of 99 /// what the type of `rep` is. This is then used when getting/removing 100 /// from the table to ensure that the guest does indeed have the right 101 /// permission to access this slot. 102 ty: TypeResourceTableIndex, 103 }, 104 } 105 106 impl TypedResource { rep(&self, access_ty: &TypedResourceIndex) -> Result<u32>107 pub(super) fn rep(&self, access_ty: &TypedResourceIndex) -> Result<u32> { 108 match (self, access_ty) { 109 (Self::Host(rep), TypedResourceIndex::Host(_)) => Ok(*rep), 110 (Self::Host(_), expected) => bail!(ResourceTypeMismatch { 111 expected: *expected, 112 found: "host resource", 113 }), 114 (Self::Component { rep, ty }, TypedResourceIndex::Component { ty: expected, .. }) => { 115 if ty == expected { 116 Ok(*rep) 117 } else { 118 bail!(ResourceTypeMismatch { 119 expected: *access_ty, 120 found: "a different guest-defined resource", 121 }) 122 } 123 } 124 (Self::Component { .. }, expected) => bail!(ResourceTypeMismatch { 125 expected: *expected, 126 found: "guest-defined resource", 127 }), 128 } 129 } 130 } 131 132 /// An index used to access a resource. 133 /// 134 /// This reflects how index operations are always accompanied not only with a 135 /// 32-bit index but additionally with a type. For example a `resource.drop` 136 /// intrinsic in a guest takes only a 32-bit integer argument, but it 137 /// inherently is used to only drop one type of resource which is additionally 138 /// ascribed here. 139 #[derive(Debug, Copy, Clone)] 140 pub enum TypedResourceIndex { 141 /// A host resource at the given index is being accessed. 142 Host(u32), 143 144 /// A guest resource is being accessed. 145 Component { 146 /// The index supplied by the guest being accessed. 147 index: u32, 148 149 /// The fully-specific type of this resource. 150 ty: TypeResourceTableIndex, 151 }, 152 } 153 154 impl TypedResourceIndex { raw_index(&self) -> u32155 pub(super) fn raw_index(&self) -> u32 { 156 match self { 157 Self::Host(index) | Self::Component { index, .. } => *index, 158 } 159 } 160 desc(&self) -> &'static str161 fn desc(&self) -> &'static str { 162 match self { 163 Self::Host(_) => "host resource", 164 Self::Component { .. } => "guest-defined resource", 165 } 166 } 167 } 168 169 /// State related to borrows for a specific call. 170 #[derive(Default)] 171 pub struct CallContext { 172 lenders: Vec<TypedResourceIndex>, 173 borrow_count: u32, 174 } 175 176 impl ResourceTables<'_> { table_for_resource(&mut self, resource: &TypedResource) -> &mut HandleTable177 fn table_for_resource(&mut self, resource: &TypedResource) -> &mut HandleTable { 178 match resource { 179 TypedResource::Host(_) => self.host_table, 180 TypedResource::Component { ty, .. } => { 181 let (states, types) = self.guest.as_mut().unwrap(); 182 states[types[*ty].unwrap_concrete_instance()].handle_table() 183 } 184 } 185 } 186 table_for_index(&mut self, index: &TypedResourceIndex) -> &mut HandleTable187 fn table_for_index(&mut self, index: &TypedResourceIndex) -> &mut HandleTable { 188 match index { 189 TypedResourceIndex::Host(_) => self.host_table, 190 TypedResourceIndex::Component { ty, .. } => { 191 let (states, types) = self.guest.as_mut().unwrap(); 192 states[types[*ty].unwrap_concrete_instance()].handle_table() 193 } 194 } 195 } 196 197 /// Implementation of the `resource.new` canonical intrinsic. 198 /// 199 /// Note that this is the same as `resource_lower_own`. resource_new(&mut self, resource: TypedResource) -> Result<u32>200 pub fn resource_new(&mut self, resource: TypedResource) -> Result<u32> { 201 self.table_for_resource(&resource) 202 .resource_own_insert(resource) 203 } 204 205 /// Implementation of the `resource.rep` canonical intrinsic. 206 /// 207 /// This one's one of the simpler ones: "just get the rep please" resource_rep(&mut self, index: TypedResourceIndex) -> Result<u32>208 pub fn resource_rep(&mut self, index: TypedResourceIndex) -> Result<u32> { 209 self.table_for_index(&index).resource_rep(index) 210 } 211 212 /// Implementation of the `resource.drop` canonical intrinsic minus the 213 /// actual invocation of the destructor. 214 /// 215 /// This will drop the handle at the `index` specified, removing it from 216 /// the specified table. This operation can fail if: 217 /// 218 /// * The index is invalid. 219 /// * The index points to an `own` resource which has active borrows. 220 /// * The index's type is mismatched with the entry in the table's type. 221 /// 222 /// Otherwise this will return `Some(rep)` if the destructor for `rep` needs 223 /// to run. If `None` is returned then that means a `borrow` handle was 224 /// removed and no destructor is necessary. resource_drop(&mut self, index: TypedResourceIndex) -> Result<Option<u32>>225 pub fn resource_drop(&mut self, index: TypedResourceIndex) -> Result<Option<u32>> { 226 match self.table_for_index(&index).remove_resource(index)? { 227 RemovedResource::Own { rep } => Ok(Some(rep)), 228 RemovedResource::Borrow { scope } => { 229 self.task_state.call_context(scope)?.borrow_count -= 1; 230 Ok(None) 231 } 232 } 233 } 234 235 /// Inserts a new "own" handle into the specified table. 236 /// 237 /// This will insert the specified representation into the specified type 238 /// table. 239 /// 240 /// Note that this operation is infallible, and additionally that this is 241 /// the same as `resource_new` implementation-wise. 242 /// 243 /// This is an implementation of the canonical ABI `lower_own` function. resource_lower_own(&mut self, resource: TypedResource) -> Result<u32>244 pub fn resource_lower_own(&mut self, resource: TypedResource) -> Result<u32> { 245 self.table_for_resource(&resource) 246 .resource_own_insert(resource) 247 } 248 249 /// Attempts to remove an "own" handle from the specified table and its 250 /// index. 251 /// 252 /// This operation will fail if `index` is invalid, if it's a `borrow` 253 /// handle, if the own handle has currently been "lent" as a borrow, or if 254 /// `index` has a different type in the table than the index. 255 /// 256 /// This is an implementation of the canonical ABI `lift_own` function. resource_lift_own(&mut self, index: TypedResourceIndex) -> Result<u32>257 pub fn resource_lift_own(&mut self, index: TypedResourceIndex) -> Result<u32> { 258 match self.table_for_index(&index).remove_resource(index)? { 259 RemovedResource::Own { rep } => Ok(rep), 260 RemovedResource::Borrow { .. } => bail!("cannot lift own resource from a borrow"), 261 } 262 } 263 264 /// Extracts the underlying resource representation by lifting a "borrow" 265 /// from the tables. 266 /// 267 /// This primarily employs dynamic tracking when a borrow is created from an 268 /// "own" handle to ensure that the "own" handle isn't dropped while the 269 /// borrow is active and additionally that when the current call scope 270 /// returns the lend operation is undone. 271 /// 272 /// This is an implementation of the canonical ABI `lift_borrow` function. resource_lift_borrow(&mut self, index: TypedResourceIndex) -> Result<u32>273 pub fn resource_lift_borrow(&mut self, index: TypedResourceIndex) -> Result<u32> { 274 let (rep, is_own) = self.table_for_index(&index).resource_lend(index)?; 275 if is_own { 276 let scope = self.task_state.current_call_context_scope_id()?; 277 self.task_state.call_context(scope)?.lenders.push(index); 278 } 279 Ok(rep) 280 } 281 282 /// Records a new `borrow` resource with the given representation within the 283 /// current call scope. 284 /// 285 /// This requires that a call scope is active. Additionally the number of 286 /// active borrows in the latest scope will be increased and must be 287 /// decreased through a future use of `resource_drop` before the current 288 /// call scope exits. 289 /// 290 /// This some of the implementation of the canonical ABI `lower_borrow` 291 /// function. The other half of this implementation is located on 292 /// `VMComponentContext` which handles the special case of avoiding borrow 293 /// tracking entirely. resource_lower_borrow(&mut self, resource: TypedResource) -> Result<u32>294 pub fn resource_lower_borrow(&mut self, resource: TypedResource) -> Result<u32> { 295 let scope = self.task_state.current_call_context_scope_id()?; 296 let cx = self.task_state.call_context(scope)?; 297 cx.borrow_count = cx.borrow_count.checked_add(1).unwrap(); 298 self.table_for_resource(&resource) 299 .resource_borrow_insert(resource, scope) 300 } 301 302 /// Validates that the current scope can be exited. 303 /// 304 /// This will ensure that this context's active borrows have all been 305 /// dropped. This will then commit the lend decrements back to the owned 306 /// resources that were originally passed in. 307 #[inline] validate_scope_exit(&mut self) -> Result<()>308 pub fn validate_scope_exit(&mut self) -> Result<()> { 309 let current = self.task_state.current_call_context_scope_id()?; 310 let cx = self.task_state.call_context(current)?; 311 if cx.borrow_count > 0 { 312 bail!("borrow handles still remain at the end of the call") 313 } 314 for lender in mem::take(&mut cx.lenders) { 315 // Note the panics here which should never get triggered in theory 316 // due to the dynamic tracking of borrows and such employed for 317 // resources. 318 self.table_for_index(&lender) 319 .resource_undo_lend(lender) 320 .unwrap(); 321 } 322 Ok(()) 323 } 324 } 325 326 #[derive(Debug)] 327 struct ResourceTypeMismatch { 328 expected: TypedResourceIndex, 329 found: &'static str, 330 } 331 332 impl fmt::Display for ResourceTypeMismatch { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 334 write!( 335 f, 336 "handle index {} used with the wrong type, \ 337 expected {} but found {}", 338 self.expected.raw_index(), 339 self.expected.desc(), 340 self.found, 341 ) 342 } 343 } 344 345 impl Error for ResourceTypeMismatch {} 346