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