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