1 #[cfg(all(feature = "runtime", feature = "component-model"))]
2 use crate::component::Component;
3 use crate::Engine;
4 #[cfg(feature = "runtime")]
5 use crate::{instantiate::MmapVecWrapper, CodeMemory, Module};
6 use anyhow::{anyhow, bail, Context, Result};
7 use std::borrow::Cow;
8 use std::path::Path;
9 use std::sync::Arc;
10 use wasmtime_environ::ObjectKind;
11 #[cfg(feature = "runtime")]
12 use wasmtime_runtime::MmapVec;
13 
14 /// Builder-style structure used to create a [`Module`](crate::Module) or
15 /// pre-compile a module to a serialized list of bytes.
16 ///
17 /// This structure can be used for more advanced configuration when compiling a
18 /// WebAssembly module. Most configuration can use simpler constructors such as:
19 ///
20 /// * [`Module::new`](crate::Module::new)
21 /// * [`Module::from_file`](crate::Module::from_file)
22 /// * [`Module::from_binary`](crate::Module::from_binary)
23 ///
24 /// Note that a [`CodeBuilder`] always involves compiling WebAssembly bytes
25 /// to machine code. To deserialize a list of bytes use
26 /// [`Module::deserialize`](crate::Module::deserialize) instead.
27 ///
28 /// A [`CodeBuilder`] requires a source of WebAssembly bytes to be configured
29 /// before calling [`compile_module_serialized`] or [`compile_module`]. This can be
30 /// provided with either the [`wasm`] or [`wasm_file`] method. Note that only
31 /// a single source of bytes can be provided.
32 ///
33 /// # WebAssembly Text Format
34 ///
35 /// This builder supports the WebAssembly Text Format (`*.wat` files).
36 /// WebAssembly text files are automatically converted to a WebAssembly binary
37 /// and then the binary is compiled. This requires the `wat` feature of the
38 /// `wasmtime` crate to be enabled, and the feature is enabled by default.
39 ///
40 /// If the text format is not desired then the [`CodeBuilder::wat`] method
41 /// can be used to disable this conversion.
42 ///
43 /// [`compile_module_serialized`]: CodeBuilder::compile_module_serialized
44 /// [`compile_module`]: CodeBuilder::compile_module
45 /// [`wasm`]: CodeBuilder::wasm
46 /// [`wasm_file`]: CodeBuilder::wasm_file
47 pub struct CodeBuilder<'a> {
48     engine: &'a Engine,
49     wasm: Option<Cow<'a, [u8]>>,
50     wasm_path: Option<Cow<'a, Path>>,
51     wat: bool,
52 }
53 
54 impl<'a> CodeBuilder<'a> {
55     /// Creates a new builder which will insert modules into the specified
56     /// [`Engine`].
57     pub fn new(engine: &'a Engine) -> CodeBuilder<'a> {
58         CodeBuilder {
59             engine,
60             wasm: None,
61             wasm_path: None,
62             wat: cfg!(feature = "wat"),
63         }
64     }
65 
66     /// Configures the WebAssembly binary or text that is being compiled.
67     ///
68     /// The `wasm_bytes` parameter is either a binary WebAssembly file or a
69     /// WebAssembly module in its text format. This will be stored within the
70     /// [`CodeBuilder`] for processing later when compilation is finalized.
71     ///
72     /// The optional `wasm_path` parameter is the path to the `wasm_bytes` on
73     /// disk, if any. This may be used for diagnostics and other
74     /// debugging-related purposes, but this method will not read the path
75     /// specified.
76     ///
77     /// # Errors
78     ///
79     /// If wasm bytes have already been configured via a call to this method or
80     /// [`CodeBuilder::wasm_file`] then an error will be returned.
81     pub fn wasm(&mut self, wasm_bytes: &'a [u8], wasm_path: Option<&'a Path>) -> Result<&mut Self> {
82         if self.wasm.is_some() {
83             bail!("cannot call `wasm` or `wasm_file` twice");
84         }
85         self.wasm = Some(wasm_bytes.into());
86         self.wasm_path = wasm_path.map(|p| p.into());
87         Ok(self)
88     }
89 
90     /// Configures whether the WebAssembly text format is supported in this
91     /// builder.
92     ///
93     /// This support is enabled by default if the `wat` crate feature is also
94     /// enabled.
95     ///
96     /// # Errors
97     ///
98     /// If this feature is explicitly enabled here via this method and the
99     /// `wat` crate feature is disabled then an error will be returned.
100     pub fn wat(&mut self, enable: bool) -> Result<&mut Self> {
101         if !cfg!(feature = "wat") && enable {
102             bail!("support for `wat` was disabled at compile time");
103         }
104         self.wat = enable;
105         Ok(self)
106     }
107 
108     /// Reads the `file` specified for the WebAssembly bytes that are going to
109     /// be compiled.
110     ///
111     /// This method will read `file` from the filesystem and interpret it
112     /// either as a WebAssembly binary or as a WebAssembly text file. The
113     /// contents are inspected to do this, the file extension is not consulted.
114     ///
115     /// # Errors
116     ///
117     /// If wasm bytes have already been configured via a call to this method or
118     /// [`CodeBuilder::wasm`] then an error will be returned.
119     ///
120     /// If `file` can't be read or an error happens reading it then that will
121     /// also be returned.
122     pub fn wasm_file(&mut self, file: &'a Path) -> Result<&mut Self> {
123         if self.wasm.is_some() {
124             bail!("cannot call `wasm` or `wasm_file` twice");
125         }
126         let wasm = std::fs::read(file)
127             .with_context(|| format!("failed to read input file: {}", file.display()))?;
128         self.wasm = Some(wasm.into());
129         self.wasm_path = Some(file.into());
130         Ok(self)
131     }
132 
133     fn wasm_binary(&self) -> Result<Cow<'_, [u8]>> {
134         let wasm = self
135             .wasm
136             .as_ref()
137             .ok_or_else(|| anyhow!("no wasm bytes have been configured"))?;
138         if self.wat {
139             #[cfg(feature = "wat")]
140             return wat::parse_bytes(wasm).map_err(|mut e| {
141                 if let Some(path) = &self.wasm_path {
142                     e.set_path(path);
143                 }
144                 e.into()
145             });
146         }
147         Ok((&wasm[..]).into())
148     }
149 
150     #[cfg(feature = "runtime")]
151     fn compile_cached<T>(
152         &self,
153         build_artifacts: fn(&Engine, &[u8]) -> Result<(MmapVecWrapper, Option<T>)>,
154     ) -> Result<(Arc<CodeMemory>, Option<T>)> {
155         let wasm = self.wasm_binary()?;
156 
157         self.engine
158             .check_compatible_with_native_host()
159             .context("compilation settings are not compatible with the native host")?;
160 
161         #[cfg(feature = "cache")]
162         {
163             let state = (
164                 HashedEngineCompileEnv(self.engine),
165                 &wasm,
166                 // Don't hash this as it's just its own "pure" function pointer.
167                 NotHashed(build_artifacts),
168             );
169             let (code, info_and_types) =
170                 wasmtime_cache::ModuleCacheEntry::new("wasmtime", self.engine.cache_config())
171                     .get_data_raw(
172                         &state,
173                         // Cache miss, compute the actual artifacts
174                         |(engine, wasm, build_artifacts)| -> Result<_> {
175                             let (mmap, info) = (build_artifacts.0)(engine.0, wasm)?;
176                             let code = publish_mmap(mmap.0)?;
177                             Ok((code, info))
178                         },
179                         // Implementation of how to serialize artifacts
180                         |(_engine, _wasm, _), (code, _info_and_types)| Some(code.mmap().to_vec()),
181                         // Cache hit, deserialize the provided artifacts
182                         |(engine, _wasm, _), serialized_bytes| {
183                             let code = engine
184                                 .0
185                                 .load_code_bytes(&serialized_bytes, ObjectKind::Module)
186                                 .ok()?;
187                             Some((code, None))
188                         },
189                     )?;
190             return Ok((code, info_and_types));
191         }
192 
193         #[cfg(not(feature = "cache"))]
194         {
195             let (mmap, info_and_types) = build_artifacts(self.engine, &wasm)?;
196             let code = publish_mmap(mmap.0)?;
197             return Ok((code, info_and_types));
198         }
199 
200         struct NotHashed<T>(T);
201 
202         impl<T> std::hash::Hash for NotHashed<T> {
203             fn hash<H: std::hash::Hasher>(&self, _hasher: &mut H) {}
204         }
205     }
206 
207     /// Finishes this compilation and produces a serialized list of bytes.
208     ///
209     /// This method requires that either [`CodeBuilder::wasm`] or
210     /// [`CodeBuilder::wasm_file`] was invoked prior to indicate what is
211     /// being compiled.
212     ///
213     /// This method will block the current thread until compilation has
214     /// finished, and when done the serialized artifact will be returned.
215     ///
216     /// Note that this method will never cache compilations, even if the
217     /// `cache` feature is enabled.
218     ///
219     /// # Errors
220     ///
221     /// This can fail if the input wasm module was not valid or if another
222     /// compilation-related error is encountered.
223     pub fn compile_module_serialized(&self) -> Result<Vec<u8>> {
224         let wasm = self.wasm_binary()?;
225         let (v, _) = super::build_artifacts(self.engine, &wasm)?;
226         Ok(v)
227     }
228 
229     /// Same as [`CodeBuilder::compile_module_serialized`] except that a
230     /// [`Module`](crate::Module) is produced instead.
231     ///
232     /// Note that this method will cache compilations if the `cache` feature is
233     /// enabled and turned on in [`Config`](crate::Config).
234     #[cfg(feature = "runtime")]
235     #[cfg_attr(docsrs, doc(cfg(feature = "runtime")))]
236     pub fn compile_module(&self) -> Result<Module> {
237         let (code, info_and_types) = self.compile_cached(super::build_artifacts)?;
238         Module::from_parts(self.engine, code, info_and_types)
239     }
240 
241     /// Same as [`CodeBuilder::compile_module_serialized`] except that it
242     /// compiles a serialized [`Component`] instead of a module.
243     #[cfg(feature = "component-model")]
244     #[cfg_attr(docsrs, doc(cfg(feature = "component-model")))]
245     pub fn compile_component_serialized(&self) -> Result<Vec<u8>> {
246         let bytes = self.wasm_binary()?;
247         let (v, _) = super::build_component_artifacts(self.engine, &bytes)?;
248         Ok(v)
249     }
250 
251     /// Same as [`CodeBuilder::compile_module`] except that it compiles a
252     /// [`Component`] instead of a module.
253     #[cfg(all(feature = "runtime", feature = "component-model"))]
254     #[cfg_attr(
255         docsrs,
256         doc(cfg(all(feature = "runtime", feature = "component-model")))
257     )]
258     pub fn compile_component(&self) -> Result<Component> {
259         let (code, artifacts) = self.compile_cached(super::build_component_artifacts)?;
260         Component::from_parts(self.engine, code, artifacts)
261     }
262 }
263 
264 /// This is a helper struct used when caching to hash the state of an `Engine`
265 /// used for module compilation.
266 ///
267 /// The hash computed for this structure is used to key the global wasmtime
268 /// cache and dictates whether artifacts are reused. Consequently the contents
269 /// of this hash dictate when artifacts are or aren't re-used.
270 pub struct HashedEngineCompileEnv<'a>(pub &'a Engine);
271 
272 impl std::hash::Hash for HashedEngineCompileEnv<'_> {
273     fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
274         // Hash the compiler's state based on its target and configuration.
275         let compiler = self.0.compiler();
276         compiler.triple().hash(hasher);
277         compiler.flags().hash(hasher);
278         compiler.isa_flags().hash(hasher);
279 
280         // Hash configuration state read for compilation
281         let config = self.0.config();
282         self.0.tunables().hash(hasher);
283         config.features.hash(hasher);
284         config.wmemcheck.hash(hasher);
285 
286         // Catch accidental bugs of reusing across crate versions.
287         config.module_version.hash(hasher);
288     }
289 }
290 
291 #[cfg(feature = "runtime")]
292 fn publish_mmap(mmap: MmapVec) -> Result<Arc<CodeMemory>> {
293     let mut code = CodeMemory::new(mmap)?;
294     code.publish()?;
295     Ok(Arc::new(code))
296 }
297