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 linear_lower_to_flat<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 .linear_lower_to_flat(cx, InterfaceType::U32, dst) 860 } 861 862 fn linear_lower_to_memory<U>( 863 &self, 864 cx: &mut LowerContext<'_, U>, 865 ty: InterfaceType, 866 offset: usize, 867 ) -> Result<()> { 868 self.lower_to_index(cx, ty)? 869 .linear_lower_to_memory(cx, InterfaceType::U32, offset) 870 } 871 } 872 873 unsafe impl<T: 'static> Lift for Resource<T> { 874 fn linear_lift_from_flat( 875 cx: &mut LiftContext<'_>, 876 ty: InterfaceType, 877 src: &Self::Lower, 878 ) -> Result<Self> { 879 let index = u32::linear_lift_from_flat(cx, InterfaceType::U32, src)?; 880 Resource::lift_from_index(cx, ty, index) 881 } 882 883 fn linear_lift_from_memory( 884 cx: &mut LiftContext<'_>, 885 ty: InterfaceType, 886 bytes: &[u8], 887 ) -> Result<Self> { 888 let index = u32::linear_lift_from_memory(cx, InterfaceType::U32, bytes)?; 889 Resource::lift_from_index(cx, ty, index) 890 } 891 } 892 893 impl<T> fmt::Debug for Resource<T> { 894 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 895 let state = match self.state.get() { 896 ResourceState::Borrow => "borrow", 897 ResourceState::NotInTable => "own (not in table)", 898 ResourceState::Taken => "taken", 899 ResourceState::Index(_) => "own", 900 }; 901 f.debug_struct("Resource") 902 .field("rep", &self.rep) 903 .field("state", &state) 904 .finish() 905 } 906 } 907 908 /// Representation of a resource in the component model, either a guest-defined 909 /// or a host-defined resource. 910 /// 911 /// This type is similar to [`Resource`] except that it can be used to represent 912 /// any resource, either host or guest. This type cannot be directly constructed 913 /// and is only available if the guest returns it to the host (e.g. a function 914 /// returning a guest-defined resource) or by a conversion from [`Resource`] via 915 /// [`ResourceAny::try_from_resource`]. 916 /// This type also does not carry a static type parameter `T` for example and 917 /// does not have as much information about its type. 918 /// This means that it's possible to get runtime type-errors when 919 /// using this type because it cannot statically prevent mismatching resource 920 /// types. 921 /// 922 /// Like [`Resource`] this type represents either an `own` or a `borrow` 923 /// resource internally. Unlike [`Resource`], however, a [`ResourceAny`] must 924 /// always be explicitly destroyed with the [`ResourceAny::resource_drop`] 925 /// method. This will update internal dynamic state tracking and invoke the 926 /// WebAssembly-defined destructor for a resource, if any. 927 /// 928 /// Note that it is required to call `resource_drop` for all instances of 929 /// [`ResourceAny`]: even borrows. Both borrows and own handles have state 930 /// associated with them that must be discarded by the time they're done being 931 /// used. 932 #[derive(Debug, PartialEq, Eq, Copy, Clone)] 933 pub struct ResourceAny { 934 idx: HostResourceIndex, 935 ty: ResourceType, 936 owned: bool, 937 } 938 939 impl ResourceAny { 940 /// Attempts to convert an imported [`Resource`] into [`ResourceAny`]. 941 /// 942 /// * `resource` is the resource to convert. 943 /// * `store` is the store to place the returned resource into. 944 /// 945 /// The returned `ResourceAny` will not have a destructor attached to it 946 /// meaning that if `resource_drop` is called then it will not invoked a 947 /// host-defined destructor. This is similar to how `Resource<T>` does not 948 /// have a destructor associated with it. 949 /// 950 /// # Errors 951 /// 952 /// This method will return an error if `resource` has already been "taken" 953 /// and has ownership transferred elsewhere which can happen in situations 954 /// such as when it's already lowered into a component. 955 pub fn try_from_resource<T: 'static>( 956 resource: Resource<T>, 957 mut store: impl AsContextMut, 958 ) -> Result<Self> { 959 let Resource { rep, state, .. } = resource; 960 let store = store.as_context_mut(); 961 962 let mut tables = HostResourceTables::new_host(store.0); 963 let (idx, owned) = match state.get() { 964 ResourceState::Borrow => (tables.host_resource_lower_borrow(rep)?, false), 965 ResourceState::NotInTable => { 966 let idx = tables.host_resource_lower_own(rep, None, None)?; 967 (idx, true) 968 } 969 ResourceState::Taken => bail!("host resource already consumed"), 970 ResourceState::Index(idx) => (idx, true), 971 }; 972 Ok(Self { 973 idx, 974 ty: ResourceType::host::<T>(), 975 owned, 976 }) 977 } 978 979 /// See [`Resource::try_from_resource_any`] 980 pub fn try_into_resource<T: 'static>(self, store: impl AsContextMut) -> Result<Resource<T>> { 981 Resource::try_from_resource_any(self, store) 982 } 983 984 /// Returns the corresponding type associated with this resource, either a 985 /// host-defined type or a guest-defined type. 986 /// 987 /// This can be compared against [`ResourceType::host`] for example to see 988 /// if it's a host-resource or against a type extracted with 989 /// [`Instance::get_resource`] to see if it's a guest-defined resource. 990 /// 991 /// [`Instance::get_resource`]: crate::component::Instance::get_resource 992 pub fn ty(&self) -> ResourceType { 993 self.ty 994 } 995 996 /// Returns whether this is an owned resource, and if not it's a borrowed 997 /// resource. 998 pub fn owned(&self) -> bool { 999 self.owned 1000 } 1001 1002 /// Destroy this resource and release any state associated with it. 1003 /// 1004 /// This is required to be called (or the async version) for all instances 1005 /// of [`ResourceAny`] to ensure that state associated with this resource is 1006 /// properly cleaned up. For owned resources this may execute the 1007 /// guest-defined destructor if applicable (or the host-defined destructor 1008 /// if one was specified). 1009 pub fn resource_drop(self, mut store: impl AsContextMut) -> Result<()> { 1010 let mut store = store.as_context_mut(); 1011 assert!( 1012 !store.0.async_support(), 1013 "must use `resource_drop_async` when async support is enabled on the config" 1014 ); 1015 self.resource_drop_impl(&mut store.as_context_mut()) 1016 } 1017 1018 /// Same as [`ResourceAny::resource_drop`] except for use with async stores 1019 /// to execute the destructor asynchronously. 1020 #[cfg(feature = "async")] 1021 pub async fn resource_drop_async<T>( 1022 self, 1023 mut store: impl AsContextMut<Data: Send>, 1024 ) -> Result<()> { 1025 let mut store = store.as_context_mut(); 1026 assert!( 1027 store.0.async_support(), 1028 "cannot use `resource_drop_async` without enabling async support in the config" 1029 ); 1030 store 1031 .on_fiber(|store| self.resource_drop_impl(store)) 1032 .await? 1033 } 1034 1035 fn resource_drop_impl<T: 'static>(self, store: &mut StoreContextMut<'_, T>) -> Result<()> { 1036 // Attempt to remove `self.idx` from the host table in `store`. 1037 // 1038 // This could fail if the index is invalid or if this is removing an 1039 // `Own` entry which is currently being borrowed. 1040 let pair = HostResourceTables::new_host(store.0).host_resource_drop(self.idx)?; 1041 1042 let (rep, slot) = match (pair, self.owned) { 1043 (Some(pair), true) => pair, 1044 1045 // A `borrow` was removed from the table and no further 1046 // destruction, e.g. the destructor, is required so we're done. 1047 (None, false) => return Ok(()), 1048 1049 _ => unreachable!(), 1050 }; 1051 1052 // Implement the reentrance check required by the canonical ABI. Note 1053 // that this happens whether or not a destructor is present. 1054 // 1055 // Note that this should be safe because the raw pointer access in 1056 // `flags` is valid due to `store` being the owner of the flags and 1057 // flags are never destroyed within the store. 1058 if let Some(flags) = slot.flags { 1059 unsafe { 1060 if !flags.may_enter() { 1061 bail!(Trap::CannotEnterComponent); 1062 } 1063 } 1064 } 1065 1066 let dtor = match slot.dtor { 1067 Some(dtor) => dtor.as_non_null(), 1068 None => return Ok(()), 1069 }; 1070 let mut args = [ValRaw::u32(rep)]; 1071 1072 // This should be safe because `dtor` has been checked to belong to the 1073 // `store` provided which means it's valid and still alive. Additionally 1074 // destructors have al been previously type-checked and are guaranteed 1075 // to take one i32 argument and return no results, so the parameters 1076 // here should be configured correctly. 1077 unsafe { crate::Func::call_unchecked_raw(store, dtor, NonNull::from(&mut args)) } 1078 } 1079 1080 fn lower_to_index<U>(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result<u32> { 1081 match ty { 1082 InterfaceType::Own(t) => { 1083 if cx.resource_type(t) != self.ty { 1084 bail!("mismatched resource types"); 1085 } 1086 let rep = cx.host_resource_lift_own(self.idx)?; 1087 cx.guest_resource_lower_own(t, rep) 1088 } 1089 InterfaceType::Borrow(t) => { 1090 if cx.resource_type(t) != self.ty { 1091 bail!("mismatched resource types"); 1092 } 1093 let rep = cx.host_resource_lift_borrow(self.idx)?; 1094 cx.guest_resource_lower_borrow(t, rep) 1095 } 1096 _ => bad_type_info(), 1097 } 1098 } 1099 1100 fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result<Self> { 1101 match ty { 1102 InterfaceType::Own(t) => { 1103 let ty = cx.resource_type(t); 1104 let (rep, dtor, flags) = cx.guest_resource_lift_own(t, index)?; 1105 let idx = cx.host_resource_lower_own(rep, dtor, flags)?; 1106 Ok(ResourceAny { 1107 idx, 1108 ty, 1109 owned: true, 1110 }) 1111 } 1112 InterfaceType::Borrow(t) => { 1113 let ty = cx.resource_type(t); 1114 let rep = cx.guest_resource_lift_borrow(t, index)?; 1115 let idx = cx.host_resource_lower_borrow(rep)?; 1116 Ok(ResourceAny { 1117 idx, 1118 ty, 1119 owned: false, 1120 }) 1121 } 1122 _ => bad_type_info(), 1123 } 1124 } 1125 } 1126 1127 unsafe impl ComponentType for ResourceAny { 1128 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4; 1129 1130 type Lower = <u32 as ComponentType>::Lower; 1131 1132 fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> { 1133 match ty { 1134 InterfaceType::Own(_) | InterfaceType::Borrow(_) => Ok(()), 1135 other => bail!("expected `own` or `borrow`, found `{}`", desc(other)), 1136 } 1137 } 1138 } 1139 1140 unsafe impl Lower for ResourceAny { 1141 fn linear_lower_to_flat<T>( 1142 &self, 1143 cx: &mut LowerContext<'_, T>, 1144 ty: InterfaceType, 1145 dst: &mut MaybeUninit<Self::Lower>, 1146 ) -> Result<()> { 1147 self.lower_to_index(cx, ty)? 1148 .linear_lower_to_flat(cx, InterfaceType::U32, dst) 1149 } 1150 1151 fn linear_lower_to_memory<T>( 1152 &self, 1153 cx: &mut LowerContext<'_, T>, 1154 ty: InterfaceType, 1155 offset: usize, 1156 ) -> Result<()> { 1157 self.lower_to_index(cx, ty)? 1158 .linear_lower_to_memory(cx, InterfaceType::U32, offset) 1159 } 1160 } 1161 1162 unsafe impl Lift for ResourceAny { 1163 fn linear_lift_from_flat( 1164 cx: &mut LiftContext<'_>, 1165 ty: InterfaceType, 1166 src: &Self::Lower, 1167 ) -> Result<Self> { 1168 let index = u32::linear_lift_from_flat(cx, InterfaceType::U32, src)?; 1169 ResourceAny::lift_from_index(cx, ty, index) 1170 } 1171 1172 fn linear_lift_from_memory( 1173 cx: &mut LiftContext<'_>, 1174 ty: InterfaceType, 1175 bytes: &[u8], 1176 ) -> Result<Self> { 1177 let index = u32::linear_lift_from_memory(cx, InterfaceType::U32, bytes)?; 1178 ResourceAny::lift_from_index(cx, ty, index) 1179 } 1180 } 1181