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