Skip to main content

wasmtime/runtime/store/
func_refs.rs

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