1 use crate::component::matching::InstanceType; 2 use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables}; 3 use crate::component::{Instance, ResourceType}; 4 use crate::prelude::*; 5 use crate::runtime::vm::component::{ 6 CallContexts, ComponentInstance, HandleTable, InstanceFlags, ResourceTables, 7 }; 8 use crate::runtime::vm::{VMFuncRef, VMMemoryDefinition}; 9 use crate::store::{StoreId, StoreOpaque}; 10 use crate::{FuncType, StoreContextMut}; 11 use alloc::sync::Arc; 12 use core::pin::Pin; 13 use core::ptr::NonNull; 14 use wasmtime_environ::component::{ 15 CanonicalOptions, CanonicalOptionsDataModel, ComponentTypes, OptionsIndex, StringEncoding, 16 TypeResourceTableIndex, 17 }; 18 19 /// Runtime representation of canonical ABI options in the component model. 20 /// 21 /// This structure packages up the runtime representation of each option from 22 /// memories to reallocs to string encodings. Note that this is a "standalone" 23 /// structure which has raw pointers internally. This allows it to be created 24 /// out of thin air for a host function import, for example. The `store_id` 25 /// field, however, is what is used to pair this set of options with a store 26 /// reference to actually use the pointers. 27 #[derive(Copy, Clone)] 28 pub struct Options { 29 /// The store from which this options originated. 30 store_id: StoreId, 31 32 /// An optional pointer for the memory that this set of options is referring 33 /// to. This option is not required to be specified in the canonical ABI 34 /// hence the `Option`. 35 /// 36 /// Note that this pointer cannot be safely dereferenced unless a store, 37 /// verified with `self.store_id`, has the appropriate borrow available. 38 memory: Option<NonNull<VMMemoryDefinition>>, 39 40 /// Similar to `memory` but corresponds to the `canonical_abi_realloc` 41 /// function. 42 /// 43 /// Safely using this pointer has the same restrictions as `memory` above. 44 realloc: Option<NonNull<VMFuncRef>>, 45 46 /// The encoding used for strings, if found. 47 /// 48 /// This defaults to utf-8 but can be changed if necessary. 49 string_encoding: StringEncoding, 50 51 /// Whether or not this the async option was set when lowering. 52 async_: bool, 53 54 #[cfg(feature = "component-model-async")] 55 callback: Option<NonNull<VMFuncRef>>, 56 } 57 58 // The `Options` structure stores raw pointers but they're never used unless a 59 // `Store` is available so this should be threadsafe and largely inherit the 60 // thread-safety story of `Store<T>` itself. 61 unsafe impl Send for Options {} 62 unsafe impl Sync for Options {} 63 64 impl Options { 65 // FIXME(#4311): prevent a ctor where the memory is memory64 66 67 /// Creates a new [`Options`] from the given [`OptionsIndex`] belonging to 68 /// the specified [`Instance`] 69 /// 70 /// # Panics 71 /// 72 /// Panics if `instance` is not owned by `store` or if `index` is not valid 73 /// for `instance`'s component. 74 pub fn new_index(store: &StoreOpaque, instance: Instance, index: OptionsIndex) -> Options { 75 let instance = instance.id().get(store); 76 let CanonicalOptions { 77 string_encoding, 78 async_, 79 callback, 80 ref data_model, 81 .. 82 } = instance.component().env_component().options[index]; 83 let (memory, realloc) = match data_model { 84 CanonicalOptionsDataModel::Gc { .. } => (None, None), 85 CanonicalOptionsDataModel::LinearMemory(o) => (o.memory, o.realloc), 86 }; 87 let memory = memory.map(|i| NonNull::new(instance.runtime_memory(i)).unwrap()); 88 let realloc = realloc.map(|i| instance.runtime_realloc(i)); 89 let callback = callback.map(|i| instance.runtime_callback(i)); 90 let _ = callback; 91 92 Options { 93 store_id: store.id(), 94 memory, 95 realloc, 96 string_encoding, 97 async_, 98 #[cfg(feature = "component-model-async")] 99 callback, 100 } 101 } 102 103 fn realloc<'a, T>( 104 &self, 105 store: &'a mut StoreContextMut<'_, T>, 106 realloc_ty: &FuncType, 107 old: usize, 108 old_size: usize, 109 old_align: u32, 110 new_size: usize, 111 ) -> Result<(&'a mut [u8], usize)> { 112 self.store_id.assert_belongs_to(store.0.id()); 113 114 let realloc = self.realloc.unwrap(); 115 116 let params = ( 117 u32::try_from(old)?, 118 u32::try_from(old_size)?, 119 old_align, 120 u32::try_from(new_size)?, 121 ); 122 123 type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>; 124 125 // Invoke the wasm malloc function using its raw and statically known 126 // signature. 127 let result = unsafe { ReallocFunc::call_raw(store, realloc_ty, realloc, params)? }; 128 129 if result % old_align != 0 { 130 bail!("realloc return: result not aligned"); 131 } 132 let result = usize::try_from(result)?; 133 134 let memory = self.memory_mut(store.0); 135 136 let result_slice = match memory.get_mut(result..).and_then(|s| s.get_mut(..new_size)) { 137 Some(end) => end, 138 None => bail!("realloc return: beyond end of memory"), 139 }; 140 141 Ok((result_slice, result)) 142 } 143 144 /// Asserts that this function has an associated memory attached to it and 145 /// then returns the slice of memory tied to the lifetime of the provided 146 /// store. 147 pub fn memory<'a>(&self, store: &'a StoreOpaque) -> &'a [u8] { 148 self.store_id.assert_belongs_to(store.id()); 149 150 // The unsafety here is intended to be encapsulated by the two 151 // preceding assertions. Namely we assert that the `store` is the same 152 // as the original store of this `Options`, meaning that we safely have 153 // either a shared reference or a mutable reference (as below) which 154 // means it's safe to view the memory (aka it's not a different store 155 // where our original store is on some other thread or something like 156 // that). 157 // 158 // Additionally the memory itself is asserted to be present as memory 159 // is an optional configuration in canonical ABI options. 160 unsafe { 161 let memory = self.memory.unwrap().as_ref(); 162 core::slice::from_raw_parts(memory.base.as_ptr(), memory.current_length()) 163 } 164 } 165 166 /// Same as above, just `_mut` 167 pub fn memory_mut<'a>(&self, store: &'a mut StoreOpaque) -> &'a mut [u8] { 168 self.store_id.assert_belongs_to(store.id()); 169 170 // See comments in `memory` about the unsafety 171 unsafe { 172 let memory = self.memory.unwrap().as_ref(); 173 core::slice::from_raw_parts_mut(memory.base.as_ptr(), memory.current_length()) 174 } 175 } 176 177 /// Returns the underlying encoding used for strings in this 178 /// lifting/lowering. 179 pub fn string_encoding(&self) -> StringEncoding { 180 self.string_encoding 181 } 182 183 /// Returns the id of the store that this `Options` is connected to. 184 pub fn store_id(&self) -> StoreId { 185 self.store_id 186 } 187 188 /// Returns whether this lifting or lowering uses the async ABI. 189 pub fn async_(&self) -> bool { 190 self.async_ 191 } 192 193 #[cfg(feature = "component-model-async")] 194 pub(crate) fn callback(&self) -> Option<NonNull<VMFuncRef>> { 195 self.callback 196 } 197 198 #[cfg(feature = "component-model-async")] 199 pub(crate) fn memory_raw(&self) -> Option<NonNull<VMMemoryDefinition>> { 200 self.memory 201 } 202 } 203 204 /// A helper structure which is a "package" of the context used during lowering 205 /// values into a component (or storing them into memory). 206 /// 207 /// This type is used by the `Lower` trait extensively and contains any 208 /// contextual information necessary related to the context in which the 209 /// lowering is happening. 210 #[doc(hidden)] 211 pub struct LowerContext<'a, T: 'static> { 212 /// Lowering may involve invoking memory allocation functions so part of the 213 /// context here is carrying access to the entire store that wasm is 214 /// executing within. This store serves as proof-of-ability to actually 215 /// execute wasm safely. 216 pub store: StoreContextMut<'a, T>, 217 218 /// Lowering always happens into a function that's been `canon lift`'d or 219 /// `canon lower`'d, both of which specify a set of options for the 220 /// canonical ABI. For example details like string encoding are contained 221 /// here along with which memory pointers are relative to or what the memory 222 /// allocation function is. 223 pub options: &'a Options, 224 225 /// Lowering happens within the context of a component instance and this 226 /// field stores the type information of that component instance. This is 227 /// used for type lookups and general type queries during the 228 /// lifting/lowering process. 229 pub types: &'a ComponentTypes, 230 231 /// Index of the component instance that's being lowered into. 232 instance: Instance, 233 234 /// Whether to allow `options.realloc` to be used when lowering. 235 allow_realloc: bool, 236 } 237 238 #[doc(hidden)] 239 impl<'a, T: 'static> LowerContext<'a, T> { 240 /// Creates a new lowering context from the specified parameters. 241 pub fn new( 242 store: StoreContextMut<'a, T>, 243 options: &'a Options, 244 types: &'a ComponentTypes, 245 instance: Instance, 246 ) -> LowerContext<'a, T> { 247 #[cfg(all(debug_assertions, feature = "component-model-async"))] 248 if store.engine().config().async_support { 249 // Assert that we're running on a fiber, which is necessary in 250 // case we call the guest's realloc function. 251 store.0.with_blocking(|_, _| {}); 252 } 253 LowerContext { 254 store, 255 options, 256 types, 257 instance, 258 allow_realloc: true, 259 } 260 } 261 262 /// Like `new`, except disallows use of `options.realloc`. 263 /// 264 /// The returned object will panic if its `realloc` method is called. 265 /// 266 /// This is meant for use when lowering "flat" values (i.e. values which 267 /// require no allocations) into already-allocated memory or into stack 268 /// slots, in which case the lowering may safely be done outside of a fiber 269 /// since there is no need to make any guest calls. 270 #[cfg(feature = "component-model-async")] 271 pub(crate) fn new_without_realloc( 272 store: StoreContextMut<'a, T>, 273 options: &'a Options, 274 types: &'a ComponentTypes, 275 instance: Instance, 276 ) -> LowerContext<'a, T> { 277 LowerContext { 278 store, 279 options, 280 types, 281 instance, 282 allow_realloc: false, 283 } 284 } 285 286 /// Returns the `&ComponentInstance` that's being lowered into. 287 pub fn instance(&self) -> &ComponentInstance { 288 self.instance.id().get(self.store.0) 289 } 290 291 /// Returns the `&mut ComponentInstance` that's being lowered into. 292 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> { 293 self.instance.id().get_mut(self.store.0) 294 } 295 296 /// Returns a view into memory as a mutable slice of bytes. 297 /// 298 /// # Panics 299 /// 300 /// This will panic if memory has not been configured for this lowering 301 /// (e.g. it wasn't present during the specification of canonical options). 302 pub fn as_slice_mut(&mut self) -> &mut [u8] { 303 self.options.memory_mut(self.store.0) 304 } 305 306 /// Invokes the memory allocation function (which is style after `realloc`) 307 /// with the specified parameters. 308 /// 309 /// # Panics 310 /// 311 /// This will panic if realloc hasn't been configured for this lowering via 312 /// its canonical options. 313 pub fn realloc( 314 &mut self, 315 old: usize, 316 old_size: usize, 317 old_align: u32, 318 new_size: usize, 319 ) -> Result<usize> { 320 assert!(self.allow_realloc); 321 322 let realloc_func_ty = Arc::clone(self.instance().component().realloc_func_ty()); 323 self.options 324 .realloc( 325 &mut self.store, 326 &realloc_func_ty, 327 old, 328 old_size, 329 old_align, 330 new_size, 331 ) 332 .map(|(_, ptr)| ptr) 333 } 334 335 /// Returns a fixed mutable slice of memory `N` bytes large starting at 336 /// offset `N`, panicking on out-of-bounds. 337 /// 338 /// It should be previously verified that `offset` is in-bounds via 339 /// bounds-checks. 340 /// 341 /// # Panics 342 /// 343 /// This will panic if memory has not been configured for this lowering 344 /// (e.g. it wasn't present during the specification of canonical options). 345 pub fn get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N] { 346 // FIXME: this bounds check shouldn't actually be necessary, all 347 // callers of `ComponentType::store` have already performed a bounds 348 // check so we're guaranteed that `offset..offset+N` is in-bounds. That 349 // being said we at least should do bounds checks in debug mode and 350 // it's not clear to me how to easily structure this so that it's 351 // "statically obvious" the bounds check isn't necessary. 352 // 353 // For now I figure we can leave in this bounds check and if it becomes 354 // an issue we can optimize further later, probably with judicious use 355 // of `unsafe`. 356 self.as_slice_mut()[offset..].first_chunk_mut().unwrap() 357 } 358 359 /// Lowers an `own` resource into the guest, converting the `rep` specified 360 /// into a guest-local index. 361 /// 362 /// The `ty` provided is which table to put this into. 363 pub fn guest_resource_lower_own( 364 &mut self, 365 ty: TypeResourceTableIndex, 366 rep: u32, 367 ) -> Result<u32> { 368 self.resource_tables().guest_resource_lower_own(rep, ty) 369 } 370 371 /// Lowers a `borrow` resource into the guest, converting the `rep` to a 372 /// guest-local index in the `ty` table specified. 373 pub fn guest_resource_lower_borrow( 374 &mut self, 375 ty: TypeResourceTableIndex, 376 rep: u32, 377 ) -> Result<u32> { 378 // Implement `lower_borrow`'s special case here where if a borrow is 379 // inserted into a table owned by the instance which implemented the 380 // original resource then no borrow tracking is employed and instead the 381 // `rep` is returned "raw". 382 // 383 // This check is performed by comparing the owning instance of `ty` 384 // against the owning instance of the resource that `ty` is working 385 // with. 386 if self.instance().resource_owned_by_own_instance(ty) { 387 return Ok(rep); 388 } 389 self.resource_tables().guest_resource_lower_borrow(rep, ty) 390 } 391 392 /// Lifts a host-owned `own` resource at the `idx` specified into the 393 /// representation of that resource. 394 pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> { 395 self.resource_tables().host_resource_lift_own(idx) 396 } 397 398 /// Lifts a host-owned `borrow` resource at the `idx` specified into the 399 /// representation of that resource. 400 pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> { 401 self.resource_tables().host_resource_lift_borrow(idx) 402 } 403 404 /// Lowers a resource into the host-owned table, returning the index it was 405 /// inserted at. 406 /// 407 /// Note that this is a special case for `Resource<T>`. Most of the time a 408 /// host value shouldn't be lowered with a lowering context. 409 pub fn host_resource_lower_own( 410 &mut self, 411 rep: u32, 412 dtor: Option<NonNull<VMFuncRef>>, 413 flags: Option<InstanceFlags>, 414 ) -> Result<HostResourceIndex> { 415 self.resource_tables() 416 .host_resource_lower_own(rep, dtor, flags) 417 } 418 419 /// Returns the underlying resource type for the `ty` table specified. 420 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 421 self.instance_type().resource_type(ty) 422 } 423 424 /// Returns the instance type information corresponding to the instance that 425 /// this context is lowering into. 426 pub fn instance_type(&self) -> InstanceType<'_> { 427 InstanceType::new(self.instance()) 428 } 429 430 fn resource_tables(&mut self) -> HostResourceTables<'_> { 431 let (calls, host_table, host_resource_data, instance) = self 432 .store 433 .0 434 .component_resource_state_with_instance(self.instance); 435 HostResourceTables::from_parts( 436 ResourceTables { 437 host_table: Some(host_table), 438 calls, 439 guest: Some(instance.guest_tables()), 440 }, 441 host_resource_data, 442 ) 443 } 444 445 /// See [`HostResourceTables::enter_call`]. 446 #[inline] 447 pub fn enter_call(&mut self) { 448 self.resource_tables().enter_call() 449 } 450 451 /// See [`HostResourceTables::exit_call`]. 452 #[inline] 453 pub fn exit_call(&mut self) -> Result<()> { 454 self.resource_tables().exit_call() 455 } 456 } 457 458 /// Contextual information used when lifting a type from a component into the 459 /// host. 460 /// 461 /// This structure is the analogue of `LowerContext` except used during lifting 462 /// operations (or loading from memory). 463 #[doc(hidden)] 464 pub struct LiftContext<'a> { 465 /// Like lowering, lifting always has options configured. 466 pub options: &'a Options, 467 468 /// Instance type information, like with lowering. 469 pub types: &'a Arc<ComponentTypes>, 470 471 memory: Option<&'a [u8]>, 472 473 instance: Pin<&'a mut ComponentInstance>, 474 instance_handle: Instance, 475 476 host_table: &'a mut HandleTable, 477 host_resource_data: &'a mut HostResourceData, 478 479 calls: &'a mut CallContexts, 480 } 481 482 #[doc(hidden)] 483 impl<'a> LiftContext<'a> { 484 /// Creates a new lifting context given the provided context. 485 #[inline] 486 pub fn new( 487 store: &'a mut StoreOpaque, 488 options: &'a Options, 489 instance_handle: Instance, 490 ) -> LiftContext<'a> { 491 // From `&mut StoreOpaque` provided the goal here is to project out 492 // three different disjoint fields owned by the store: memory, 493 // `CallContexts`, and `HandleTable`. There's no native API for that 494 // so it's hacked around a bit. This unsafe pointer cast could be fixed 495 // with more methods in more places, but it doesn't seem worth doing it 496 // at this time. 497 let memory = options 498 .memory 499 .map(|_| options.memory(unsafe { &*(store as *const StoreOpaque) })); 500 let (calls, host_table, host_resource_data, instance) = 501 store.component_resource_state_with_instance(instance_handle); 502 let (component, instance) = instance.component_and_self(); 503 504 LiftContext { 505 memory, 506 options, 507 types: component.types(), 508 instance, 509 instance_handle, 510 calls, 511 host_table, 512 host_resource_data, 513 } 514 } 515 516 /// Returns the entire contents of linear memory for this set of lifting 517 /// options. 518 /// 519 /// # Panics 520 /// 521 /// This will panic if memory has not been configured for this lifting 522 /// operation. 523 pub fn memory(&self) -> &'a [u8] { 524 self.memory.unwrap() 525 } 526 527 /// Returns an identifier for the store from which this `LiftContext` was 528 /// created. 529 pub fn store_id(&self) -> StoreId { 530 self.options.store_id 531 } 532 533 /// Returns the component instance that is being lifted from. 534 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> { 535 self.instance.as_mut() 536 } 537 /// Returns the component instance that is being lifted from. 538 pub fn instance_handle(&self) -> Instance { 539 self.instance_handle 540 } 541 542 /// Lifts an `own` resource from the guest at the `idx` specified into its 543 /// representation. 544 /// 545 /// Additionally returns a destructor/instance flags to go along with the 546 /// representation so the host knows how to destroy this resource. 547 pub fn guest_resource_lift_own( 548 &mut self, 549 ty: TypeResourceTableIndex, 550 idx: u32, 551 ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<InstanceFlags>)> { 552 let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?; 553 let (dtor, flags) = self.instance.dtor_and_flags(ty); 554 Ok((idx, dtor, flags)) 555 } 556 557 /// Lifts a `borrow` resource from the guest at the `idx` specified. 558 pub fn guest_resource_lift_borrow( 559 &mut self, 560 ty: TypeResourceTableIndex, 561 idx: u32, 562 ) -> Result<u32> { 563 self.resource_tables().guest_resource_lift_borrow(idx, ty) 564 } 565 566 /// Lowers a resource into the host-owned table, returning the index it was 567 /// inserted at. 568 pub fn host_resource_lower_own( 569 &mut self, 570 rep: u32, 571 dtor: Option<NonNull<VMFuncRef>>, 572 flags: Option<InstanceFlags>, 573 ) -> Result<HostResourceIndex> { 574 self.resource_tables() 575 .host_resource_lower_own(rep, dtor, flags) 576 } 577 578 /// Lowers a resource into the host-owned table, returning the index it was 579 /// inserted at. 580 pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> { 581 self.resource_tables().host_resource_lower_borrow(rep) 582 } 583 584 /// Returns the underlying type of the resource table specified by `ty`. 585 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 586 self.instance_type().resource_type(ty) 587 } 588 589 /// Returns instance type information for the component instance that is 590 /// being lifted from. 591 pub fn instance_type(&self) -> InstanceType<'_> { 592 InstanceType::new(&self.instance) 593 } 594 595 fn resource_tables(&mut self) -> HostResourceTables<'_> { 596 HostResourceTables::from_parts( 597 ResourceTables { 598 host_table: Some(self.host_table), 599 calls: self.calls, 600 // Note that the unsafety here should be valid given the contract of 601 // `LiftContext::new`. 602 guest: Some(self.instance.as_mut().guest_tables()), 603 }, 604 self.host_resource_data, 605 ) 606 } 607 608 /// See [`HostResourceTables::enter_call`]. 609 #[inline] 610 pub fn enter_call(&mut self) { 611 self.resource_tables().enter_call() 612 } 613 614 /// See [`HostResourceTables::exit_call`]. 615 #[inline] 616 pub fn exit_call(&mut self) -> Result<()> { 617 self.resource_tables().exit_call() 618 } 619 } 620