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, Instance, InstancePre, ResourceType, RuntimeImport}; 10 use crate::runtime::component::ComponentInstanceId; 11 use crate::runtime::vm::instance::{InstanceLayout, OwnedInstance, OwnedVMContext}; 12 use crate::runtime::vm::vmcontext::VMFunctionBody; 13 use crate::runtime::vm::{ 14 SendSyncPtr, VMArrayCallFunction, VMContext, VMFuncRef, VMGlobalDefinition, VMMemoryDefinition, 15 VMOpaqueContext, VMStore, VMStoreRawPtr, VMTableDefinition, VMTableImport, VMWasmCallFunction, 16 ValRaw, VmPtr, VmSafe, 17 }; 18 use crate::store::InstanceId; 19 use alloc::alloc::Layout; 20 use alloc::sync::Arc; 21 use core::mem; 22 use core::mem::offset_of; 23 use core::pin::Pin; 24 use core::ptr::NonNull; 25 use wasmtime_environ::component::*; 26 use wasmtime_environ::{DefinedTableIndex, HostPtr, PrimaryMap, VMSharedTypeIndex}; 27 28 #[allow(clippy::cast_possible_truncation)] // it's intended this is truncated on 29 // 32-bit platforms 30 const INVALID_PTR: usize = 0xdead_dead_beef_beef_u64 as usize; 31 32 mod libcalls; 33 mod resources; 34 35 pub use self::resources::{ 36 CallContexts, ResourceTable, ResourceTables, TypedResource, TypedResourceIndex, 37 }; 38 39 /// Runtime representation of a component instance and all state necessary for 40 /// the instance itself. 41 /// 42 /// This type never exists by-value, but rather it's always behind a pointer. 43 /// The size of the allocation for `ComponentInstance` includes the trailing 44 /// `VMComponentContext` which is variably sized based on the `offsets` 45 /// contained within. 46 /// 47 /// # Pin 48 /// 49 /// Note that this type is mutated through `Pin<&mut ComponentInstance>` in the 50 /// same manner as `vm::Instance` for core modules, and see more information 51 /// over there for documentation and rationale. 52 #[repr(C)] 53 pub struct ComponentInstance { 54 /// The index within the store of where to find this component instance. 55 id: ComponentInstanceId, 56 57 /// Size and offset information for the trailing `VMComponentContext`. 58 offsets: VMComponentOffsets<HostPtr>, 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 /// Required by `InstanceLayout`, also required to be the last field (with 113 /// repr(C)) 114 vmctx: OwnedVMContext<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 /// An opaque function pointer which is a `VMLoweringFunction` under the hood 170 /// but this is stored as `VMPtr<VMLoweringFunction>` within `VMLowering` below 171 /// to handle provenance correctly when using Pulley. 172 #[repr(transparent)] 173 pub struct VMLoweringFunction(VMFunctionBody); 174 175 /// Structure describing a lowered host function stored within a 176 /// `VMComponentContext` per-lowering. 177 #[derive(Copy, Clone)] 178 #[repr(C)] 179 pub struct VMLowering { 180 /// The host function pointer that is invoked when this lowering is 181 /// invoked. 182 pub callee: VmPtr<VMLoweringFunction>, 183 /// The host data pointer (think void* pointer) to get passed to `callee`. 184 pub data: VmPtr<u8>, 185 } 186 187 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 188 unsafe impl VmSafe for VMLowering {} 189 190 /// This is a marker type to represent the underlying allocation of a 191 /// `VMComponentContext`. 192 /// 193 /// This type is similar to `VMContext` for core wasm and is allocated once per 194 /// component instance in Wasmtime. While the static size of this type is 0 the 195 /// actual runtime size is variable depending on the shape of the component that 196 /// this corresponds to. This structure always trails a `ComponentInstance` 197 /// allocation and the allocation/lifetime of this allocation is managed by 198 /// `ComponentInstance`. 199 #[repr(C)] 200 // Set an appropriate alignment for this structure where the most-aligned value 201 // internally right now `VMGlobalDefinition` which has an alignment of 16 bytes. 202 #[repr(align(16))] 203 pub struct VMComponentContext; 204 205 impl ComponentInstance { 206 /// Converts the `vmctx` provided into a `ComponentInstance` and runs the 207 /// provided closure with that instance. 208 /// 209 /// # Unsafety 210 /// 211 /// This is `unsafe` because `vmctx` cannot be guaranteed to be a valid 212 /// pointer and it cannot be proven statically that it's safe to get a 213 /// mutable reference at this time to the instance from `vmctx`. 214 pub unsafe fn from_vmctx<R>( 215 vmctx: NonNull<VMComponentContext>, 216 f: impl FnOnce(&mut dyn VMStore, Instance) -> R, 217 ) -> R { 218 let mut ptr = vmctx 219 .byte_sub(mem::size_of::<ComponentInstance>()) 220 .cast::<ComponentInstance>(); 221 let reference = ptr.as_mut(); 222 let store = &mut *reference.store.0.as_ptr(); 223 let instance = Instance::from_wasmtime(store, reference.id); 224 f(store, instance) 225 } 226 227 /// Returns the layout corresponding to what would be an allocation of a 228 /// `ComponentInstance` for the `offsets` provided. 229 /// 230 /// The returned layout has space for both the `ComponentInstance` and the 231 /// trailing `VMComponentContext`. 232 fn alloc_layout(offsets: &VMComponentOffsets<HostPtr>) -> Layout { 233 let size = mem::size_of::<Self>() 234 .checked_add(usize::try_from(offsets.size_of_vmctx()).unwrap()) 235 .unwrap(); 236 let align = mem::align_of::<Self>(); 237 Layout::from_size_align(size, align).unwrap() 238 } 239 240 /// Allocates a new `ComponentInstance + VMComponentContext` pair on the 241 /// heap with `malloc` and configures it for the `component` specified. 242 pub(crate) fn new( 243 id: ComponentInstanceId, 244 component: &Component, 245 resource_types: Arc<PrimaryMap<ResourceIndex, ResourceType>>, 246 imports: &Arc<PrimaryMap<RuntimeImportIndex, RuntimeImport>>, 247 store: NonNull<dyn VMStore>, 248 ) -> OwnedComponentInstance { 249 let offsets = VMComponentOffsets::new(HostPtr, component.env_component()); 250 let num_instances = component.env_component().num_runtime_component_instances; 251 let mut instance_resource_tables = 252 PrimaryMap::with_capacity(num_instances.try_into().unwrap()); 253 for _ in 0..num_instances { 254 instance_resource_tables.push(ResourceTable::default()); 255 } 256 let mut ret = OwnedInstance::new(ComponentInstance { 257 id, 258 offsets, 259 instance_resource_tables, 260 instances: PrimaryMap::with_capacity( 261 component 262 .env_component() 263 .num_runtime_instances 264 .try_into() 265 .unwrap(), 266 ), 267 component: component.clone(), 268 resource_types, 269 imports: imports.clone(), 270 store: VMStoreRawPtr(store), 271 post_return_arg: None, 272 vmctx: OwnedVMContext::new(), 273 }); 274 unsafe { 275 ret.get_mut().initialize_vmctx(); 276 } 277 ret 278 } 279 280 #[inline] 281 pub fn vmctx(&self) -> NonNull<VMComponentContext> { 282 InstanceLayout::vmctx(self) 283 } 284 285 /// Returns a pointer to the "may leave" flag for this instance specified 286 /// for canonical lowering and lifting operations. 287 #[inline] 288 pub fn instance_flags(&self, instance: RuntimeComponentInstanceIndex) -> InstanceFlags { 289 unsafe { 290 let ptr = self 291 .vmctx_plus_offset_raw::<VMGlobalDefinition>(self.offsets.instance_flags(instance)); 292 InstanceFlags(SendSyncPtr::new(ptr)) 293 } 294 } 295 296 /// Returns the runtime memory definition corresponding to the index of the 297 /// memory provided. 298 /// 299 /// This can only be called after `idx` has been initialized at runtime 300 /// during the instantiation process of a component. 301 pub fn runtime_memory(&self, idx: RuntimeMemoryIndex) -> *mut VMMemoryDefinition { 302 unsafe { 303 let ret = *self.vmctx_plus_offset::<VmPtr<_>>(self.offsets.runtime_memory(idx)); 304 debug_assert!(ret.as_ptr() as usize != INVALID_PTR); 305 ret.as_ptr() 306 } 307 } 308 309 /// Returns the runtime table definition and associated instance `VMContext` 310 /// corresponding to the index of the table provided. 311 /// 312 /// This can only be called after `idx` has been initialized at runtime 313 /// during the instantiation process of a component. 314 pub fn runtime_table(&self, idx: RuntimeTableIndex) -> VMTableImport { 315 unsafe { 316 let ret = *self.vmctx_plus_offset::<VMTableImport>(self.offsets.runtime_table(idx)); 317 debug_assert!(ret.from.as_ptr() as usize != INVALID_PTR); 318 debug_assert!(ret.vmctx.as_ptr() as usize != INVALID_PTR); 319 ret 320 } 321 } 322 323 /// Returns the realloc pointer corresponding to the index provided. 324 /// 325 /// This can only be called after `idx` has been initialized at runtime 326 /// during the instantiation process of a component. 327 pub fn runtime_realloc(&self, idx: RuntimeReallocIndex) -> NonNull<VMFuncRef> { 328 unsafe { 329 let ret = *self.vmctx_plus_offset::<VmPtr<_>>(self.offsets.runtime_realloc(idx)); 330 debug_assert!(ret.as_ptr() as usize != INVALID_PTR); 331 ret.as_non_null() 332 } 333 } 334 335 /// Returns the post-return pointer corresponding to the index provided. 336 /// 337 /// This can only be called after `idx` has been initialized at runtime 338 /// during the instantiation process of a component. 339 pub fn runtime_post_return(&self, idx: RuntimePostReturnIndex) -> NonNull<VMFuncRef> { 340 unsafe { 341 let ret = *self.vmctx_plus_offset::<VmPtr<_>>(self.offsets.runtime_post_return(idx)); 342 debug_assert!(ret.as_ptr() as usize != INVALID_PTR); 343 ret.as_non_null() 344 } 345 } 346 347 /// Returns the host information for the lowered function at the index 348 /// specified. 349 /// 350 /// This can only be called after `idx` has been initialized at runtime 351 /// during the instantiation process of a component. 352 pub fn lowering(&self, idx: LoweredIndex) -> VMLowering { 353 unsafe { 354 let ret = *self.vmctx_plus_offset::<VMLowering>(self.offsets.lowering(idx)); 355 debug_assert!(ret.callee.as_ptr() as usize != INVALID_PTR); 356 debug_assert!(ret.data.as_ptr() as usize != INVALID_PTR); 357 ret 358 } 359 } 360 361 /// Returns the core wasm `funcref` corresponding to the trampoline 362 /// specified. 363 /// 364 /// The returned function is suitable to pass directly to a wasm module 365 /// instantiation and the function contains cranelift-compiled trampolines. 366 /// 367 /// This can only be called after `idx` has been initialized at runtime 368 /// during the instantiation process of a component. 369 pub fn trampoline_func_ref(&self, idx: TrampolineIndex) -> NonNull<VMFuncRef> { 370 unsafe { 371 let offset = self.offsets.trampoline_func_ref(idx); 372 let ret = self.vmctx_plus_offset_raw::<VMFuncRef>(offset); 373 debug_assert!( 374 mem::transmute::<Option<VmPtr<VMWasmCallFunction>>, usize>(ret.as_ref().wasm_call) 375 != INVALID_PTR 376 ); 377 debug_assert!(ret.as_ref().vmctx.as_ptr() as usize != INVALID_PTR); 378 ret 379 } 380 } 381 382 /// Stores the runtime memory pointer at the index specified. 383 /// 384 /// This is intended to be called during the instantiation process of a 385 /// component once a memory is available, which may not be until part-way 386 /// through component instantiation. 387 /// 388 /// Note that it should be a property of the component model that the `ptr` 389 /// here is never needed prior to it being configured here in the instance. 390 pub fn set_runtime_memory( 391 self: Pin<&mut Self>, 392 idx: RuntimeMemoryIndex, 393 ptr: NonNull<VMMemoryDefinition>, 394 ) { 395 unsafe { 396 let offset = self.offsets.runtime_memory(idx); 397 let storage = self.vmctx_plus_offset_mut::<VmPtr<VMMemoryDefinition>>(offset); 398 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 399 *storage = ptr.into(); 400 } 401 } 402 403 /// Same as `set_runtime_memory` but for realloc function pointers. 404 pub fn set_runtime_realloc( 405 self: Pin<&mut Self>, 406 idx: RuntimeReallocIndex, 407 ptr: NonNull<VMFuncRef>, 408 ) { 409 unsafe { 410 let offset = self.offsets.runtime_realloc(idx); 411 let storage = self.vmctx_plus_offset_mut::<VmPtr<VMFuncRef>>(offset); 412 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 413 *storage = ptr.into(); 414 } 415 } 416 417 /// Same as `set_runtime_memory` but for async callback function pointers. 418 pub fn set_runtime_callback( 419 self: Pin<&mut Self>, 420 idx: RuntimeCallbackIndex, 421 ptr: NonNull<VMFuncRef>, 422 ) { 423 unsafe { 424 let offset = self.offsets.runtime_callback(idx); 425 let storage = self.vmctx_plus_offset_mut::<VmPtr<VMFuncRef>>(offset); 426 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 427 *storage = ptr.into(); 428 } 429 } 430 431 /// Same as `set_runtime_memory` but for post-return function pointers. 432 pub fn set_runtime_post_return( 433 self: Pin<&mut Self>, 434 idx: RuntimePostReturnIndex, 435 ptr: NonNull<VMFuncRef>, 436 ) { 437 unsafe { 438 let offset = self.offsets.runtime_post_return(idx); 439 let storage = self.vmctx_plus_offset_mut::<VmPtr<VMFuncRef>>(offset); 440 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 441 *storage = ptr.into(); 442 } 443 } 444 445 /// Stores the runtime table pointer at the index specified. 446 /// 447 /// This is intended to be called during the instantiation process of a 448 /// component once a table is available, which may not be until part-way 449 /// through component instantiation. 450 /// 451 /// Note that it should be a property of the component model that the `ptr` 452 /// here is never needed prior to it being configured here in the instance. 453 pub fn set_runtime_table( 454 self: Pin<&mut Self>, 455 idx: RuntimeTableIndex, 456 ptr: NonNull<VMTableDefinition>, 457 vmctx: NonNull<VMContext>, 458 index: DefinedTableIndex, 459 ) { 460 unsafe { 461 let offset = self.offsets.runtime_table(idx); 462 let storage = self.vmctx_plus_offset_mut::<VMTableImport>(offset); 463 debug_assert!((*storage).vmctx.as_ptr() as usize == INVALID_PTR); 464 debug_assert!((*storage).from.as_ptr() as usize == INVALID_PTR); 465 *storage = VMTableImport { 466 vmctx: vmctx.into(), 467 from: ptr.into(), 468 index, 469 }; 470 } 471 } 472 473 /// Configures host runtime lowering information associated with imported f 474 /// functions for the `idx` specified. 475 pub fn set_lowering(self: Pin<&mut Self>, idx: LoweredIndex, lowering: VMLowering) { 476 unsafe { 477 let callee = self.offsets.lowering_callee(idx); 478 debug_assert!(*self.vmctx_plus_offset::<usize>(callee) == INVALID_PTR); 479 let data = self.offsets.lowering_data(idx); 480 debug_assert!(*self.vmctx_plus_offset::<usize>(data) == INVALID_PTR); 481 let offset = self.offsets.lowering(idx); 482 *self.vmctx_plus_offset_mut(offset) = lowering; 483 } 484 } 485 486 /// Same as `set_lowering` but for the resource.drop functions. 487 pub fn set_trampoline( 488 self: Pin<&mut Self>, 489 idx: TrampolineIndex, 490 wasm_call: NonNull<VMWasmCallFunction>, 491 array_call: NonNull<VMArrayCallFunction>, 492 type_index: VMSharedTypeIndex, 493 ) { 494 unsafe { 495 let offset = self.offsets.trampoline_func_ref(idx); 496 debug_assert!(*self.vmctx_plus_offset::<usize>(offset) == INVALID_PTR); 497 let vmctx = VMOpaqueContext::from_vmcomponent(self.vmctx()); 498 *self.vmctx_plus_offset_mut(offset) = VMFuncRef { 499 wasm_call: Some(wasm_call.into()), 500 array_call: array_call.into(), 501 type_index, 502 vmctx: vmctx.into(), 503 }; 504 } 505 } 506 507 /// Configures the destructor for a resource at the `idx` specified. 508 /// 509 /// This is required to be called for each resource as it's defined within a 510 /// component during the instantiation process. 511 pub fn set_resource_destructor( 512 self: Pin<&mut Self>, 513 idx: ResourceIndex, 514 dtor: Option<NonNull<VMFuncRef>>, 515 ) { 516 unsafe { 517 let offset = self.offsets.resource_destructor(idx); 518 debug_assert!(*self.vmctx_plus_offset::<usize>(offset) == INVALID_PTR); 519 *self.vmctx_plus_offset_mut(offset) = dtor.map(VmPtr::from); 520 } 521 } 522 523 /// Returns the destructor, if any, for `idx`. 524 /// 525 /// This is only valid to call after `set_resource_destructor`, or typically 526 /// after instantiation. 527 pub fn resource_destructor(&self, idx: ResourceIndex) -> Option<NonNull<VMFuncRef>> { 528 unsafe { 529 let offset = self.offsets.resource_destructor(idx); 530 debug_assert!(*self.vmctx_plus_offset::<usize>(offset) != INVALID_PTR); 531 (*self.vmctx_plus_offset::<Option<VmPtr<VMFuncRef>>>(offset)).map(|p| p.as_non_null()) 532 } 533 } 534 535 unsafe fn initialize_vmctx(mut self: Pin<&mut Self>) { 536 let offset = self.offsets.magic(); 537 *self.as_mut().vmctx_plus_offset_mut(offset) = VMCOMPONENT_MAGIC; 538 // Initialize the built-in functions 539 static BUILTINS: libcalls::VMComponentBuiltins = libcalls::VMComponentBuiltins::INIT; 540 let ptr = BUILTINS.expose_provenance(); 541 let offset = self.offsets.builtins(); 542 *self.as_mut().vmctx_plus_offset_mut(offset) = VmPtr::from(ptr); 543 let offset = self.offsets.vm_store_context(); 544 *self.as_mut().vmctx_plus_offset_mut(offset) = 545 VmPtr::from(self.store.0.as_ref().vm_store_context_ptr()); 546 547 for i in 0..self.offsets.num_runtime_component_instances { 548 let i = RuntimeComponentInstanceIndex::from_u32(i); 549 let mut def = VMGlobalDefinition::new(); 550 *def.as_i32_mut() = FLAG_MAY_ENTER | FLAG_MAY_LEAVE; 551 self.instance_flags(i).as_raw().write(def); 552 } 553 554 // In debug mode set non-null bad values to all "pointer looking" bits 555 // and pieces related to lowering and such. This'll help detect any 556 // erroneous usage and enable debug assertions above as well to prevent 557 // loading these before they're configured or setting them twice. 558 if cfg!(debug_assertions) { 559 for i in 0..self.offsets.num_lowerings { 560 let i = LoweredIndex::from_u32(i); 561 let offset = self.offsets.lowering_callee(i); 562 *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; 563 let offset = self.offsets.lowering_data(i); 564 *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; 565 } 566 for i in 0..self.offsets.num_trampolines { 567 let i = TrampolineIndex::from_u32(i); 568 let offset = self.offsets.trampoline_func_ref(i); 569 *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; 570 } 571 for i in 0..self.offsets.num_runtime_memories { 572 let i = RuntimeMemoryIndex::from_u32(i); 573 let offset = self.offsets.runtime_memory(i); 574 *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; 575 } 576 for i in 0..self.offsets.num_runtime_reallocs { 577 let i = RuntimeReallocIndex::from_u32(i); 578 let offset = self.offsets.runtime_realloc(i); 579 *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; 580 } 581 for i in 0..self.offsets.num_runtime_callbacks { 582 let i = RuntimeCallbackIndex::from_u32(i); 583 let offset = self.offsets.runtime_callback(i); 584 *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; 585 } 586 for i in 0..self.offsets.num_runtime_post_returns { 587 let i = RuntimePostReturnIndex::from_u32(i); 588 let offset = self.offsets.runtime_post_return(i); 589 *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; 590 } 591 for i in 0..self.offsets.num_resources { 592 let i = ResourceIndex::from_u32(i); 593 let offset = self.offsets.resource_destructor(i); 594 *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; 595 } 596 for i in 0..self.offsets.num_runtime_tables { 597 let i = RuntimeTableIndex::from_u32(i); 598 let offset = self.offsets.runtime_table(i); 599 *self.as_mut().vmctx_plus_offset_mut(offset) = INVALID_PTR; 600 } 601 } 602 } 603 604 /// Returns a reference to the component type information for this 605 /// instance. 606 pub fn component(&self) -> &Component { 607 &self.component 608 } 609 610 /// Returns a reference to the resource type information. 611 pub fn resource_types(&self) -> &Arc<PrimaryMap<ResourceIndex, ResourceType>> { 612 &self.resource_types 613 } 614 615 /// Returns a mutable reference to the resource type information. 616 pub fn resource_types_mut( 617 self: Pin<&mut Self>, 618 ) -> &mut Arc<PrimaryMap<ResourceIndex, ResourceType>> { 619 // SAFETY: we've chosen the `Pin` guarantee of `Self` to not apply to 620 // the map returned. 621 unsafe { &mut self.get_unchecked_mut().resource_types } 622 } 623 624 /// Returns whether the resource that `ty` points to is owned by the 625 /// instance that `ty` correspond to. 626 /// 627 /// This is used when lowering borrows to skip table management and instead 628 /// thread through the underlying representation directly. 629 pub fn resource_owned_by_own_instance(&self, ty: TypeResourceTableIndex) -> bool { 630 let resource = &self.component.types()[ty]; 631 let component = self.component.env_component(); 632 let idx = match component.defined_resource_index(resource.ty) { 633 Some(idx) => idx, 634 None => return false, 635 }; 636 resource.instance == component.defined_resource_instances[idx] 637 } 638 639 /// Returns the runtime state of resources associated with this component. 640 #[inline] 641 pub fn guest_tables( 642 self: Pin<&mut Self>, 643 ) -> ( 644 &mut PrimaryMap<RuntimeComponentInstanceIndex, ResourceTable>, 645 &ComponentTypes, 646 ) { 647 // safety: we've chosen the `pin` guarantee of `self` to not apply to 648 // the map returned. 649 unsafe { 650 let me = self.get_unchecked_mut(); 651 (&mut me.instance_resource_tables, me.component.types()) 652 } 653 } 654 655 /// Returns the destructor and instance flags for the specified resource 656 /// table type. 657 /// 658 /// This will lookup the origin definition of the `ty` table and return the 659 /// destructor/flags for that. 660 pub fn dtor_and_flags( 661 &self, 662 ty: TypeResourceTableIndex, 663 ) -> (Option<NonNull<VMFuncRef>>, Option<InstanceFlags>) { 664 let resource = self.component.types()[ty].ty; 665 let dtor = self.resource_destructor(resource); 666 let component = self.component.env_component(); 667 let flags = component.defined_resource_index(resource).map(|i| { 668 let instance = component.defined_resource_instances[i]; 669 self.instance_flags(instance) 670 }); 671 (dtor, flags) 672 } 673 674 /// Returns the store-local id that points to this component. 675 pub fn id(&self) -> ComponentInstanceId { 676 self.id 677 } 678 679 /// Pushes a new runtime instance that's been created into 680 /// `self.instances`. 681 pub fn push_instance_id(self: Pin<&mut Self>, id: InstanceId) -> RuntimeInstanceIndex { 682 self.instances_mut().push(id) 683 } 684 685 /// Returns the [`InstanceId`] previously pushed by `push_instance_id` 686 /// above. 687 /// 688 /// # Panics 689 /// 690 /// Panics if `idx` hasn't been initialized yet. 691 pub fn instance(&self, idx: RuntimeInstanceIndex) -> InstanceId { 692 self.instances[idx] 693 } 694 695 fn instances_mut(self: Pin<&mut Self>) -> &mut PrimaryMap<RuntimeInstanceIndex, InstanceId> { 696 // SAFETY: we've chosen the `Pin` guarantee of `Self` to not apply to 697 // the map returned. 698 unsafe { &mut self.get_unchecked_mut().instances } 699 } 700 701 /// Looks up the value used for `import` at runtime. 702 /// 703 /// # Panics 704 /// 705 /// Panics of `import` is out of bounds for this component. 706 pub(crate) fn runtime_import(&self, import: RuntimeImportIndex) -> &RuntimeImport { 707 &self.imports[import] 708 } 709 710 /// Returns an `InstancePre<T>` which can be used to re-instantiated this 711 /// component if desired. 712 /// 713 /// # Safety 714 /// 715 /// This function places no bounds on `T` so it's up to the caller to match 716 /// that up appropriately with the store that this instance resides within. 717 pub unsafe fn instance_pre<T>(&self) -> InstancePre<T> { 718 // SAFETY: The `T` part of `new_unchecked` is forwarded as a contract of 719 // this function, and otherwise the validity of the components of the 720 // InstancePre should be guaranteed as it's what we were built with 721 // ourselves. 722 unsafe { 723 InstancePre::new_unchecked( 724 self.component.clone(), 725 self.imports.clone(), 726 self.resource_types.clone(), 727 ) 728 } 729 } 730 731 /// Sets the cached argument for the canonical ABI option `post-return` to 732 /// the `arg` specified. 733 /// 734 /// This function is used in conjunction with function calls to record, 735 /// after a fuction call completes, the optional ABI return value. This 736 /// return value is cached within this instance for future use when the 737 /// `post_return` Rust-API-level function is invoked. 738 /// 739 /// Note that `index` here is the index of the export that was just 740 /// invoked, and this is used to ensure that `post_return` is called on the 741 /// same function afterwards. This restriction technically isn't necessary 742 /// though and may be one we want to lift in the future. 743 /// 744 /// # Panics 745 /// 746 /// This function will panic if `post_return_arg` is already set to `Some`. 747 pub fn post_return_arg_set(self: Pin<&mut Self>, index: ExportIndex, arg: ValRaw) { 748 assert!(self.post_return_arg.is_none()); 749 *self.post_return_arg_mut() = Some((index, arg)); 750 } 751 752 /// Re-acquires the value originally saved via `post_return_arg_set`. 753 /// 754 /// This function will take a function `index` that's having its 755 /// `post_return` function called. If an argument was previously stored and 756 /// `index` matches the index that was stored then `Some(arg)` is returned. 757 /// Otherwise `None` is returned. 758 pub fn post_return_arg_take(self: Pin<&mut Self>, index: ExportIndex) -> Option<ValRaw> { 759 let post_return_arg = self.post_return_arg_mut(); 760 let (expected_index, arg) = post_return_arg.take()?; 761 if index != expected_index { 762 *post_return_arg = Some((expected_index, arg)); 763 None 764 } else { 765 Some(arg) 766 } 767 } 768 769 fn post_return_arg_mut(self: Pin<&mut Self>) -> &mut Option<(ExportIndex, ValRaw)> { 770 // SAFETY: we've chosen the `Pin` guarantee of `Self` to not apply to 771 // the map returned. 772 unsafe { &mut self.get_unchecked_mut().post_return_arg } 773 } 774 } 775 776 // SAFETY: `layout` should describe this accurately and `OwnedVMContext` is the 777 // last field of `ComponentInstance`. 778 unsafe impl InstanceLayout for ComponentInstance { 779 /// Technically it is not required to `alloc_zeroed` here. The primary 780 /// reason for doing this is because a component context start is a "partly 781 /// initialized" state where pointers and such are configured as the 782 /// instantiation process continues. The component model should guarantee 783 /// that we never access uninitialized memory in the context, but to help 784 /// protect against possible bugs a zeroed allocation is done here to try to 785 /// contain use-before-initialized issues. 786 const INIT_ZEROED: bool = true; 787 788 type VMContext = VMComponentContext; 789 790 fn layout(&self) -> Layout { 791 ComponentInstance::alloc_layout(&self.offsets) 792 } 793 794 fn owned_vmctx(&self) -> &OwnedVMContext<VMComponentContext> { 795 &self.vmctx 796 } 797 798 fn owned_vmctx_mut(&mut self) -> &mut OwnedVMContext<VMComponentContext> { 799 &mut self.vmctx 800 } 801 } 802 803 pub type OwnedComponentInstance = OwnedInstance<ComponentInstance>; 804 805 impl VMComponentContext { 806 /// Moves the `self` pointer backwards to the `ComponentInstance` pointer 807 /// that this `VMComponentContext` trails. 808 pub fn instance(&self) -> *mut ComponentInstance { 809 unsafe { 810 (self as *const Self as *mut u8) 811 .offset(-(offset_of!(ComponentInstance, vmctx) as isize)) 812 as *mut ComponentInstance 813 } 814 } 815 816 /// Helper function to cast between context types using a debug assertion to 817 /// protect against some mistakes. 818 #[inline] 819 pub unsafe fn from_opaque(opaque: NonNull<VMOpaqueContext>) -> NonNull<VMComponentContext> { 820 // See comments in `VMContext::from_opaque` for this debug assert 821 debug_assert_eq!(opaque.as_ref().magic, VMCOMPONENT_MAGIC); 822 opaque.cast() 823 } 824 } 825 826 impl VMOpaqueContext { 827 /// Helper function to clearly indicate the cast desired 828 #[inline] 829 pub fn from_vmcomponent(ptr: NonNull<VMComponentContext>) -> NonNull<VMOpaqueContext> { 830 ptr.cast() 831 } 832 } 833 834 #[allow(missing_docs)] 835 #[repr(transparent)] 836 #[derive(Copy, Clone)] 837 pub struct InstanceFlags(SendSyncPtr<VMGlobalDefinition>); 838 839 #[allow(missing_docs)] 840 impl InstanceFlags { 841 /// Wraps the given pointer as an `InstanceFlags` 842 /// 843 /// # Unsafety 844 /// 845 /// This is a raw pointer argument which needs to be valid for the lifetime 846 /// that `InstanceFlags` is used. 847 pub unsafe fn from_raw(ptr: NonNull<VMGlobalDefinition>) -> InstanceFlags { 848 InstanceFlags(SendSyncPtr::from(ptr)) 849 } 850 851 #[inline] 852 pub unsafe fn may_leave(&self) -> bool { 853 *self.as_raw().as_ref().as_i32() & FLAG_MAY_LEAVE != 0 854 } 855 856 #[inline] 857 pub unsafe fn set_may_leave(&mut self, val: bool) { 858 if val { 859 *self.as_raw().as_mut().as_i32_mut() |= FLAG_MAY_LEAVE; 860 } else { 861 *self.as_raw().as_mut().as_i32_mut() &= !FLAG_MAY_LEAVE; 862 } 863 } 864 865 #[inline] 866 pub unsafe fn may_enter(&self) -> bool { 867 *self.as_raw().as_ref().as_i32() & FLAG_MAY_ENTER != 0 868 } 869 870 #[inline] 871 pub unsafe fn set_may_enter(&mut self, val: bool) { 872 if val { 873 *self.as_raw().as_mut().as_i32_mut() |= FLAG_MAY_ENTER; 874 } else { 875 *self.as_raw().as_mut().as_i32_mut() &= !FLAG_MAY_ENTER; 876 } 877 } 878 879 #[inline] 880 pub unsafe fn needs_post_return(&self) -> bool { 881 *self.as_raw().as_ref().as_i32() & FLAG_NEEDS_POST_RETURN != 0 882 } 883 884 #[inline] 885 pub unsafe fn set_needs_post_return(&mut self, val: bool) { 886 if val { 887 *self.as_raw().as_mut().as_i32_mut() |= FLAG_NEEDS_POST_RETURN; 888 } else { 889 *self.as_raw().as_mut().as_i32_mut() &= !FLAG_NEEDS_POST_RETURN; 890 } 891 } 892 893 #[inline] 894 pub fn as_raw(&self) -> NonNull<VMGlobalDefinition> { 895 self.0.as_non_null() 896 } 897 } 898