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