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