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