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