1 use crate::StoreContextMut; 2 #[cfg(feature = "component-model-async")] 3 use crate::component::concurrent::ConcurrentState; 4 use crate::component::matching::InstanceType; 5 use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables}; 6 use crate::component::store::ComponentTaskState; 7 use crate::component::{Instance, ResourceType, RuntimeInstance}; 8 use crate::prelude::*; 9 use crate::runtime::vm::VMFuncRef; 10 use crate::runtime::vm::component::{ComponentInstance, HandleTable, ResourceTables}; 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 // Debug-assert that if we can't block that blocking is indeed allowed. 63 // This'll catch when this is accidentally created outside of a fiber 64 // when we need to be on a fiber. 65 if cfg!(debug_assertions) && !store.0.can_block() { 66 store.0.validate_sync_call().unwrap(); 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 instance: Option<RuntimeInstance>, 264 ) -> Result<HostResourceIndex> { 265 self.resource_tables() 266 .host_resource_lower_own(rep, dtor, instance) 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 (tables, data) = self 282 .store 283 .0 284 .component_resource_tables_and_host_resource_data(Some(self.instance)); 285 HostResourceTables::from_parts(tables, data) 286 } 287 288 /// See [`HostResourceTables::validate_scope_exit`]. 289 #[inline] 290 pub fn validate_scope_exit(&mut self) -> Result<()> { 291 self.resource_tables().validate_scope_exit() 292 } 293 } 294 295 /// Contextual information used when lifting a type from a component into the 296 /// host. 297 /// 298 /// This structure is the analogue of `LowerContext` except used during lifting 299 /// operations (or loading from memory). 300 #[doc(hidden)] 301 pub struct LiftContext<'a> { 302 store_id: StoreId, 303 /// Like lowering, lifting always has options configured. 304 options: OptionsIndex, 305 306 /// Instance type information, like with lowering. 307 pub types: &'a Arc<ComponentTypes>, 308 309 memory: &'a [u8], 310 311 instance: Pin<&'a mut ComponentInstance>, 312 instance_handle: Instance, 313 314 host_table: &'a mut HandleTable, 315 host_resource_data: &'a mut HostResourceData, 316 317 task_state: &'a mut ComponentTaskState, 318 } 319 320 #[doc(hidden)] 321 impl<'a> LiftContext<'a> { 322 /// Creates a new lifting context given the provided context. 323 #[inline] 324 pub fn new( 325 store: &'a mut StoreOpaque, 326 options: OptionsIndex, 327 instance_handle: Instance, 328 ) -> LiftContext<'a> { 329 let store_id = store.id(); 330 // From `&mut StoreOpaque` provided the goal here is to project out 331 // three different disjoint fields owned by the store: memory, 332 // `CallContexts`, and `HandleTable`. There's no native API for that 333 // so it's hacked around a bit. This unsafe pointer cast could be fixed 334 // with more methods in more places, but it doesn't seem worth doing it 335 // at this time. 336 let memory = 337 instance_handle.options_memory(unsafe { &*(store as *const StoreOpaque) }, options); 338 let (task_state, host_table, host_resource_data, instance) = 339 store.lift_context_parts(instance_handle); 340 let (component, instance) = instance.component_and_self(); 341 342 LiftContext { 343 store_id, 344 memory, 345 options, 346 types: component.types(), 347 instance, 348 instance_handle, 349 task_state, 350 host_table, 351 host_resource_data, 352 } 353 } 354 355 /// Returns the canonical options that are being used during lifting. 356 pub fn options(&self) -> &CanonicalOptions { 357 &self.instance.component().env_component().options[self.options] 358 } 359 360 /// Returns the `OptionsIndex` being used during lifting. 361 pub fn options_index(&self) -> OptionsIndex { 362 self.options 363 } 364 365 /// Returns the entire contents of linear memory for this set of lifting 366 /// options. 367 /// 368 /// # Panics 369 /// 370 /// This will panic if memory has not been configured for this lifting 371 /// operation. 372 pub fn memory(&self) -> &'a [u8] { 373 self.memory 374 } 375 376 /// Returns an identifier for the store from which this `LiftContext` was 377 /// created. 378 pub fn store_id(&self) -> StoreId { 379 self.store_id 380 } 381 382 /// Returns the component instance that is being lifted from. 383 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> { 384 self.instance.as_mut() 385 } 386 /// Returns the component instance that is being lifted from. 387 pub fn instance_handle(&self) -> Instance { 388 self.instance_handle 389 } 390 391 #[cfg(feature = "component-model-async")] 392 pub(crate) fn concurrent_state_mut(&mut self) -> &mut ConcurrentState { 393 self.task_state.concurrent_state_mut() 394 } 395 396 /// Lifts an `own` resource from the guest at the `idx` specified into its 397 /// representation. 398 /// 399 /// Additionally returns a destructor/instance flags to go along with the 400 /// representation so the host knows how to destroy this resource. 401 pub fn guest_resource_lift_own( 402 &mut self, 403 ty: TypeResourceTableIndex, 404 idx: u32, 405 ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<RuntimeInstance>)> { 406 let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?; 407 let (dtor, instance) = self.instance.dtor_and_instance(ty); 408 Ok((idx, dtor, instance)) 409 } 410 411 /// Lifts a `borrow` resource from the guest at the `idx` specified. 412 pub fn guest_resource_lift_borrow( 413 &mut self, 414 ty: TypeResourceTableIndex, 415 idx: u32, 416 ) -> Result<u32> { 417 self.resource_tables().guest_resource_lift_borrow(idx, ty) 418 } 419 420 /// Lowers a resource into the host-owned table, returning the index it was 421 /// inserted at. 422 pub fn host_resource_lower_own( 423 &mut self, 424 rep: u32, 425 dtor: Option<NonNull<VMFuncRef>>, 426 instance: Option<RuntimeInstance>, 427 ) -> Result<HostResourceIndex> { 428 self.resource_tables() 429 .host_resource_lower_own(rep, dtor, instance) 430 } 431 432 /// Lowers a resource into the host-owned table, returning the index it was 433 /// inserted at. 434 pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> { 435 self.resource_tables().host_resource_lower_borrow(rep) 436 } 437 438 /// Returns the underlying type of the resource table specified by `ty`. 439 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 440 self.instance_type().resource_type(ty) 441 } 442 443 /// Returns instance type information for the component instance that is 444 /// being lifted from. 445 pub fn instance_type(&self) -> InstanceType<'_> { 446 InstanceType::new(&self.instance) 447 } 448 449 fn resource_tables(&mut self) -> HostResourceTables<'_> { 450 HostResourceTables::from_parts( 451 ResourceTables { 452 host_table: self.host_table, 453 task_state: self.task_state, 454 guest: Some(self.instance.as_mut().instance_states()), 455 }, 456 self.host_resource_data, 457 ) 458 } 459 460 /// See [`HostResourceTables::validate_scope_exit`]. 461 #[inline] 462 pub fn validate_scope_exit(&mut self) -> Result<()> { 463 self.resource_tables().validate_scope_exit() 464 } 465 } 466