ayaka_plugin_wasmtime/
lib.rs

1//! Wasmtime-based plugin backend.
2
3#![warn(missing_docs)]
4
5use ayaka_plugin::*;
6use std::{
7    collections::HashMap,
8    sync::{Arc, Mutex},
9};
10use wasmtime::*;
11
12unsafe fn mem_slice<'a, T: 'a>(
13    store: impl Into<StoreContext<'a, T>>,
14    memory: &Memory,
15    start: i32,
16    len: i32,
17) -> &'a [u8] {
18    memory
19        .data(store)
20        .get_unchecked(start as usize..)
21        .get_unchecked(..len as usize)
22}
23
24unsafe fn mem_slice_mut<'a, T: 'a>(
25    store: impl Into<StoreContextMut<'a, T>>,
26    memory: &Memory,
27    start: i32,
28    len: i32,
29) -> &'a mut [u8] {
30    memory
31        .data_mut(store)
32        .get_unchecked_mut(start as usize..)
33        .get_unchecked_mut(..len as usize)
34}
35
36type HostStore = Arc<Mutex<Store<()>>>;
37
38/// A Wasmtime [`Instance`].
39pub struct WasmtimeModule {
40    store: HostStore,
41    instance: Instance,
42    memory: Memory,
43    abi_free: TypedFunc<(i32, i32), ()>,
44    abi_alloc: TypedFunc<i32, i32>,
45}
46
47impl WasmtimeModule {
48    fn new(store: HostStore, module: &Module, linker: &wasmtime::Linker<()>) -> Result<Self> {
49        let mut inner_store = store.lock().unwrap();
50        let instance = linker.instantiate(inner_store.as_context_mut(), module)?;
51        let memory = instance
52            .get_memory(inner_store.as_context_mut(), MEMORY_NAME)
53            .ok_or_else(|| anyhow!("cannot get memory"))?;
54        let abi_free = instance.get_typed_func(inner_store.as_context_mut(), ABI_FREE_NAME)?;
55        let abi_alloc = instance.get_typed_func(inner_store.as_context_mut(), ABI_ALLOC_NAME)?;
56        drop(inner_store);
57        Ok(Self {
58            store,
59            instance,
60            memory,
61            abi_free,
62            abi_alloc,
63        })
64    }
65
66    fn call_impl<T>(
67        &self,
68        mut store: StoreContextMut<()>,
69        name: &str,
70        data: &[u8],
71        f: impl FnOnce(&[u8]) -> Result<T>,
72    ) -> Result<T> {
73        let func = self
74            .instance
75            .get_typed_func::<(i32, i32), u64>(&mut store, name)?;
76
77        let ptr = self.abi_alloc.call(&mut store, data.len() as i32)?;
78        unsafe {
79            mem_slice_mut(&mut store, &self.memory, ptr, data.len() as i32).copy_from_slice(data)
80        };
81
82        let res = func.call(&mut store, (data.len() as i32, ptr));
83
84        self.abi_free.call(&mut store, (ptr, data.len() as i32))?;
85
86        let res = res?;
87        let (len, res) = ((res >> 32) as i32, (res & 0xFFFFFFFF) as i32);
88
89        let res_data = unsafe { mem_slice(&store, &self.memory, res, len) };
90
91        let res_data = f(res_data);
92
93        self.abi_free.call(&mut store, (res, len))?;
94
95        let res_data = res_data?;
96        Ok(res_data)
97    }
98}
99
100impl RawModule for WasmtimeModule {
101    type Linker = WasmtimeLinker;
102
103    type LinkerHandle<'a> = WasmtimeLinkerHandle<'a>;
104
105    type Func = Func;
106
107    fn call<T>(&self, name: &str, data: &[u8], f: impl FnOnce(&[u8]) -> Result<T>) -> Result<T> {
108        self.call_impl(self.store.lock().unwrap().as_context_mut(), name, data, f)
109    }
110}
111
112/// A Wasmtime [`Store`] with [`Linker`].
113pub struct WasmtimeLinker {
114    engine: Engine,
115    store: HostStore,
116    linker: wasmtime::Linker<()>,
117}
118
119impl ayaka_plugin::Linker<WasmtimeModule> for WasmtimeLinker {
120    type Config = ();
121
122    fn new(_: ()) -> Result<Self> {
123        let engine = Engine::default();
124        let store = Store::new(&engine, ());
125        let linker = wasmtime::Linker::new(&engine);
126        Ok(Self {
127            engine,
128            store: Arc::new(Mutex::new(store)),
129            linker,
130        })
131    }
132
133    fn create(&self, binary: &[u8]) -> Result<WasmtimeModule> {
134        let module = Module::new(&self.engine, binary)?;
135        let host = WasmtimeModule::new(self.store.clone(), &module, &self.linker)?;
136        Ok(host)
137    }
138
139    fn import(&mut self, ns: impl Into<String>, funcs: HashMap<String, Func>) -> Result<()> {
140        let ns = ns.into();
141        let store = self.store.lock().unwrap();
142        for (name, func) in funcs {
143            self.linker.define(store.as_context(), &ns, &name, func)?;
144        }
145        Ok(())
146    }
147
148    fn wrap_raw(
149        &self,
150        f: impl (Fn(WasmtimeLinkerHandle<'_>, i32, i32) -> Result<Vec<u8>>) + Send + Sync + 'static,
151    ) -> Func {
152        Func::wrap(
153            self.store.lock().unwrap().as_context_mut(),
154            move |mut store: Caller<()>, len: i32, data: i32| unsafe {
155                let memory = store
156                    .get_export(MEMORY_NAME)
157                    .ok_or_else(|| anyhow!("cannot get memory"))?
158                    .into_memory()
159                    .ok_or_else(|| anyhow!("memory is not Memory"))?;
160                let data = {
161                    let store = store.as_context_mut();
162                    let handle = WasmtimeLinkerHandle { store, memory };
163                    f(handle, data, len)?
164                };
165                let abi_alloc = store
166                    .get_export(ABI_ALLOC_NAME)
167                    .ok_or_else(|| anyhow!("cannot get abi_alloc"))?
168                    .into_func()
169                    .ok_or_else(|| anyhow!("abi_alloc is not Func"))?
170                    .typed::<i32, i32>(store.as_context())?;
171                let ptr = abi_alloc.call(store.as_context_mut(), data.len() as i32)?;
172                mem_slice_mut(store.as_context_mut(), &memory, ptr, data.len() as i32)
173                    .copy_from_slice(&data);
174                Ok(((data.len() as u64) << 32) | (ptr as u64))
175            },
176        )
177    }
178}
179
180/// A Wasmtime [`StoreContextMut`].
181pub struct WasmtimeLinkerHandle<'a> {
182    store: StoreContextMut<'a, ()>,
183    memory: Memory,
184}
185
186impl<'a> LinkerHandle<'a, WasmtimeModule> for WasmtimeLinkerHandle<'a> {
187    fn call<T>(
188        &mut self,
189        m: &WasmtimeModule,
190        name: &str,
191        data: &[u8],
192        f: impl FnOnce(&[u8]) -> Result<T>,
193    ) -> Result<T> {
194        m.call_impl(self.store.as_context_mut(), name, data, f)
195    }
196
197    fn slice<T>(&self, start: i32, len: i32, f: impl FnOnce(&[u8]) -> T) -> T {
198        f(unsafe { mem_slice(self.store.as_context(), &self.memory, start, len) })
199    }
200
201    fn slice_mut<T>(&mut self, start: i32, len: i32, f: impl FnOnce(&mut [u8]) -> T) -> T {
202        f(unsafe { mem_slice_mut(self.store.as_context_mut(), &self.memory, start, len) })
203    }
204}