1 //! Lifetime management of `VMFuncRef`s inside of stores, and filling in their 2 //! trampolines. 3 4 use crate::Definition; 5 use crate::module::ModuleRegistry; 6 use crate::prelude::*; 7 use crate::runtime::HostFunc; 8 use crate::runtime::vm::{AlwaysMut, SendSyncPtr, VMArrayCallHostFuncContext, VMFuncRef}; 9 use alloc::sync::Arc; 10 use core::ptr::NonNull; 11 12 /// An arena of `VMFuncRef`s. 13 /// 14 /// Allows a store to pin and own funcrefs so that it can patch in trampolines 15 /// for `VMFuncRef`s that are missing a `wasm_call` trampoline and 16 /// need Wasm to supply it. 17 #[derive(Default)] 18 pub struct FuncRefs { 19 /// A bump allocation arena where we allocate `VMFuncRef`s such 20 /// that they are pinned and owned. 21 bump: AlwaysMut<bumpalo::Bump>, 22 23 /// Pointers into `self.bump` for entries that need `wasm_call` field filled 24 /// in. 25 with_holes: Vec<SendSyncPtr<VMFuncRef>>, 26 27 /// General-purpose storage of "function things" that need to live as long 28 /// as the entire store. 29 storage: Vec<Storage>, 30 } 31 32 /// Various items to place in `FuncRefs::storage` 33 /// 34 /// Note that each field has its own heap-level indirection to be resistant to 35 /// `FuncRefs::storage` having its own backing storage reallocated. 36 enum Storage { 37 /// Pinned arbitrary `Linker` definitions that must be kept alive for the 38 /// entire duration of the store. This can include host functions, funcrefs 39 /// inside them, etc. 40 InstancePreDefinitions { 41 #[expect(dead_code, reason = "only here to keep the original value alive")] 42 defs: Arc<[Definition]>, 43 }, 44 45 /// Pinned `VMFuncRef`s that had their `wasm_call` field 46 /// pre-patched when constructing an `InstancePre`, and which we need to 47 /// keep alive for our owning store's lifetime. 48 InstancePreFuncRefs { 49 #[expect(dead_code, reason = "only here to keep the original value alive")] 50 funcs: Arc<[VMFuncRef]>, 51 }, 52 53 /// A uniquely-owned host function within a `Store`. This comes about with 54 /// `Func::new` or similar APIs. The `HostFunc` internally owns the 55 /// `InstanceHandle` and that will get dropped when this `HostFunc` itself 56 /// is dropped. 57 /// 58 /// Note that this contains the vmctx that the `VMFuncRef` points to for 59 /// this host function. 60 BoxHost { 61 #[expect(dead_code, reason = "only here to keep the original value alive")] 62 func: Box<HostFunc>, 63 }, 64 65 /// A function is shared across possibly other stores, hence the `Arc`. This 66 /// variant happens when a `Linker`-defined function is instantiated within 67 /// a `Store` (e.g. via `Linker::get` or similar APIs). The `Arc` here 68 /// indicates that there's some number of other stores holding this function 69 /// too, so dropping this may not deallocate the underlying 70 /// `InstanceHandle`. 71 /// 72 /// Note that this contains the vmctx that the `VMFuncRef` points to for 73 /// this host function. 74 ArcHost { 75 #[expect(dead_code, reason = "only here to keep the original value alive")] 76 func: Arc<HostFunc>, 77 }, 78 } 79 80 impl FuncRefs { 81 /// Push the given `VMFuncRef` into this arena, returning a 82 /// pinned pointer to it. 83 /// 84 /// # Safety 85 /// 86 /// You may only access the return value on the same thread as this 87 /// `FuncRefs` and only while the store holding this `FuncRefs` exists. 88 /// Additionally the `vmctx` field of `func_ref` must be valid to read. 89 pub unsafe fn push( 90 &mut self, 91 func_ref: VMFuncRef, 92 modules: &ModuleRegistry, 93 ) -> NonNull<VMFuncRef> { 94 debug_assert!(func_ref.wasm_call.is_none()); 95 let func_ref = self.bump.get_mut().alloc(func_ref); 96 // SAFETY: it's a contract of this function itself that `func_ref` has a 97 // valid vmctx field to read. 98 let has_hole = unsafe { !try_fill(func_ref, modules) }; 99 let unpatched = SendSyncPtr::from(func_ref); 100 if has_hole { 101 self.with_holes.push(unpatched); 102 } 103 unpatched.as_non_null() 104 } 105 106 /// Patch any `VMFuncRef::wasm_call`s that need filling in. 107 pub fn fill(&mut self, modules: &ModuleRegistry) { 108 self.with_holes 109 .retain_mut(|f| unsafe { !try_fill(f.as_mut(), modules) }); 110 } 111 112 /// Reserves `amt` space for extra items in "storage" for this store. 113 pub fn reserve_storage(&mut self, amt: usize) { 114 self.storage.reserve(amt); 115 } 116 117 /// Push pre-patched `VMFuncRef`s from an `InstancePre`. 118 /// 119 /// This is used to ensure that the store itself persists the entire list of 120 /// `funcs` for the entire lifetime of the store. 121 pub fn push_instance_pre_func_refs(&mut self, funcs: Arc<[VMFuncRef]>) { 122 self.storage.push(Storage::InstancePreFuncRefs { funcs }); 123 } 124 125 /// Push linker definitions into storage, keeping them alive for the entire 126 /// lifetime of the store. 127 /// 128 /// This is used to keep linker-defined functions' vmctx values alive, for 129 /// example. 130 pub fn push_instance_pre_definitions(&mut self, defs: Arc<[Definition]>) { 131 self.storage.push(Storage::InstancePreDefinitions { defs }); 132 } 133 134 /// Pushes a shared host function into this store. 135 /// 136 /// This will create a store-local `VMFuncRef` with a hole to fill in where 137 /// the `wasm_call` will get filled in as needed. 138 /// 139 /// This function returns a `VMFuncRef` which is store-local and will have 140 /// `wasm_call` filled in eventually if needed. 141 /// 142 /// # Safety 143 /// 144 /// You may only access the return value on the same thread as this 145 /// `FuncRefs` and only while the store holding this `FuncRefs` exists. 146 pub fn push_arc_host( 147 &mut self, 148 func: Arc<HostFunc>, 149 modules: &ModuleRegistry, 150 ) -> NonNull<VMFuncRef> { 151 debug_assert!(func.func_ref().wasm_call.is_none()); 152 // SAFETY: the vmctx field in the funcref of `HostFunc` is safe to read. 153 let ret = unsafe { self.push(func.func_ref().clone(), modules) }; 154 self.storage.push(Storage::ArcHost { func }); 155 ret 156 } 157 158 /// Same as `push_arc_host`, but for owned host functions. 159 pub fn push_box_host( 160 &mut self, 161 func: Box<HostFunc>, 162 modules: &ModuleRegistry, 163 ) -> NonNull<VMFuncRef> { 164 debug_assert!(func.func_ref().wasm_call.is_none()); 165 // SAFETY: the vmctx field in the funcref of `HostFunc` is safe to read. 166 let ret = unsafe { self.push(func.func_ref().clone(), modules) }; 167 self.storage.push(Storage::BoxHost { func }); 168 ret 169 } 170 } 171 172 /// Attempts to fill the `wasm_call` field of `func_ref` given `modules` 173 /// registered and returns `true` if the field was filled, `false` otherwise. 174 /// 175 /// # Panics 176 /// 177 /// Panics if `func_ref.wasm_call.is_some()` 178 /// 179 /// # Safety 180 /// 181 /// This relies on `func_ref` being a valid pointer with a valid `vmctx` field. 182 unsafe fn try_fill(func_ref: &mut VMFuncRef, modules: &ModuleRegistry) -> bool { 183 debug_assert!(func_ref.wasm_call.is_none()); 184 185 // Debug assert that the vmctx is a `VMArrayCallHostFuncContext` as 186 // that is the only kind that can have holes. 187 // 188 // SAFETY: the validity of `vmctx` is a contract of this function itself. 189 unsafe { 190 let _ = VMArrayCallHostFuncContext::from_opaque(func_ref.vmctx.as_non_null()); 191 } 192 193 func_ref.wasm_call = modules 194 .wasm_to_array_trampoline(func_ref.type_index) 195 .map(|f| f.into()); 196 func_ref.wasm_call.is_some() 197 } 198