wasmer_engine_staticlib/
engine.rs

1use crate::StaticlibArtifact;
2use loupe::MemoryUsage;
3use std::io::Read;
4use std::path::Path;
5use std::sync::{Arc, Mutex};
6#[cfg(feature = "compiler")]
7use wasmer_compiler::Compiler;
8use wasmer_compiler::{CompileError, Target};
9use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
10#[cfg(feature = "compiler")]
11use wasmer_types::Features;
12use wasmer_types::FunctionType;
13use wasmer_vm::{
14    FuncDataRegistry, SignatureRegistry, VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex,
15};
16
17/// A WebAssembly `Staticlib` Engine.
18#[derive(Clone, MemoryUsage)]
19pub struct StaticlibEngine {
20    inner: Arc<Mutex<StaticlibEngineInner>>,
21    /// The target for the compiler
22    target: Arc<Target>,
23    engine_id: EngineId,
24}
25
26impl StaticlibEngine {
27    /// Create a new `StaticlibEngine` with the given config
28    #[cfg(feature = "compiler")]
29    pub fn new(compiler: Box<dyn Compiler>, target: Target, features: Features) -> Self {
30        Self {
31            inner: Arc::new(Mutex::new(StaticlibEngineInner {
32                compiler: Some(compiler),
33                signatures: SignatureRegistry::new(),
34                func_data: Arc::new(FuncDataRegistry::new()),
35                prefixer: None,
36                features,
37            })),
38            target: Arc::new(target),
39            engine_id: EngineId::default(),
40        }
41    }
42
43    /// Create a headless `StaticlibEngine`
44    ///
45    /// A headless engine is an engine without any compiler attached.
46    /// This is useful for assuring a minimal runtime for running
47    /// WebAssembly modules.
48    ///
49    /// For example, for running in IoT devices where compilers are very
50    /// expensive, or also to optimize startup speed.
51    ///
52    /// # Important
53    ///
54    /// Headless engines can't compile or validate any modules,
55    /// they just take already processed Modules (via `Module::serialize`).
56    pub fn headless() -> Self {
57        Self {
58            inner: Arc::new(Mutex::new(StaticlibEngineInner {
59                #[cfg(feature = "compiler")]
60                compiler: None,
61                #[cfg(feature = "compiler")]
62                features: Features::default(),
63                signatures: SignatureRegistry::new(),
64                func_data: Arc::new(FuncDataRegistry::new()),
65                prefixer: None,
66            })),
67            target: Arc::new(Target::default()),
68            engine_id: EngineId::default(),
69        }
70    }
71
72    /// Sets a prefixer for the Wasm module, so we can avoid any
73    /// collisions in the exported function names on the generated
74    /// object.
75    ///
76    /// This, allows us to rather than have functions named
77    /// `wasmer_function_1` to be named `wasmer_function_PREFIX_1`.
78    ///
79    /// # Important
80    ///
81    /// This prefixer function should be deterministic, so the
82    /// compilation remains deterministic.
83    pub fn set_deterministic_prefixer<F>(&mut self, prefixer: F)
84    where
85        F: Fn(&[u8]) -> String + Send + 'static,
86    {
87        let mut inner = self.inner_mut();
88        inner.prefixer = Some(Box::new(prefixer));
89    }
90
91    pub(crate) fn inner(&self) -> std::sync::MutexGuard<'_, StaticlibEngineInner> {
92        self.inner.lock().unwrap()
93    }
94
95    pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, StaticlibEngineInner> {
96        self.inner.lock().unwrap()
97    }
98}
99
100impl Engine for StaticlibEngine {
101    /// The target
102    fn target(&self) -> &Target {
103        &self.target
104    }
105
106    /// Register a signature
107    fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex {
108        let compiler = self.inner();
109        compiler.signatures().register(func_type)
110    }
111
112    fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef {
113        let compiler = self.inner();
114        compiler.func_data().register(func_data)
115    }
116
117    /// Lookup a signature
118    fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
119        let compiler = self.inner();
120        compiler.signatures().lookup(sig)
121    }
122
123    /// Validates a WebAssembly module
124    fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
125        self.inner().validate(binary)
126    }
127
128    /// Compile a WebAssembly binary
129    #[cfg(feature = "compiler")]
130    fn compile(
131        &self,
132        binary: &[u8],
133        tunables: &dyn Tunables,
134    ) -> Result<Arc<dyn Artifact>, CompileError> {
135        Ok(Arc::new(StaticlibArtifact::new(&self, binary, tunables)?))
136    }
137
138    /// Compile a WebAssembly binary (it will fail because the `compiler` flag is disabled).
139    #[cfg(not(feature = "compiler"))]
140    fn compile(
141        &self,
142        _binary: &[u8],
143        _tunables: &dyn Tunables,
144    ) -> Result<Arc<dyn Artifact>, CompileError> {
145        Err(CompileError::Codegen(
146            "The `StaticlibEngine` is operating in headless mode, so it cannot compile a module."
147                .to_string(),
148        ))
149    }
150
151    /// Deserializes a WebAssembly module (binary content of a static object file)
152    unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> {
153        Ok(Arc::new(StaticlibArtifact::deserialize(&self, &bytes)?))
154    }
155
156    /// Deserializes a WebAssembly module from a path
157    ///
158    /// It should point to a static object file generated by this
159    /// engine.
160    unsafe fn deserialize_from_file(
161        &self,
162        file_ref: &Path,
163    ) -> Result<Arc<dyn Artifact>, DeserializeError> {
164        let mut f = std::fs::File::open(file_ref)?;
165        let mut vec = vec![];
166        f.read_to_end(&mut vec)?;
167
168        self.deserialize(&vec[..])
169    }
170
171    fn id(&self) -> &EngineId {
172        &self.engine_id
173    }
174
175    fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
176        Arc::new(self.clone())
177    }
178}
179
180/// The inner contents of `StaticlibEngine`
181#[derive(MemoryUsage)]
182pub struct StaticlibEngineInner {
183    /// The compiler
184    #[cfg(feature = "compiler")]
185    compiler: Option<Box<dyn Compiler>>,
186
187    /// The WebAssembly features to use
188    #[cfg(feature = "compiler")]
189    features: Features,
190
191    /// The signature registry is used mainly to operate with trampolines
192    /// performantly.
193    signatures: SignatureRegistry,
194
195    /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2
196    /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`.
197    /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped.
198    func_data: Arc<FuncDataRegistry>,
199
200    /// The prefixer returns the a String to prefix each of the
201    /// functions in the static object generated by the
202    /// `StaticlibEngine`, so we can assure no collisions.
203    #[loupe(skip)]
204    prefixer: Option<Box<dyn Fn(&[u8]) -> String + Send>>,
205}
206
207impl StaticlibEngineInner {
208    /// Gets the compiler associated to this engine.
209    #[cfg(feature = "compiler")]
210    pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
211        if self.compiler.is_none() {
212            return Err(CompileError::Codegen("The `StaticlibEngine` is operating in headless mode, so it can only execute already compiled Modules.".to_string()));
213        }
214        Ok(&**self
215            .compiler
216            .as_ref()
217            .expect("Can't get compiler reference"))
218    }
219
220    #[cfg(feature = "compiler")]
221    pub(crate) fn get_prefix(&self, bytes: &[u8]) -> String {
222        if let Some(prefixer) = &self.prefixer {
223            prefixer(&bytes)
224        } else {
225            "".to_string()
226        }
227    }
228
229    #[cfg(feature = "compiler")]
230    pub(crate) fn features(&self) -> &Features {
231        &self.features
232    }
233
234    /// Validate the module
235    #[cfg(feature = "compiler")]
236    pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
237        self.compiler()?.validate_module(self.features(), data)
238    }
239
240    /// Validate the module
241    #[cfg(not(feature = "compiler"))]
242    pub fn validate<'data>(&self, _data: &'data [u8]) -> Result<(), CompileError> {
243        Err(CompileError::Validate(
244            "The `StaticlibEngine` is not compiled with compiler support, which is required for validating".to_string(),
245        ))
246    }
247
248    /// Shared signature registry.
249    pub fn signatures(&self) -> &SignatureRegistry {
250        &self.signatures
251    }
252
253    /// Shared func metadata registry.
254    pub(crate) fn func_data(&self) -> &Arc<FuncDataRegistry> {
255        &self.func_data
256    }
257}