1 use crate::StoreContextMut; 2 use crate::component::concurrent::ConcurrentState; 3 use crate::component::matching::InstanceType; 4 use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables}; 5 use crate::component::{Instance, ResourceType}; 6 use crate::prelude::*; 7 use crate::runtime::vm::VMFuncRef; 8 use crate::runtime::vm::component::{ 9 CallContexts, ComponentInstance, HandleTable, InstanceFlags, ResourceTables, 10 }; 11 use crate::store::{StoreId, StoreOpaque}; 12 use alloc::sync::Arc; 13 use core::pin::Pin; 14 use core::ptr::NonNull; 15 use wasmtime_environ::component::{ 16 CanonicalOptions, CanonicalOptionsDataModel, ComponentTypes, OptionsIndex, 17 TypeResourceTableIndex, 18 }; 19 20 /// A helper structure which is a "package" of the context used during lowering 21 /// values into a component (or storing them into memory). 22 /// 23 /// This type is used by the `Lower` trait extensively and contains any 24 /// contextual information necessary related to the context in which the 25 /// lowering is happening. 26 #[doc(hidden)] 27 pub struct LowerContext<'a, T: 'static> { 28 /// Lowering may involve invoking memory allocation functions so part of the 29 /// context here is carrying access to the entire store that wasm is 30 /// executing within. This store serves as proof-of-ability to actually 31 /// execute wasm safely. 32 pub store: StoreContextMut<'a, T>, 33 34 /// Lowering always happens into a function that's been `canon lift`'d or 35 /// `canon lower`'d, both of which specify a set of options for the 36 /// canonical ABI. For example details like string encoding are contained 37 /// here along with which memory pointers are relative to or what the memory 38 /// allocation function is. 39 options: OptionsIndex, 40 41 /// Lowering happens within the context of a component instance and this 42 /// field stores the type information of that component instance. This is 43 /// used for type lookups and general type queries during the 44 /// lifting/lowering process. 45 pub types: &'a ComponentTypes, 46 47 /// Index of the component instance that's being lowered into. 48 instance: Instance, 49 50 /// Whether to allow `options.realloc` to be used when lowering. 51 allow_realloc: bool, 52 } 53 54 #[doc(hidden)] 55 impl<'a, T: 'static> LowerContext<'a, T> { 56 /// Creates a new lowering context from the specified parameters. 57 pub fn new( 58 store: StoreContextMut<'a, T>, 59 options: OptionsIndex, 60 instance: Instance, 61 ) -> LowerContext<'a, T> { 62 #[cfg(all(debug_assertions, feature = "component-model-async"))] 63 if store.engine().config().async_support { 64 // Assert that we're running on a fiber, which is necessary in 65 // case we call the guest's realloc function. 66 store.0.with_blocking(|_, _| {}); 67 } 68 let (component, store) = instance.component_and_store_mut(store.0); 69 LowerContext { 70 store: StoreContextMut(store), 71 options, 72 types: component.types(), 73 instance, 74 allow_realloc: true, 75 } 76 } 77 78 /// Like `new`, except disallows use of `options.realloc`. 79 /// 80 /// The returned object will panic if its `realloc` method is called. 81 /// 82 /// This is meant for use when lowering "flat" values (i.e. values which 83 /// require no allocations) into already-allocated memory or into stack 84 /// slots, in which case the lowering may safely be done outside of a fiber 85 /// since there is no need to make any guest calls. 86 #[cfg(feature = "component-model-async")] 87 pub(crate) fn new_without_realloc( 88 store: StoreContextMut<'a, T>, 89 options: OptionsIndex, 90 instance: Instance, 91 ) -> LowerContext<'a, T> { 92 let (component, store) = instance.component_and_store_mut(store.0); 93 LowerContext { 94 store: StoreContextMut(store), 95 options, 96 types: component.types(), 97 instance, 98 allow_realloc: false, 99 } 100 } 101 102 /// Returns the `&ComponentInstance` that's being lowered into. 103 pub fn instance(&self) -> &ComponentInstance { 104 self.instance.id().get(self.store.0) 105 } 106 107 /// Returns the `&mut ComponentInstance` that's being lowered into. 108 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> { 109 self.instance.id().get_mut(self.store.0) 110 } 111 112 /// Returns the canonical options that are being used during lifting. 113 pub fn options(&self) -> &CanonicalOptions { 114 &self.instance().component().env_component().options[self.options] 115 } 116 117 /// Returns a view into memory as a mutable slice of bytes. 118 /// 119 /// # Panics 120 /// 121 /// This will panic if memory has not been configured for this lowering 122 /// (e.g. it wasn't present during the specification of canonical options). 123 pub fn as_slice_mut(&mut self) -> &mut [u8] { 124 self.instance.options_memory_mut(self.store.0, self.options) 125 } 126 127 /// Invokes the memory allocation function (which is style after `realloc`) 128 /// with the specified parameters. 129 /// 130 /// # Panics 131 /// 132 /// This will panic if realloc hasn't been configured for this lowering via 133 /// its canonical options. 134 pub fn realloc( 135 &mut self, 136 old: usize, 137 old_size: usize, 138 old_align: u32, 139 new_size: usize, 140 ) -> Result<usize> { 141 assert!(self.allow_realloc); 142 143 let (component, store) = self.instance.component_and_store_mut(self.store.0); 144 let instance = self.instance.id().get(store); 145 let options = &component.env_component().options[self.options]; 146 let realloc_ty = component.realloc_func_ty(); 147 let realloc = match options.data_model { 148 CanonicalOptionsDataModel::Gc {} => unreachable!(), 149 CanonicalOptionsDataModel::LinearMemory(m) => m.realloc.unwrap(), 150 }; 151 let realloc = instance.runtime_realloc(realloc); 152 153 let params = ( 154 u32::try_from(old)?, 155 u32::try_from(old_size)?, 156 old_align, 157 u32::try_from(new_size)?, 158 ); 159 160 type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>; 161 162 // Invoke the wasm malloc function using its raw and statically known 163 // signature. 164 let result = unsafe { 165 ReallocFunc::call_raw(&mut StoreContextMut(store), &realloc_ty, realloc, params)? 166 }; 167 168 if result % old_align != 0 { 169 bail!("realloc return: result not aligned"); 170 } 171 let result = usize::try_from(result)?; 172 173 if self 174 .as_slice_mut() 175 .get_mut(result..) 176 .and_then(|s| s.get_mut(..new_size)) 177 .is_none() 178 { 179 bail!("realloc return: beyond end of memory") 180 } 181 182 Ok(result) 183 } 184 185 /// Returns a fixed mutable slice of memory `N` bytes large starting at 186 /// offset `N`, panicking on out-of-bounds. 187 /// 188 /// It should be previously verified that `offset` is in-bounds via 189 /// bounds-checks. 190 /// 191 /// # Panics 192 /// 193 /// This will panic if memory has not been configured for this lowering 194 /// (e.g. it wasn't present during the specification of canonical options). 195 pub fn get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N] { 196 // FIXME: this bounds check shouldn't actually be necessary, all 197 // callers of `ComponentType::store` have already performed a bounds 198 // check so we're guaranteed that `offset..offset+N` is in-bounds. That 199 // being said we at least should do bounds checks in debug mode and 200 // it's not clear to me how to easily structure this so that it's 201 // "statically obvious" the bounds check isn't necessary. 202 // 203 // For now I figure we can leave in this bounds check and if it becomes 204 // an issue we can optimize further later, probably with judicious use 205 // of `unsafe`. 206 self.as_slice_mut()[offset..].first_chunk_mut().unwrap() 207 } 208 209 /// Lowers an `own` resource into the guest, converting the `rep` specified 210 /// into a guest-local index. 211 /// 212 /// The `ty` provided is which table to put this into. 213 pub fn guest_resource_lower_own( 214 &mut self, 215 ty: TypeResourceTableIndex, 216 rep: u32, 217 ) -> Result<u32> { 218 self.resource_tables().guest_resource_lower_own(rep, ty) 219 } 220 221 /// Lowers a `borrow` resource into the guest, converting the `rep` to a 222 /// guest-local index in the `ty` table specified. 223 pub fn guest_resource_lower_borrow( 224 &mut self, 225 ty: TypeResourceTableIndex, 226 rep: u32, 227 ) -> Result<u32> { 228 // Implement `lower_borrow`'s special case here where if a borrow is 229 // inserted into a table owned by the instance which implemented the 230 // original resource then no borrow tracking is employed and instead the 231 // `rep` is returned "raw". 232 // 233 // This check is performed by comparing the owning instance of `ty` 234 // against the owning instance of the resource that `ty` is working 235 // with. 236 if self.instance().resource_owned_by_own_instance(ty) { 237 return Ok(rep); 238 } 239 self.resource_tables().guest_resource_lower_borrow(rep, ty) 240 } 241 242 /// Lifts a host-owned `own` resource at the `idx` specified into the 243 /// representation of that resource. 244 pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> { 245 self.resource_tables().host_resource_lift_own(idx) 246 } 247 248 /// Lifts a host-owned `borrow` resource at the `idx` specified into the 249 /// representation of that resource. 250 pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> { 251 self.resource_tables().host_resource_lift_borrow(idx) 252 } 253 254 /// Lowers a resource into the host-owned table, returning the index it was 255 /// inserted at. 256 /// 257 /// Note that this is a special case for `Resource<T>`. Most of the time a 258 /// host value shouldn't be lowered with a lowering context. 259 pub fn host_resource_lower_own( 260 &mut self, 261 rep: u32, 262 dtor: Option<NonNull<VMFuncRef>>, 263 flags: Option<InstanceFlags>, 264 ) -> Result<HostResourceIndex> { 265 self.resource_tables() 266 .host_resource_lower_own(rep, dtor, flags) 267 } 268 269 /// Returns the underlying resource type for the `ty` table specified. 270 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 271 self.instance_type().resource_type(ty) 272 } 273 274 /// Returns the instance type information corresponding to the instance that 275 /// this context is lowering into. 276 pub fn instance_type(&self) -> InstanceType<'_> { 277 InstanceType::new(self.instance()) 278 } 279 280 fn resource_tables(&mut self) -> HostResourceTables<'_> { 281 let (calls, host_table, host_resource_data, instance) = self 282 .store 283 .0 284 .component_resource_state_with_instance(self.instance); 285 HostResourceTables::from_parts( 286 ResourceTables { 287 host_table: Some(host_table), 288 calls, 289 guest: Some(instance.guest_tables()), 290 }, 291 host_resource_data, 292 ) 293 } 294 295 /// See [`HostResourceTables::enter_call`]. 296 #[inline] 297 pub fn enter_call(&mut self) { 298 self.resource_tables().enter_call() 299 } 300 301 /// See [`HostResourceTables::exit_call`]. 302 #[inline] 303 pub fn exit_call(&mut self) -> Result<()> { 304 self.resource_tables().exit_call() 305 } 306 } 307 308 /// Contextual information used when lifting a type from a component into the 309 /// host. 310 /// 311 /// This structure is the analogue of `LowerContext` except used during lifting 312 /// operations (or loading from memory). 313 #[doc(hidden)] 314 pub struct LiftContext<'a> { 315 store_id: StoreId, 316 /// Like lowering, lifting always has options configured. 317 options: OptionsIndex, 318 319 /// Instance type information, like with lowering. 320 pub types: &'a Arc<ComponentTypes>, 321 322 memory: &'a [u8], 323 324 instance: Pin<&'a mut ComponentInstance>, 325 instance_handle: Instance, 326 327 host_table: &'a mut HandleTable, 328 host_resource_data: &'a mut HostResourceData, 329 330 calls: &'a mut CallContexts, 331 332 #[cfg_attr( 333 not(feature = "component-model-async"), 334 allow(unused, reason = "easier to not #[cfg] away") 335 )] 336 concurrent_state: &'a mut ConcurrentState, 337 } 338 339 #[doc(hidden)] 340 impl<'a> LiftContext<'a> { 341 /// Creates a new lifting context given the provided context. 342 #[inline] 343 pub fn new( 344 store: &'a mut StoreOpaque, 345 options: OptionsIndex, 346 instance_handle: Instance, 347 ) -> LiftContext<'a> { 348 let store_id = store.id(); 349 // From `&mut StoreOpaque` provided the goal here is to project out 350 // three different disjoint fields owned by the store: memory, 351 // `CallContexts`, and `HandleTable`. There's no native API for that 352 // so it's hacked around a bit. This unsafe pointer cast could be fixed 353 // with more methods in more places, but it doesn't seem worth doing it 354 // at this time. 355 let memory = 356 instance_handle.options_memory(unsafe { &*(store as *const StoreOpaque) }, options); 357 let (calls, host_table, host_resource_data, instance, concurrent_state) = 358 store.component_resource_state_with_instance_and_concurrent_state(instance_handle); 359 let (component, instance) = instance.component_and_self(); 360 361 LiftContext { 362 store_id, 363 memory, 364 options, 365 types: component.types(), 366 instance, 367 instance_handle, 368 calls, 369 host_table, 370 host_resource_data, 371 concurrent_state, 372 } 373 } 374 375 /// Returns the canonical options that are being used during lifting. 376 pub fn options(&self) -> &CanonicalOptions { 377 &self.instance.component().env_component().options[self.options] 378 } 379 380 /// Returns the `OptionsIndex` being used during lifting. 381 pub fn options_index(&self) -> OptionsIndex { 382 self.options 383 } 384 385 /// Returns the entire contents of linear memory for this set of lifting 386 /// options. 387 /// 388 /// # Panics 389 /// 390 /// This will panic if memory has not been configured for this lifting 391 /// operation. 392 pub fn memory(&self) -> &'a [u8] { 393 self.memory 394 } 395 396 /// Returns an identifier for the store from which this `LiftContext` was 397 /// created. 398 pub fn store_id(&self) -> StoreId { 399 self.store_id 400 } 401 402 /// Returns the component instance that is being lifted from. 403 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> { 404 self.instance.as_mut() 405 } 406 /// Returns the component instance that is being lifted from. 407 pub fn instance_handle(&self) -> Instance { 408 self.instance_handle 409 } 410 411 #[cfg(feature = "component-model-async")] 412 pub(crate) fn concurrent_state_mut(&mut self) -> &mut ConcurrentState { 413 self.concurrent_state 414 } 415 416 /// Lifts an `own` resource from the guest at the `idx` specified into its 417 /// representation. 418 /// 419 /// Additionally returns a destructor/instance flags to go along with the 420 /// representation so the host knows how to destroy this resource. 421 pub fn guest_resource_lift_own( 422 &mut self, 423 ty: TypeResourceTableIndex, 424 idx: u32, 425 ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<InstanceFlags>)> { 426 let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?; 427 let (dtor, flags) = self.instance.dtor_and_flags(ty); 428 Ok((idx, dtor, flags)) 429 } 430 431 /// Lifts a `borrow` resource from the guest at the `idx` specified. 432 pub fn guest_resource_lift_borrow( 433 &mut self, 434 ty: TypeResourceTableIndex, 435 idx: u32, 436 ) -> Result<u32> { 437 self.resource_tables().guest_resource_lift_borrow(idx, ty) 438 } 439 440 /// Lowers a resource into the host-owned table, returning the index it was 441 /// inserted at. 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 self.resource_tables() 449 .host_resource_lower_own(rep, dtor, flags) 450 } 451 452 /// Lowers a resource into the host-owned table, returning the index it was 453 /// inserted at. 454 pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> { 455 self.resource_tables().host_resource_lower_borrow(rep) 456 } 457 458 /// Returns the underlying type of the resource table specified by `ty`. 459 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 460 self.instance_type().resource_type(ty) 461 } 462 463 /// Returns instance type information for the component instance that is 464 /// being lifted from. 465 pub fn instance_type(&self) -> InstanceType<'_> { 466 InstanceType::new(&self.instance) 467 } 468 469 fn resource_tables(&mut self) -> HostResourceTables<'_> { 470 HostResourceTables::from_parts( 471 ResourceTables { 472 host_table: Some(self.host_table), 473 calls: self.calls, 474 // Note that the unsafety here should be valid given the contract of 475 // `LiftContext::new`. 476 guest: Some(self.instance.as_mut().guest_tables()), 477 }, 478 self.host_resource_data, 479 ) 480 } 481 482 /// See [`HostResourceTables::enter_call`]. 483 #[inline] 484 pub fn enter_call(&mut self) { 485 self.resource_tables().enter_call() 486 } 487 488 /// See [`HostResourceTables::exit_call`]. 489 #[inline] 490 pub fn exit_call(&mut self) -> Result<()> { 491 self.resource_tables().exit_call() 492 } 493 } 494