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