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