linera_witty/runtime/wasmer/
mod.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Support for the [Wasmer](https://wasmer.io) runtime.
5
6mod export_function;
7mod function;
8mod memory;
9mod parameters;
10mod results;
11
12use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
13
14pub use wasmer::FunctionEnvMut;
15use wasmer::{
16    AsStoreMut, AsStoreRef, Engine, Extern, FunctionEnv, Imports, InstantiationError, Memory,
17    Module, Store, StoreMut, StoreObjects, StoreRef,
18};
19
20pub use self::{parameters::WasmerParameters, results::WasmerResults};
21use super::traits::{Instance, Runtime};
22
23/// Representation of the [Wasmer](https://wasmer.io) runtime.
24pub struct Wasmer;
25
26impl Runtime for Wasmer {
27    type Export = Extern;
28    type Memory = Memory;
29}
30
31/// Helper to create Wasmer [`Instance`] implementations.
32pub struct InstanceBuilder<UserData> {
33    store: Store,
34    imports: Imports,
35    environment: Environment<UserData>,
36}
37
38impl<UserData: 'static> InstanceBuilder<UserData> {
39    /// Creates a new [`InstanceBuilder`].
40    pub fn new(engine: Engine, user_data: UserData) -> Self {
41        InstanceBuilder {
42            store: Store::new(engine),
43            imports: Imports::default(),
44            environment: Environment::new(user_data),
45        }
46    }
47
48    /// Returns a reference to the [`Store`] used in this [`InstanceBuilder`].
49    pub fn store(&self) -> &Store {
50        &self.store
51    }
52
53    /// Creates a [`FunctionEnv`] representing the instance of this [`InstanceBuilder`].
54    ///
55    /// This can be used when exporting host functions that may perform reentrant calls.
56    pub fn environment(&mut self) -> FunctionEnv<Environment<UserData>> {
57        FunctionEnv::new(&mut self.store, self.environment.clone())
58    }
59
60    /// Defines a new import for the Wasm guest instance.
61    pub fn define(&mut self, namespace: &str, name: &str, value: impl Into<Extern>) {
62        self.imports.define(namespace, name, value);
63    }
64
65    /// Creates an [`EntrypointInstance`] from this [`InstanceBuilder`].
66    #[allow(clippy::result_large_err)]
67    pub fn instantiate(
68        mut self,
69        module: &Module,
70    ) -> Result<EntrypointInstance<UserData>, InstantiationError> {
71        let instance = wasmer::Instance::new(&mut self.store, module, &self.imports)?;
72        self.environment
73            .exports
74            .set(instance.exports.clone())
75            .expect("Environment already initialized");
76        Ok(EntrypointInstance {
77            store: self.store,
78            instance,
79            instance_slot: self.environment,
80        })
81    }
82}
83
84impl<UserData> AsStoreRef for InstanceBuilder<UserData> {
85    fn as_store_ref(&self) -> StoreRef<'_> {
86        self.store.as_store_ref()
87    }
88}
89
90impl<UserData> AsStoreMut for InstanceBuilder<UserData> {
91    fn as_store_mut(&mut self) -> StoreMut<'_> {
92        self.store.as_store_mut()
93    }
94
95    fn objects_mut(&mut self) -> &mut StoreObjects {
96        self.store.objects_mut()
97    }
98}
99
100/// Necessary data for implementing an entrypoint [`Instance`].
101pub struct EntrypointInstance<UserData> {
102    store: Store,
103    instance: wasmer::Instance,
104    instance_slot: Environment<UserData>,
105}
106
107impl<UserData> AsStoreRef for EntrypointInstance<UserData> {
108    fn as_store_ref(&self) -> StoreRef<'_> {
109        self.store.as_store_ref()
110    }
111}
112
113impl<UserData> AsStoreMut for EntrypointInstance<UserData> {
114    fn as_store_mut(&mut self) -> StoreMut<'_> {
115        self.store.as_store_mut()
116    }
117
118    fn objects_mut(&mut self) -> &mut StoreObjects {
119        self.store.objects_mut()
120    }
121}
122
123impl<UserData> EntrypointInstance<UserData> {
124    /// Returns mutable references to the [`Store`] and the [`wasmer::Instance`] stored inside this
125    /// [`EntrypointInstance`].
126    pub fn as_store_and_instance_mut(&mut self) -> (StoreMut, &mut wasmer::Instance) {
127        (self.store.as_store_mut(), &mut self.instance)
128    }
129}
130
131impl<UserData> Instance for EntrypointInstance<UserData> {
132    type Runtime = Wasmer;
133    type UserData = UserData;
134    type UserDataReference<'a>
135        = MutexGuard<'a, UserData>
136    where
137        Self::UserData: 'a,
138        Self: 'a;
139    type UserDataMutReference<'a>
140        = MutexGuard<'a, UserData>
141    where
142        Self::UserData: 'a,
143        Self: 'a;
144
145    fn load_export(&mut self, name: &str) -> Option<Extern> {
146        self.instance_slot.load_export(name)
147    }
148
149    fn user_data(&self) -> Self::UserDataReference<'_> {
150        self.instance_slot.user_data()
151    }
152
153    fn user_data_mut(&mut self) -> Self::UserDataMutReference<'_> {
154        self.instance_slot.user_data()
155    }
156}
157
158/// Alias for the [`Instance`] implementation made available inside host functions called by the
159/// guest.
160pub type ReentrantInstance<'a, UserData> = FunctionEnvMut<'a, Environment<UserData>>;
161
162impl<UserData: 'static> Instance for ReentrantInstance<'_, UserData> {
163    type Runtime = Wasmer;
164    type UserData = UserData;
165    type UserDataReference<'a>
166        = MutexGuard<'a, UserData>
167    where
168        Self::UserData: 'a,
169        Self: 'a;
170    type UserDataMutReference<'a>
171        = MutexGuard<'a, UserData>
172    where
173        Self::UserData: 'a,
174        Self: 'a;
175
176    fn load_export(&mut self, name: &str) -> Option<Extern> {
177        self.data_mut().load_export(name)
178    }
179
180    fn user_data(&self) -> Self::UserDataReference<'_> {
181        FunctionEnvMut::data(self).user_data()
182    }
183
184    fn user_data_mut(&mut self) -> Self::UserDataMutReference<'_> {
185        FunctionEnvMut::data_mut(self).user_data()
186    }
187}
188
189/// A slot to store a [`wasmer::Instance`] in a way that can be shared with reentrant calls.
190pub struct Environment<UserData> {
191    exports: Arc<OnceLock<wasmer::Exports>>,
192    user_data: Arc<Mutex<UserData>>,
193}
194
195impl<UserData> Environment<UserData> {
196    /// Creates a new [`Environment`] with no associated instance.
197    fn new(user_data: UserData) -> Self {
198        Environment {
199            exports: Arc::new(OnceLock::new()),
200            user_data: Arc::new(Mutex::new(user_data)),
201        }
202    }
203
204    /// Loads an export from the current instance.
205    ///
206    /// # Panics
207    ///
208    /// If the slot is empty.
209    fn load_export(&mut self, name: &str) -> Option<Extern> {
210        self.exports
211            .get()
212            .expect("Attempted to get export before instance is loaded")
213            .get_extern(name)
214            .cloned()
215    }
216
217    /// Returns a reference to the `UserData` stored in this [`Environment`].
218    fn user_data(&self) -> MutexGuard<'_, UserData> {
219        self.user_data
220            .try_lock()
221            .expect("Unexpected reentrant access to data")
222    }
223}
224
225impl<UserData> Clone for Environment<UserData> {
226    fn clone(&self) -> Self {
227        Environment {
228            exports: self.exports.clone(),
229            user_data: self.user_data.clone(),
230        }
231    }
232}