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