1 //! Runtime support for the component model in Wasmtime 2 //! 3 //! Currently this runtime support includes a `VMComponentContext` which is 4 //! similar in purpose to `VMContext`. The context is read from 5 //! cranelift-generated trampolines when entering the host from a wasm module. 6 //! Eventually it's intended that module-to-module calls, which would be 7 //! cranelift-compiled adapters, will use this `VMComponentContext` as well. 8 9 use crate::component::{Component, InstancePre, ResourceType, RuntimeImport}; 10 use crate::prelude::*; 11 use crate::runtime::component::ComponentInstanceId; 12 use crate::runtime::vm::{ 13 Export, ExportFunction, ExportGlobal, ExportGlobalKind, SendSyncPtr, VMArrayCallFunction, 14 VMContext, VMFuncRef, VMGlobalDefinition, VMMemoryDefinition, VMOpaqueContext, VMStore, 15 VMStoreRawPtr, VMTableDefinition, VMTableImport, VMWasmCallFunction, ValRaw, VmPtr, VmSafe, 16 }; 17 use crate::store::{InstanceId, StoreOpaque}; 18 use alloc::alloc::Layout; 19 use alloc::sync::Arc; 20 use core::marker; 21 use core::mem; 22 use core::mem::offset_of; 23 use core::ops::Deref; 24 use core::ptr::{self, NonNull}; 25 use wasmtime_environ::component::*; 26 use wasmtime_environ::{ 27 DefinedTableIndex, EntityIndex, Global, HostPtr, PrimaryMap, VMSharedTypeIndex, WasmValType, 28 }; 29 30 #[allow(clippy::cast_possible_truncation)] // it's intended this is truncated on 31 // 32-bit platforms 32 const INVALID_PTR: usize = 0xdead_dead_beef_beef_u64 as usize; 33 34 mod libcalls; 35 mod resources; 36 37 pub use self::resources::{ 38 CallContexts, ResourceTable, ResourceTables, TypedResource, TypedResourceIndex, 39 }; 40 41 /// Runtime representation of a component instance and all state necessary for 42 /// the instance itself. 43 /// 44 /// This type never exists by-value, but rather it's always behind a pointer. 45 /// The size of the allocation for `ComponentInstance` includes the trailing 46 /// `VMComponentContext` which is variably sized based on the `offsets` 47 /// contained within. 48 #[repr(C)] 49 pub struct ComponentInstance { 50 /// The index within the store of where to find this component instance. 51 id: ComponentInstanceId, 52 53 /// Size and offset information for the trailing `VMComponentContext`. 54 offsets: VMComponentOffsets<HostPtr>, 55 56 /// For more information about this see the documentation on 57 /// `Instance::vmctx_self_reference`. 58 vmctx_self_reference: SendSyncPtr<VMComponentContext>, 59 60 /// The component that this instance was created from. 61 // 62 // NB: in the future if necessary it would be possible to avoid storing an 63 // entire `Component` here and instead storing only information such as: 64 // 65 // * Some reference to `Arc<ComponentTypes>` 66 // * Necessary references to closed-over modules which are exported from the 67 // component itself. 68 // 69 // Otherwise the full guts of this component should only ever be used during 70 // the instantiation of this instance, meaning that after instantiation much 71 // of the component can be thrown away (theoretically). 72 component: Component, 73 74 /// State of resources for this component. 75 /// 76 /// This is paired with other information to create a `ResourceTables` which 77 /// is how this field is manipulated. 78 instance_resource_tables: PrimaryMap<RuntimeComponentInstanceIndex, ResourceTable>, 79 80 /// What all compile-time-identified core instances are mapped to within the 81 /// `Store` that this component belongs to. 82 instances: PrimaryMap<RuntimeInstanceIndex, InstanceId>, 83 84 /// Storage for the type information about resources within this component 85 /// instance. 86 resource_types: Arc<PrimaryMap<ResourceIndex, ResourceType>>, 87 88 /// Arguments that this instance used to be instantiated. 89 /// 90 /// Strong references are stored to these arguments since pointers are saved 91 /// into the structures such as functions within the 92 /// `OwnedComponentInstance` but it's our job to keep them alive. 93 /// 94 /// One purpose of this storage is to enable embedders to drop a `Linker`, 95 /// for example, after a component is instantiated. In that situation if the 96 /// arguments weren't held here then they might be dropped, and structures 97 /// such as `.lowering()` which point back into the original function would 98 /// become stale and use-after-free conditions when used. By preserving the 99 /// entire list here though we're guaranteed that nothing is lost for the 100 /// duration of the lifetime of this instance. 101 imports: Arc<PrimaryMap<RuntimeImportIndex, RuntimeImport>>, 102 103 /// Self-pointer back to `Store<T>` and its functions. 104 store: VMStoreRawPtr, 105 106 /// Cached ABI return value from the last-invoked function call along with 107 /// the function index that was invoked. 108 /// 109 /// Used in `post_return_arg_set` and `post_return_arg_take` below. 110 post_return_arg: Option<(ExportIndex, ValRaw)>, 111 112 /// A zero-sized field which represents the end of the struct for the actual 113 /// `VMComponentContext` to be allocated behind. 114 vmctx: VMComponentContext, 115 } 116 117 /// Type signature for host-defined trampolines that are called from 118 /// WebAssembly. 119 /// 120 /// This function signature is invoked from a cranelift-compiled trampoline that 121 /// adapts from the core wasm System-V ABI into the ABI provided here: 122 /// 123 /// * `vmctx` - this is the first argument to the wasm import, and should always 124 /// end up being a `VMComponentContext`. 125 /// * `data` - this is the data pointer associated with the `VMLowering` for 126 /// which this function pointer was registered. 127 /// * `ty` - the type index, relative to the tables in `vmctx`, that is the 128 /// type of the function being called. 129 /// * `caller_instance` - the `RuntimeComponentInstanceIndex` representing the 130 /// caller component instance, used to track the owner of an async host task. 131 /// * `flags` - the component flags for may_enter/leave corresponding to the 132 /// component instance that the lowering happened within. 133 /// * `opt_memory` - this nullable pointer represents the memory configuration 134 /// option for the canonical ABI options. 135 /// * `opt_realloc` - this nullable pointer represents the realloc configuration 136 /// option for the canonical ABI options. 137 /// * `string_encoding` - this is the configured string encoding for the 138 /// canonical ABI this lowering corresponds to. 139 /// * `async_` - whether the caller is using the async ABI. 140 /// * `args_and_results` - pointer to stack-allocated space in the caller where 141 /// all the arguments are stored as well as where the results will be written 142 /// to. The size and initialized bytes of this depends on the core wasm type 143 /// signature that this callee corresponds to. 144 /// * `nargs_and_results` - the size, in units of `ValRaw`, of 145 /// `args_and_results`. 146 /// 147 /// This function returns a `bool` which indicates whether the call succeeded 148 /// or not. On failure this function records trap information in TLS which 149 /// should be suitable for reading later. 150 // 151 // FIXME: 11 arguments is probably too many. The `data` through `string-encoding` 152 // parameters should probably get packaged up into the `VMComponentContext`. 153 // Needs benchmarking one way or another though to figure out what the best 154 // balance is here. 155 pub type VMLoweringCallee = extern "C" fn( 156 vmctx: NonNull<VMOpaqueContext>, 157 data: NonNull<u8>, 158 ty: u32, 159 caller_instance: u32, 160 flags: NonNull<VMGlobalDefinition>, 161 opt_memory: *mut VMMemoryDefinition, 162 opt_realloc: *mut VMFuncRef, 163 string_encoding: u8, 164 async_: u8, 165 args_and_results: NonNull<mem::MaybeUninit<ValRaw>>, 166 nargs_and_results: usize, 167 ) -> bool; 168 169 /// Structure describing a lowered host function stored within a 170 /// `VMComponentContext` per-lowering. 171 #[derive(Copy, Clone)] 172 #[repr(C)] 173 pub struct VMLowering { 174 /// The host function pointer that is invoked when this lowering is 175 /// invoked. 176 pub callee: VMLoweringCallee, 177 /// The host data pointer (think void* pointer) to get passed to `callee`. 178 pub data: VmPtr<u8>, 179 } 180 181 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 182 unsafe impl VmSafe for VMLowering {} 183 184 /// This is a marker type to represent the underlying allocation of a 185 /// `VMComponentContext`. 186 /// 187 /// This type is similar to `VMContext` for core wasm and is allocated once per 188 /// component instance in Wasmtime. While the static size of this type is 0 the 189 /// actual runtime size is variable depending on the shape of the component that 190 /// this corresponds to. This structure always trails a `ComponentInstance` 191 /// allocation and the allocation/lifetime of this allocation is managed by 192 /// `ComponentInstance`. 193 #[repr(C)] 194 // Set an appropriate alignment for this structure where the most-aligned value 195 // internally right now `VMGlobalDefinition` which has an alignment of 16 bytes. 196 #[repr(align(16))] 197 pub struct VMComponentContext { 198 /// For more information about this see the equivalent field in `VMContext` 199 _marker: marker::PhantomPinned, 200 } 201 202 impl ComponentInstance { 203 /// Converts the `vmctx` provided into a `ComponentInstance` and runs the 204 /// provided closure with that instance. 205 /// 206 /// # Unsafety 207 /// 208 /// This is `unsafe` because `vmctx` cannot be guaranteed to be a valid 209 /// pointer and it cannot be proven statically that it's safe to get a 210 /// mutable reference at this time to the instance from `vmctx`. 211 pub unsafe fn from_vmctx<R>( 212 vmctx: NonNull<VMComponentContext>, 213 f: impl FnOnce(&mut ComponentInstance) -> R, 214 ) -> R { 215 let mut ptr = vmctx 216 .byte_sub(mem::size_of::<ComponentInstance>()) 217 .cast::<ComponentInstance>(); 218 f(ptr.as_mut()) 219 } 220 221 /// Returns the layout corresponding to what would be an allocation of a 222 /// `ComponentInstance` for the `offsets` provided. 223 /// 224 /// The returned layout has space for both the `ComponentInstance` and the 225 /// trailing `VMComponentContext`. 226 fn alloc_layout(offsets: &VMComponentOffsets<HostPtr>) -> Layout { 227 let size = mem::size_of::<Self>() 228 .checked_add(usize::try_from(offsets.size_of_vmctx()).unwrap()) 229 .unwrap(); 230 let align = mem::align_of::<Self>(); 231 Layout::from_size_align(size, align).unwrap() 232 } 233 234 /// Initializes an uninitialized pointer to a `ComponentInstance` in 235 /// addition to its trailing `VMComponentContext`. 236 /// 237 /// The `ptr` provided must be valid for `alloc_size` bytes and will be 238 /// entirely overwritten by this function call. The `offsets` correspond to 239 /// the shape of the component being instantiated and `store` is a pointer 240 /// back to the Wasmtime store for host functions to have access to. 241 unsafe fn new_at( 242 ptr: NonNull<ComponentInstance>, 243 alloc_size: usize, 244 offsets: VMComponentOffsets<HostPtr>, 245 id: ComponentInstanceId, 246 component: &Component, 247 resource_types: Arc<PrimaryMap<ResourceIndex, ResourceType>>, 248 imports: &Arc<PrimaryMap<RuntimeImportIndex, RuntimeImport>>, 249 store: NonNull<dyn VMStore>, 250 ) { 251 assert!(alloc_size >= Self::alloc_layout(&offsets).size()); 252 253 let num_instances = component.env_component().num_runtime_component_instances; 254 let mut instance_resource_tables = 255 PrimaryMap::with_capacity(num_instances.try_into().unwrap()); 256 for _ in 0..num_instances { 257 instance_resource_tables.push(ResourceTable::default()); 258 } 259 260 ptr::write( 261 ptr.as_ptr(), 262 ComponentInstance { 263 id, 264 offsets, 265 vmctx_self_reference: SendSyncPtr::new( 266 NonNull::new( 267 ptr.as_ptr() 268 .byte_add(mem::size_of::<ComponentInstance>()) 269 .cast(), 270 ) 271 .unwrap(), 272 ), 273 instance_resource_tables, 274 instances: PrimaryMap::with_capacity( 275 component 276 .env_component() 277 .num_runtime_instances 278 .try_into() 279 .unwrap(), 280 ), 281 component: component.clone(), 282 resource_types, 283 imports: imports.clone(), 284 store: VMStoreRawPtr(store), 285 post_return_arg: None, 286 vmctx: VMComponentContext { 287 _marker: marker::PhantomPinned, 288 }, 289 }, 290 ); 291 292 (*ptr.as_ptr()).initialize_vmctx(); 293 } 294 295 pub fn vmctx(&self) -> NonNull<VMComponentContext> { 296 let addr = &raw const self.vmctx; 297 let ret = self.vmctx_self_reference.as_ptr().with_addr(addr.addr()); 298 NonNull::new(ret).unwrap() 299 } 300 301 unsafe fn vmctx_plus_offset<T: VmSafe>(&self, offset: u32) -> *const T { 302 self.vmctx() 303 .as_ptr() 304 .byte_add(usize::try_from(offset).unwrap()) 305 .cast() 306 } 307 308 unsafe fn vmctx_plus_offset_mut<T: VmSafe>(&mut self, offset: u32) -> *mut T { 309 self.vmctx() 310 .as_ptr() 311 .byte_add(usize::try_from(offset).unwrap()) 312 .cast() 313 } 314 315 /// Returns a pointer to the "may leave" flag for this instance specified 316 /// for canonical lowering and lifting operations. 317 #[inline] 318 pub fn instance_flags(&self, instance: RuntimeComponentInstanceIndex) -> InstanceFlags { 319 unsafe { 320 let ptr = self 321 .vmctx_plus_offset::<VMGlobalDefinition>(self.offsets.instance_flags(instance)) 322 .cast_mut(); 323 InstanceFlags(SendSyncPtr::new(NonNull::new(ptr).unwrap())) 324 } 325 } 326 327 /// Returns the store that this component was created with. 328 pub fn store(&self) -> *mut dyn VMStore { 329 self.store.0.as_ptr() 330 } 331 332 /// Returns the runtime memory definition corresponding to the index of the 333 /// memory provided. 334 /// 335 /// This can only be called after `idx` has been initialized at runtime 336 /// during the instantiation process of a component. 337 pub fn runtime_memory(&self, idx: RuntimeMemoryIndex) -> *mut VMMemoryDefinition { 338 unsafe { 339 let ret = *self.vmctx_plus_offset::<VmPtr<_>>(self.offsets.runtime_memory(idx)); 340 debug_assert!(ret.as_ptr() as usize != INVALID_PTR); 341 ret.as_ptr() 342 } 343 } 344 345 /// Returns the runtime table definition and associated instance `VMContext` 346 /// corresponding to the index of the table provided. 347 /// 348 /// This can only be called after `idx` has been initialized at runtime 349 /// during the instantiation process of a component. 350 pub fn runtime_table(&self, idx: RuntimeTableIndex) -> VMTableImport { 351 unsafe { 352 let ret = *self.vmctx_plus_offset::<VMTableImport>(self.offsets.runtime_table(idx)); 353 debug_assert!(ret.from.as_ptr() as usize != INVALID_PTR); 354 debug_assert!(ret.vmctx.as_ptr() as usize != INVALID_PTR); 355 ret 356 } 357 } 358 359 /// Returns the realloc pointer corresponding to the index provided. 360 /// 361 /// This can only be called after `idx` has been initialized at runtime 362 /// during the instantiation process of a component. 363 pub fn runtime_realloc(&self, idx: RuntimeReallocIndex) -> NonNull<VMFuncRef> { 364 unsafe { 365 let ret = *self.vmctx_plus_offset::<VmPtr<_>>(self.offsets.runtime_realloc(idx)); 366 debug_assert!(ret.as_ptr() as usize != INVALID_PTR); 367 ret.as_non_null() 368 } 369 } 370 371 /// Returns the post-return pointer corresponding to the index provided. 372 /// 373 /// This can only be called after `idx` has been initialized at runtime 374 /// during the instantiation process of a component. 375 pub fn runtime_post_return(&self, idx: RuntimePostReturnIndex) -> NonNull<VMFuncRef> { 376 unsafe { 377 let ret = *self.vmctx_plus_offset::<VmPtr<_>>(self.offsets.runtime_post_return(idx)); 378 debug_assert!(ret.as_ptr() as usize != INVALID_PTR); 379 ret.as_non_null() 380 } 381 } 382 383 /// Returns the host information for the lowered function at the index 384 /// specified. 385 /// 386 /// This can only be called after `idx` has been initialized at runtime 387 /// during the instantiation process of a component. 388 pub fn lowering(&self, idx: LoweredIndex) -> VMLowering { 389 unsafe { 390 let ret = *self.vmctx_plus_offset::<VMLowering>(self.offsets.lowering(idx)); 391 debug_assert!(ret.callee as usize != INVALID_PTR); 392 debug_assert!(ret.data.as_ptr() as usize != INVALID_PTR); 393 ret 394 } 395 } 396 397 /// Returns the core wasm `funcref` corresponding to the trampoline 398 /// specified. 399 /// 400 /// The returned function is suitable to pass directly to a wasm module 401 /// instantiation and the function contains cranelift-compiled trampolines. 402 /// 403 /// This can only be called after `idx` has been initialized at runtime 404 /// during the instantiation process of a component. 405 pub fn trampoline_func_ref(&self, idx: TrampolineIndex) -> NonNull<VMFuncRef> { 406 unsafe { 407 let offset = self.offsets.trampoline_func_ref(idx); 408 let ret = self.vmctx_plus_offset::<VMFuncRef>(offset); 409 debug_assert!( 410 mem::transmute::<Option<VmPtr<VMWasmCallFunction>>, usize>((*ret).wasm_call) 411 != INVALID_PTR 412 ); 413 debug_assert!((*ret).vmctx.as_ptr() as usize != INVALID_PTR); 414 NonNull::new(ret.cast_mut()).unwrap() 415 } 416 } 417 418 /// Stores the runtime memory pointer at the index specified. 419 /// 420 /// This is intended to be called during the instantiation process of a 421 /// component once a memory is available, which may not be until part-way 422 /// through component instantiation. 423 /// 424 /// Note that it should be a property of the component model that the `ptr` 425 /// here is never needed prior to it being configured here in the instance. 426 pub fn set_runtime_memory( 427 &mut self, 428 idx: RuntimeMemoryIndex, 429 ptr: NonNull<VMMemoryDefinition>, 430 ) { 431 unsafe { 432 let storage = self.vmctx_plus_offset_mut::<VmPtr<VMMemoryDefinition>>( 433 self.offsets.runtime_memory(idx), 434 ); 435 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 436 *storage = ptr.into(); 437 } 438 } 439 440 /// Same as `set_runtime_memory` but for realloc function pointers. 441 pub fn set_runtime_realloc(&mut self, idx: RuntimeReallocIndex, ptr: NonNull<VMFuncRef>) { 442 unsafe { 443 let storage = 444 self.vmctx_plus_offset_mut::<VmPtr<VMFuncRef>>(self.offsets.runtime_realloc(idx)); 445 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 446 *storage = ptr.into(); 447 } 448 } 449 450 /// Same as `set_runtime_memory` but for async callback function pointers. 451 pub fn set_runtime_callback(&mut self, idx: RuntimeCallbackIndex, ptr: NonNull<VMFuncRef>) { 452 unsafe { 453 let storage = 454 self.vmctx_plus_offset_mut::<VmPtr<VMFuncRef>>(self.offsets.runtime_callback(idx)); 455 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 456 *storage = ptr.into(); 457 } 458 } 459 460 /// Same as `set_runtime_memory` but for post-return function pointers. 461 pub fn set_runtime_post_return( 462 &mut self, 463 idx: RuntimePostReturnIndex, 464 ptr: NonNull<VMFuncRef>, 465 ) { 466 unsafe { 467 let storage = self 468 .vmctx_plus_offset_mut::<VmPtr<VMFuncRef>>(self.offsets.runtime_post_return(idx)); 469 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 470 *storage = ptr.into(); 471 } 472 } 473 474 /// Stores the runtime table pointer at the index specified. 475 /// 476 /// This is intended to be called during the instantiation process of a 477 /// component once a table is available, which may not be until part-way 478 /// through component instantiation. 479 /// 480 /// Note that it should be a property of the component model that the `ptr` 481 /// here is never needed prior to it being configured here in the instance. 482 pub fn set_runtime_table( 483 &mut self, 484 idx: RuntimeTableIndex, 485 ptr: NonNull<VMTableDefinition>, 486 vmctx: NonNull<VMContext>, 487 index: DefinedTableIndex, 488 ) { 489 unsafe { 490 let storage = 491 self.vmctx_plus_offset_mut::<VMTableImport>(self.offsets.runtime_table(idx)); 492 debug_assert!((*storage).vmctx.as_ptr() as usize == INVALID_PTR); 493 debug_assert!((*storage).from.as_ptr() as usize == INVALID_PTR); 494 *storage = VMTableImport { 495 vmctx: vmctx.into(), 496 from: ptr.into(), 497 index, 498 }; 499 } 500 } 501 502 /// Configures host runtime lowering information associated with imported f 503 /// functions for the `idx` specified. 504 pub fn set_lowering(&mut self, idx: LoweredIndex, lowering: VMLowering) { 505 unsafe { 506 debug_assert!( 507 *self.vmctx_plus_offset::<usize>(self.offsets.lowering_callee(idx)) == INVALID_PTR 508 ); 509 debug_assert!( 510 *self.vmctx_plus_offset::<usize>(self.offsets.lowering_data(idx)) == INVALID_PTR 511 ); 512 *self.vmctx_plus_offset_mut(self.offsets.lowering(idx)) = lowering; 513 } 514 } 515 516 /// Same as `set_lowering` but for the resource.drop functions. 517 pub fn set_trampoline( 518 &mut self, 519 idx: TrampolineIndex, 520 wasm_call: NonNull<VMWasmCallFunction>, 521 array_call: NonNull<VMArrayCallFunction>, 522 type_index: VMSharedTypeIndex, 523 ) { 524 unsafe { 525 let offset = self.offsets.trampoline_func_ref(idx); 526 debug_assert!(*self.vmctx_plus_offset::<usize>(offset) == INVALID_PTR); 527 let vmctx = VMOpaqueContext::from_vmcomponent(self.vmctx()); 528 *self.vmctx_plus_offset_mut(offset) = VMFuncRef { 529 wasm_call: Some(wasm_call.into()), 530 array_call: array_call.into(), 531 type_index, 532 vmctx: vmctx.into(), 533 }; 534 } 535 } 536 537 /// Configures the destructor for a resource at the `idx` specified. 538 /// 539 /// This is required to be called for each resource as it's defined within a 540 /// component during the instantiation process. 541 pub fn set_resource_destructor( 542 &mut self, 543 idx: ResourceIndex, 544 dtor: Option<NonNull<VMFuncRef>>, 545 ) { 546 unsafe { 547 let offset = self.offsets.resource_destructor(idx); 548 debug_assert!(*self.vmctx_plus_offset::<usize>(offset) == INVALID_PTR); 549 *self.vmctx_plus_offset_mut(offset) = dtor.map(VmPtr::from); 550 } 551 } 552 553 /// Returns the destructor, if any, for `idx`. 554 /// 555 /// This is only valid to call after `set_resource_destructor`, or typically 556 /// after instantiation. 557 pub fn resource_destructor(&self, idx: ResourceIndex) -> Option<NonNull<VMFuncRef>> { 558 unsafe { 559 let offset = self.offsets.resource_destructor(idx); 560 debug_assert!(*self.vmctx_plus_offset::<usize>(offset) != INVALID_PTR); 561 (*self.vmctx_plus_offset::<Option<VmPtr<VMFuncRef>>>(offset)).map(|p| p.as_non_null()) 562 } 563 } 564 565 unsafe fn initialize_vmctx(&mut self) { 566 *self.vmctx_plus_offset_mut(self.offsets.magic()) = VMCOMPONENT_MAGIC; 567 *self.vmctx_plus_offset_mut(self.offsets.builtins()) = 568 VmPtr::from(NonNull::from(&libcalls::VMComponentBuiltins::INIT)); 569 *self.vmctx_plus_offset_mut(self.offsets.vm_store_context()) = 570 VmPtr::from(self.store.0.as_ref().vm_store_context_ptr()); 571 572 for i in 0..self.offsets.num_runtime_component_instances { 573 let i = RuntimeComponentInstanceIndex::from_u32(i); 574 let mut def = VMGlobalDefinition::new(); 575 *def.as_i32_mut() = FLAG_MAY_ENTER | FLAG_MAY_LEAVE; 576 self.instance_flags(i).as_raw().write(def); 577 } 578 579 // In debug mode set non-null bad values to all "pointer looking" bits 580 // and pices related to lowering and such. This'll help detect any 581 // erroneous usage and enable debug assertions above as well to prevent 582 // loading these before they're configured or setting them twice. 583 if cfg!(debug_assertions) { 584 for i in 0..self.offsets.num_lowerings { 585 let i = LoweredIndex::from_u32(i); 586 let offset = self.offsets.lowering_callee(i); 587 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 588 let offset = self.offsets.lowering_data(i); 589 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 590 } 591 for i in 0..self.offsets.num_trampolines { 592 let i = TrampolineIndex::from_u32(i); 593 let offset = self.offsets.trampoline_func_ref(i); 594 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 595 } 596 for i in 0..self.offsets.num_runtime_memories { 597 let i = RuntimeMemoryIndex::from_u32(i); 598 let offset = self.offsets.runtime_memory(i); 599 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 600 } 601 for i in 0..self.offsets.num_runtime_reallocs { 602 let i = RuntimeReallocIndex::from_u32(i); 603 let offset = self.offsets.runtime_realloc(i); 604 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 605 } 606 for i in 0..self.offsets.num_runtime_callbacks { 607 let i = RuntimeCallbackIndex::from_u32(i); 608 let offset = self.offsets.runtime_callback(i); 609 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 610 } 611 for i in 0..self.offsets.num_runtime_post_returns { 612 let i = RuntimePostReturnIndex::from_u32(i); 613 let offset = self.offsets.runtime_post_return(i); 614 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 615 } 616 for i in 0..self.offsets.num_resources { 617 let i = ResourceIndex::from_u32(i); 618 let offset = self.offsets.resource_destructor(i); 619 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 620 } 621 for i in 0..self.offsets.num_runtime_tables { 622 let i = RuntimeTableIndex::from_u32(i); 623 let offset = self.offsets.runtime_table(i); 624 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 625 } 626 } 627 } 628 629 /// Returns a reference to the component type information for this 630 /// instance. 631 pub fn component(&self) -> &Component { 632 &self.component 633 } 634 635 /// Returns a reference to the resource type information. 636 pub fn resource_types(&self) -> &Arc<PrimaryMap<ResourceIndex, ResourceType>> { 637 &self.resource_types 638 } 639 640 /// Returns mutable a reference to the resource type information. 641 pub fn resource_types_mut(&mut self) -> &mut Arc<PrimaryMap<ResourceIndex, ResourceType>> { 642 &mut self.resource_types 643 } 644 645 /// Returns whether the resource that `ty` points to is owned by the 646 /// instance that `ty` correspond to. 647 /// 648 /// This is used when lowering borrows to skip table management and instead 649 /// thread through the underlying representation directly. 650 pub fn resource_owned_by_own_instance(&self, ty: TypeResourceTableIndex) -> bool { 651 let resource = &self.component.types()[ty]; 652 let component = self.component.env_component(); 653 let idx = match component.defined_resource_index(resource.ty) { 654 Some(idx) => idx, 655 None => return false, 656 }; 657 resource.instance == component.defined_resource_instances[idx] 658 } 659 660 /// Implementation of the `resource.new` intrinsic for `i32` 661 /// representations. 662 pub fn resource_new32(&mut self, ty: TypeResourceTableIndex, rep: u32) -> Result<u32> { 663 self.resource_tables() 664 .resource_new(TypedResource::Component { ty, rep }) 665 } 666 667 /// Implementation of the `resource.rep` intrinsic for `i32` 668 /// representations. 669 pub fn resource_rep32(&mut self, ty: TypeResourceTableIndex, index: u32) -> Result<u32> { 670 self.resource_tables() 671 .resource_rep(TypedResourceIndex::Component { ty, index }) 672 } 673 674 /// Implementation of the `resource.drop` intrinsic. 675 pub fn resource_drop(&mut self, ty: TypeResourceTableIndex, index: u32) -> Result<Option<u32>> { 676 self.resource_tables() 677 .resource_drop(TypedResourceIndex::Component { ty, index }) 678 } 679 680 /// NB: this is intended to be a private method. This does not have 681 /// `host_table` information at this time meaning it's only suitable for 682 /// working with resources specified to this component which is currently 683 /// all that this is used for. 684 /// 685 /// If necessary though it's possible to enhance the `Store` trait to thread 686 /// through the relevant information and get `host_table` to be `Some` here. 687 fn resource_tables(&mut self) -> ResourceTables<'_> { 688 ResourceTables { 689 host_table: None, 690 calls: unsafe { (&mut *self.store()).component_calls() }, 691 guest: Some((&mut self.instance_resource_tables, self.component.types())), 692 } 693 } 694 695 /// Returns the runtime state of resources associated with this component. 696 #[inline] 697 pub fn guest_tables( 698 &mut self, 699 ) -> ( 700 &mut PrimaryMap<RuntimeComponentInstanceIndex, ResourceTable>, 701 &ComponentTypes, 702 ) { 703 (&mut self.instance_resource_tables, self.component.types()) 704 } 705 706 /// Returns the destructor and instance flags for the specified resource 707 /// table type. 708 /// 709 /// This will lookup the origin definition of the `ty` table and return the 710 /// destructor/flags for that. 711 pub fn dtor_and_flags( 712 &self, 713 ty: TypeResourceTableIndex, 714 ) -> (Option<NonNull<VMFuncRef>>, Option<InstanceFlags>) { 715 let resource = self.component.types()[ty].ty; 716 let dtor = self.resource_destructor(resource); 717 let component = self.component.env_component(); 718 let flags = component.defined_resource_index(resource).map(|i| { 719 let instance = component.defined_resource_instances[i]; 720 self.instance_flags(instance) 721 }); 722 (dtor, flags) 723 } 724 725 pub(crate) fn resource_transfer_own( 726 &mut self, 727 index: u32, 728 src: TypeResourceTableIndex, 729 dst: TypeResourceTableIndex, 730 ) -> Result<u32> { 731 let mut tables = self.resource_tables(); 732 let rep = tables.resource_lift_own(TypedResourceIndex::Component { ty: src, index })?; 733 tables.resource_lower_own(TypedResource::Component { ty: dst, rep }) 734 } 735 736 pub(crate) fn resource_transfer_borrow( 737 &mut self, 738 index: u32, 739 src: TypeResourceTableIndex, 740 dst: TypeResourceTableIndex, 741 ) -> Result<u32> { 742 let dst_owns_resource = self.resource_owned_by_own_instance(dst); 743 let mut tables = self.resource_tables(); 744 let rep = tables.resource_lift_borrow(TypedResourceIndex::Component { ty: src, index })?; 745 // Implement `lower_borrow`'s special case here where if a borrow's 746 // resource type is owned by `dst` then the destination receives the 747 // representation directly rather than a handle to the representation. 748 // 749 // This can perhaps become a different libcall in the future to avoid 750 // this check at runtime since we know at compile time whether the 751 // destination type owns the resource, but that's left as a future 752 // refactoring if truly necessary. 753 if dst_owns_resource { 754 return Ok(rep); 755 } 756 tables.resource_lower_borrow(TypedResource::Component { ty: dst, rep }) 757 } 758 759 pub(crate) fn resource_enter_call(&mut self) { 760 self.resource_tables().enter_call() 761 } 762 763 pub(crate) fn resource_exit_call(&mut self) -> Result<()> { 764 self.resource_tables().exit_call() 765 } 766 767 /// Returns the store-local id that points to this component. 768 pub fn id(&self) -> ComponentInstanceId { 769 self.id 770 } 771 772 /// Pushes a new runtime instance that's been created into 773 /// `self.instances`. 774 pub fn push_instance_id(&mut self, id: InstanceId) -> RuntimeInstanceIndex { 775 self.instances.push(id) 776 } 777 778 /// Translates a `CoreDef`, a definition of a core wasm item, to an 779 /// [`Export`] which is the runtime core wasm definition. 780 pub fn lookup_def(&self, store: &StoreOpaque, def: &CoreDef) -> Export { 781 match def { 782 CoreDef::Export(e) => self.lookup_export(store, e), 783 CoreDef::Trampoline(idx) => Export::Function(ExportFunction { 784 func_ref: self.trampoline_func_ref(*idx), 785 }), 786 CoreDef::InstanceFlags(idx) => Export::Global(ExportGlobal { 787 definition: self.instance_flags(*idx).as_raw(), 788 global: Global { 789 wasm_ty: WasmValType::I32, 790 mutability: true, 791 }, 792 kind: ExportGlobalKind::ComponentFlags(self.vmctx(), *idx), 793 }), 794 } 795 } 796 797 /// Translates a `CoreExport<T>`, an export of some core instance within 798 /// this component, to the actual runtime definition of that item. 799 pub fn lookup_export<T>(&self, store: &StoreOpaque, item: &CoreExport<T>) -> Export 800 where 801 T: Copy + Into<EntityIndex>, 802 { 803 let id = self.instances[item.instance]; 804 let instance = store.instance(id); 805 let idx = match &item.item { 806 ExportItem::Index(idx) => (*idx).into(), 807 808 // FIXME: ideally at runtime we don't actually do any name lookups 809 // here. This will only happen when the host supplies an imported 810 // module so while the structure can't be known at compile time we 811 // do know at `InstancePre` time, for example, what all the host 812 // imports are. In theory we should be able to, as part of 813 // `InstancePre` construction, perform all name=>index mappings 814 // during that phase so the actual instantiation of an `InstancePre` 815 // skips all string lookups. This should probably only be 816 // investigated if this becomes a performance issue though. 817 ExportItem::Name(name) => instance.env_module().exports[name], 818 }; 819 instance.get_export_by_index(idx) 820 } 821 822 /// Looks up the value used for `import` at runtime. 823 /// 824 /// # Panics 825 /// 826 /// Panics of `import` is out of bounds for this component. 827 pub(crate) fn runtime_import(&self, import: RuntimeImportIndex) -> &RuntimeImport { 828 &self.imports[import] 829 } 830 831 /// Returns an `InstancePre<T>` which can be used to re-instantiated this 832 /// component if desired. 833 /// 834 /// # Safety 835 /// 836 /// This function places no bounds on `T` so it's up to the caller to match 837 /// that up appropriately with the store that this instance resides within. 838 pub unsafe fn instance_pre<T>(&self) -> InstancePre<T> { 839 // SAFETY: The `T` part of `new_unchecked` is forwarded as a contract of 840 // this function, and otherwise the validity of the components of the 841 // InstancePre should be guaranteed as it's what we were built with 842 // ourselves. 843 unsafe { 844 InstancePre::new_unchecked( 845 self.component.clone(), 846 self.imports.clone(), 847 self.resource_types.clone(), 848 ) 849 } 850 } 851 852 /// Sets the cached argument for the canonical ABI option `post-return` to 853 /// the `arg` specified. 854 /// 855 /// This function is used in conjunction with function calls to record, 856 /// after a fuction call completes, the optional ABI return value. This 857 /// return value is cached within this instance for future use when the 858 /// `post_return` Rust-API-level function is invoked. 859 /// 860 /// Note that `index` here is the index of the export that was just 861 /// invoked, and this is used to ensure that `post_return` is called on the 862 /// same function afterwards. This restriction technically isn't necessary 863 /// though and may be one we want to lift in the future. 864 /// 865 /// # Panics 866 /// 867 /// This function will panic if `post_return_arg` is already set to `Some`. 868 pub fn post_return_arg_set(&mut self, index: ExportIndex, arg: ValRaw) { 869 assert!(self.post_return_arg.is_none()); 870 self.post_return_arg = Some((index, arg)); 871 } 872 873 /// Re-acquires the value originally saved via `post_return_arg_set`. 874 /// 875 /// This function will take a function `index` that's having its 876 /// `post_return` function called. If an argument was previously stored and 877 /// `index` matches the index that was stored then `Some(arg)` is returned. 878 /// Otherwise `None` is returned. 879 pub fn post_return_arg_take(&mut self, index: ExportIndex) -> Option<ValRaw> { 880 let (expected_index, arg) = self.post_return_arg.take()?; 881 if index != expected_index { 882 self.post_return_arg = Some((expected_index, arg)); 883 None 884 } else { 885 Some(arg) 886 } 887 } 888 } 889 890 impl VMComponentContext { 891 /// Moves the `self` pointer backwards to the `ComponentInstance` pointer 892 /// that this `VMComponentContext` trails. 893 pub fn instance(&self) -> *mut ComponentInstance { 894 unsafe { 895 (self as *const Self as *mut u8) 896 .offset(-(offset_of!(ComponentInstance, vmctx) as isize)) 897 as *mut ComponentInstance 898 } 899 } 900 } 901 902 /// An owned version of `ComponentInstance` which is akin to 903 /// `Box<ComponentInstance>`. 904 /// 905 /// This type can be dereferenced to `ComponentInstance` to access the 906 /// underlying methods. 907 pub struct OwnedComponentInstance { 908 ptr: SendSyncPtr<ComponentInstance>, 909 } 910 911 impl OwnedComponentInstance { 912 /// Allocates a new `ComponentInstance + VMComponentContext` pair on the 913 /// heap with `malloc` and configures it for the `component` specified. 914 pub fn new( 915 id: ComponentInstanceId, 916 component: &Component, 917 resource_types: Arc<PrimaryMap<ResourceIndex, ResourceType>>, 918 imports: &Arc<PrimaryMap<RuntimeImportIndex, RuntimeImport>>, 919 store: NonNull<dyn VMStore>, 920 ) -> OwnedComponentInstance { 921 let offsets = VMComponentOffsets::new(HostPtr, component.env_component()); 922 let layout = ComponentInstance::alloc_layout(&offsets); 923 unsafe { 924 // Technically it is not required to `alloc_zeroed` here. The 925 // primary reason for doing this is because a component context 926 // start is a "partly initialized" state where pointers and such are 927 // configured as the instantiation process continues. The component 928 // model should guarantee that we never access uninitialized memory 929 // in the context, but to help protect against possible bugs a 930 // zeroed allocation is done here to try to contain 931 // use-before-initialized issues. 932 let ptr = alloc::alloc::alloc_zeroed(layout) as *mut ComponentInstance; 933 let ptr = NonNull::new(ptr).unwrap(); 934 935 ComponentInstance::new_at( 936 ptr, 937 layout.size(), 938 offsets, 939 id, 940 component, 941 resource_types, 942 imports, 943 store, 944 ); 945 946 let ptr = SendSyncPtr::new(ptr); 947 OwnedComponentInstance { ptr } 948 } 949 } 950 951 /// Returns the underlying component instance's raw pointer. 952 pub fn instance_ptr(&self) -> NonNull<ComponentInstance> { 953 self.ptr.as_non_null() 954 } 955 } 956 957 impl Deref for OwnedComponentInstance { 958 type Target = ComponentInstance; 959 fn deref(&self) -> &ComponentInstance { 960 unsafe { &*self.ptr.as_ptr() } 961 } 962 } 963 964 impl Drop for OwnedComponentInstance { 965 fn drop(&mut self) { 966 let layout = ComponentInstance::alloc_layout(&self.offsets); 967 unsafe { 968 ptr::drop_in_place(self.ptr.as_ptr()); 969 alloc::alloc::dealloc(self.ptr.as_ptr().cast(), layout); 970 } 971 } 972 } 973 974 impl VMComponentContext { 975 /// Helper function to cast between context types using a debug assertion to 976 /// protect against some mistakes. 977 #[inline] 978 pub unsafe fn from_opaque(opaque: NonNull<VMOpaqueContext>) -> NonNull<VMComponentContext> { 979 // See comments in `VMContext::from_opaque` for this debug assert 980 debug_assert_eq!(opaque.as_ref().magic, VMCOMPONENT_MAGIC); 981 opaque.cast() 982 } 983 } 984 985 impl VMOpaqueContext { 986 /// Helper function to clearly indicate the cast desired 987 #[inline] 988 pub fn from_vmcomponent(ptr: NonNull<VMComponentContext>) -> NonNull<VMOpaqueContext> { 989 ptr.cast() 990 } 991 } 992 993 #[allow(missing_docs)] 994 #[repr(transparent)] 995 #[derive(Copy, Clone)] 996 pub struct InstanceFlags(SendSyncPtr<VMGlobalDefinition>); 997 998 #[allow(missing_docs)] 999 impl InstanceFlags { 1000 /// Wraps the given pointer as an `InstanceFlags` 1001 /// 1002 /// # Unsafety 1003 /// 1004 /// This is a raw pointer argument which needs to be valid for the lifetime 1005 /// that `InstanceFlags` is used. 1006 pub unsafe fn from_raw(ptr: NonNull<VMGlobalDefinition>) -> InstanceFlags { 1007 InstanceFlags(SendSyncPtr::from(ptr)) 1008 } 1009 1010 #[inline] 1011 pub unsafe fn may_leave(&self) -> bool { 1012 *self.as_raw().as_ref().as_i32() & FLAG_MAY_LEAVE != 0 1013 } 1014 1015 #[inline] 1016 pub unsafe fn set_may_leave(&mut self, val: bool) { 1017 if val { 1018 *self.as_raw().as_mut().as_i32_mut() |= FLAG_MAY_LEAVE; 1019 } else { 1020 *self.as_raw().as_mut().as_i32_mut() &= !FLAG_MAY_LEAVE; 1021 } 1022 } 1023 1024 #[inline] 1025 pub unsafe fn may_enter(&self) -> bool { 1026 *self.as_raw().as_ref().as_i32() & FLAG_MAY_ENTER != 0 1027 } 1028 1029 #[inline] 1030 pub unsafe fn set_may_enter(&mut self, val: bool) { 1031 if val { 1032 *self.as_raw().as_mut().as_i32_mut() |= FLAG_MAY_ENTER; 1033 } else { 1034 *self.as_raw().as_mut().as_i32_mut() &= !FLAG_MAY_ENTER; 1035 } 1036 } 1037 1038 #[inline] 1039 pub unsafe fn needs_post_return(&self) -> bool { 1040 *self.as_raw().as_ref().as_i32() & FLAG_NEEDS_POST_RETURN != 0 1041 } 1042 1043 #[inline] 1044 pub unsafe fn set_needs_post_return(&mut self, val: bool) { 1045 if val { 1046 *self.as_raw().as_mut().as_i32_mut() |= FLAG_NEEDS_POST_RETURN; 1047 } else { 1048 *self.as_raw().as_mut().as_i32_mut() &= !FLAG_NEEDS_POST_RETURN; 1049 } 1050 } 1051 1052 #[inline] 1053 pub fn as_raw(&self) -> NonNull<VMGlobalDefinition> { 1054 self.0.as_non_null() 1055 } 1056 } 1057