wasmer_vm/
func_data_registry.rs

1//! A registry for `VMFuncRef`s. This allows us to deduplicate funcrefs so that
2//! identical `VMCallerCheckedAnyfunc`s will give us identical funcrefs.
3//!
4//! This registry also helps ensure that the `VMFuncRef`s can stay valid for as
5//! long as we need them to.
6
7use crate::vmcontext::VMCallerCheckedAnyfunc;
8use std::collections::HashMap;
9use std::sync::Mutex;
10
11/// The registry that holds the values that `VMFuncRef`s point to.
12#[derive(Debug)]
13pub struct FuncDataRegistry {
14    // This structure is stored in an `Engine` and is intended to be shared
15    // across many instances. Ideally instances can themselves be sent across
16    // threads, and ideally we can compile across many threads. As a result we
17    // use interior mutability here with a lock to avoid having callers to
18    // externally synchronize calls to compilation.
19    inner: Mutex<Inner>,
20}
21
22// We use raw pointers but the data never moves, so it's not a problem
23unsafe impl Send for FuncDataRegistry {}
24unsafe impl Sync for FuncDataRegistry {}
25
26/// A function reference. A single word that points to metadata about a function.
27#[repr(transparent)]
28#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29pub struct VMFuncRef(pub(crate) *const VMCallerCheckedAnyfunc);
30
31impl wasmer_types::NativeWasmType for VMFuncRef {
32    const WASM_TYPE: wasmer_types::Type = wasmer_types::Type::FuncRef;
33    type Abi = Self;
34
35    #[inline]
36    fn from_abi(abi: Self::Abi) -> Self {
37        abi
38    }
39
40    #[inline]
41    fn into_abi(self) -> Self::Abi {
42        self
43    }
44
45    #[inline]
46    fn to_binary(self) -> i128 {
47        self.0 as _
48    }
49
50    #[inline]
51    fn from_binary(bits: i128) -> Self {
52        // TODO: ensure that the safety invariants are actually upheld here
53        Self(bits as _)
54    }
55}
56
57impl VMFuncRef {
58    /// Check if the FuncRef is null
59    // TODO: make this const when `std::ptr::is_null` is const
60    pub fn is_null(&self) -> bool {
61        self.0.is_null()
62    }
63
64    /// Create a new null FuncRef
65    pub const fn null() -> Self {
66        Self(std::ptr::null())
67    }
68}
69
70impl std::ops::Deref for VMFuncRef {
71    type Target = *const VMCallerCheckedAnyfunc;
72
73    fn deref(&self) -> &Self::Target {
74        &self.0
75    }
76}
77
78impl std::ops::DerefMut for VMFuncRef {
79    fn deref_mut(&mut self) -> &mut Self::Target {
80        &mut self.0
81    }
82}
83
84// We use raw pointers but the data never moves, so it's not a problem
85// TODO: update docs
86unsafe impl Send for VMFuncRef {}
87unsafe impl Sync for VMFuncRef {}
88
89#[derive(Debug, Default)]
90struct Inner {
91    func_data: Vec<VMCallerCheckedAnyfunc>,
92    anyfunc_to_index: HashMap<VMCallerCheckedAnyfunc, usize>,
93}
94
95impl FuncDataRegistry {
96    /// Create a new `FuncDataRegistry`.
97    pub fn new() -> Self {
98        Self {
99            inner: Default::default(),
100        }
101    }
102
103    /// Register a signature and return its unique index.
104    pub fn register(&self, anyfunc: VMCallerCheckedAnyfunc) -> VMFuncRef {
105        let mut inner = self.inner.lock().unwrap();
106        let data = if let Some(&idx) = inner.anyfunc_to_index.get(&anyfunc) {
107            &inner.func_data[idx]
108        } else {
109            let idx = inner.func_data.len();
110            inner.func_data.push(anyfunc);
111            inner.anyfunc_to_index.insert(anyfunc, idx);
112            &inner.func_data[idx]
113        };
114        VMFuncRef(data)
115    }
116}