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