dusk_wasmtime/compile/
runtime.rs

1use crate::compile::HashedEngineCompileEnv;
2#[cfg(feature = "component-model")]
3use crate::component::Component;
4use crate::{CodeBuilder, CodeMemory, Engine, Module};
5use anyhow::{Context, Error, Result};
6use object::write::WritableBuffer;
7use std::sync::Arc;
8use wasmtime_environ::{FinishedObject, ObjectBuilder, ObjectKind};
9use wasmtime_runtime::MmapVec;
10
11impl<'a> CodeBuilder<'a> {
12    fn compile_cached<T>(
13        &self,
14        build_artifacts: fn(&Engine, &[u8]) -> Result<(MmapVecWrapper, Option<T>)>,
15    ) -> Result<(Arc<CodeMemory>, Option<T>)> {
16        let wasm = self.wasm_binary()?;
17
18        self.engine
19            .check_compatible_with_native_host()
20            .context("compilation settings are not compatible with the native host")?;
21
22        #[cfg(feature = "cache")]
23        {
24            let state = (
25                HashedEngineCompileEnv(self.engine),
26                &wasm,
27                // Don't hash this as it's just its own "pure" function pointer.
28                NotHashed(build_artifacts),
29            );
30            let (code, info_and_types) =
31                wasmtime_cache::ModuleCacheEntry::new("wasmtime", self.engine.cache_config())
32                    .get_data_raw(
33                        &state,
34                        // Cache miss, compute the actual artifacts
35                        |(engine, wasm, build_artifacts)| -> Result<_> {
36                            let (mmap, info) = (build_artifacts.0)(engine.0, wasm)?;
37                            let code = publish_mmap(mmap.0)?;
38                            Ok((code, info))
39                        },
40                        // Implementation of how to serialize artifacts
41                        |(_engine, _wasm, _), (code, _info_and_types)| Some(code.mmap().to_vec()),
42                        // Cache hit, deserialize the provided artifacts
43                        |(engine, wasm, _), serialized_bytes| {
44                            let kind = if wasmparser::Parser::is_component(&wasm) {
45                                ObjectKind::Component
46                            } else {
47                                ObjectKind::Module
48                            };
49                            let code = engine.0.load_code_bytes(&serialized_bytes, kind).ok()?;
50                            Some((code, None))
51                        },
52                    )?;
53            return Ok((code, info_and_types));
54        }
55
56        #[cfg(not(feature = "cache"))]
57        {
58            let (mmap, info_and_types) = build_artifacts(self.engine, &wasm)?;
59            let code = publish_mmap(mmap.0)?;
60            return Ok((code, info_and_types));
61        }
62
63        struct NotHashed<T>(T);
64
65        impl<T> std::hash::Hash for NotHashed<T> {
66            fn hash<H: std::hash::Hasher>(&self, _hasher: &mut H) {}
67        }
68    }
69
70    /// Same as [`CodeBuilder::compile_module_serialized`] except that a
71    /// [`Module`](crate::Module) is produced instead.
72    ///
73    /// Note that this method will cache compilations if the `cache` feature is
74    /// enabled and turned on in [`Config`](crate::Config).
75    #[cfg_attr(docsrs, doc(cfg(feature = "runtime")))]
76    pub fn compile_module(&self) -> Result<Module> {
77        let (code, info_and_types) = self.compile_cached(super::build_artifacts)?;
78        Module::from_parts(self.engine, code, info_and_types)
79    }
80
81    /// Same as [`CodeBuilder::compile_module`] except that it compiles a
82    /// [`Component`] instead of a module.
83    #[cfg(feature = "component-model")]
84    #[cfg_attr(
85        docsrs,
86        doc(cfg(all(feature = "runtime", feature = "component-model")))
87    )]
88    pub fn compile_component(&self) -> Result<Component> {
89        let (code, artifacts) = self.compile_cached(super::build_component_artifacts)?;
90        Component::from_parts(self.engine, code, artifacts)
91    }
92}
93
94fn publish_mmap(mmap: MmapVec) -> Result<Arc<CodeMemory>> {
95    let mut code = CodeMemory::new(mmap)?;
96    code.publish()?;
97    Ok(Arc::new(code))
98}
99
100/// Write an object out to an [`MmapVec`] so that it can be marked executable
101/// before running.
102///
103/// The returned `MmapVec` will contain the serialized version of `obj`
104/// and is sized appropriately to the exact size of the object serialized.
105pub fn finish_object(obj: ObjectBuilder<'_>) -> Result<MmapVec> {
106    Ok(<MmapVecWrapper as FinishedObject>::finish_object(obj)?.0)
107}
108
109pub(crate) struct MmapVecWrapper(pub MmapVec);
110
111impl FinishedObject for MmapVecWrapper {
112    fn finish_object(obj: ObjectBuilder<'_>) -> Result<Self> {
113        let mut result = ObjectMmap::default();
114        return match obj.finish(&mut result) {
115            Ok(()) => {
116                assert!(result.mmap.is_some(), "no reserve");
117                let mmap = result.mmap.expect("reserve not called");
118                assert_eq!(mmap.len(), result.len);
119                Ok(MmapVecWrapper(mmap))
120            }
121            Err(e) => match result.err.take() {
122                Some(original) => Err(original.context(e)),
123                None => Err(e.into()),
124            },
125        };
126
127        /// Helper struct to implement the `WritableBuffer` trait from the `object`
128        /// crate.
129        ///
130        /// This enables writing an object directly into an mmap'd memory so it's
131        /// immediately usable for execution after compilation. This implementation
132        /// relies on a call to `reserve` happening once up front with all the needed
133        /// data, and the mmap internally does not attempt to grow afterwards.
134        #[derive(Default)]
135        struct ObjectMmap {
136            mmap: Option<MmapVec>,
137            len: usize,
138            err: Option<Error>,
139        }
140
141        impl WritableBuffer for ObjectMmap {
142            fn len(&self) -> usize {
143                self.len
144            }
145
146            fn reserve(&mut self, additional: usize) -> Result<(), ()> {
147                assert!(self.mmap.is_none(), "cannot reserve twice");
148                self.mmap = match MmapVec::with_capacity(additional) {
149                    Ok(mmap) => Some(mmap),
150                    Err(e) => {
151                        self.err = Some(e);
152                        return Err(());
153                    }
154                };
155                Ok(())
156            }
157
158            fn resize(&mut self, new_len: usize) {
159                // Resizing always appends 0 bytes and since new mmaps start out as 0
160                // bytes we don't actually need to do anything as part of this other
161                // than update our own length.
162                if new_len <= self.len {
163                    return;
164                }
165                self.len = new_len;
166            }
167
168            fn write_bytes(&mut self, val: &[u8]) {
169                let mmap = self.mmap.as_mut().expect("write before reserve");
170                mmap[self.len..][..val.len()].copy_from_slice(val);
171                self.len += val.len();
172            }
173        }
174    }
175}