1 use crate::Engine;
2 use anyhow::{anyhow, bail, Context, Result};
3 use std::borrow::Cow;
4 use std::path::Path;
5 
6 /// Builder-style structure used to create a [`Module`](crate::module::Module) or
7 /// pre-compile a module to a serialized list of bytes.
8 ///
9 /// This structure can be used for more advanced configuration when compiling a
10 /// WebAssembly module. Most configuration can use simpler constructors such as:
11 ///
12 /// * [`Module::new`](crate::Module::new)
13 /// * [`Module::from_file`](crate::Module::from_file)
14 /// * [`Module::from_binary`](crate::Module::from_binary)
15 ///
16 /// Note that a [`CodeBuilder`] always involves compiling WebAssembly bytes
17 /// to machine code. To deserialize a list of bytes use
18 /// [`Module::deserialize`](crate::Module::deserialize) instead.
19 ///
20 /// A [`CodeBuilder`] requires a source of WebAssembly bytes to be configured
21 /// before calling [`compile_module_serialized`] or [`compile_module`]. This can be
22 /// provided with either the [`wasm`] or [`wasm_file`] method. Note that only
23 /// a single source of bytes can be provided.
24 ///
25 /// # WebAssembly Text Format
26 ///
27 /// This builder supports the WebAssembly Text Format (`*.wat` files).
28 /// WebAssembly text files are automatically converted to a WebAssembly binary
29 /// and then the binary is compiled. This requires the `wat` feature of the
30 /// `wasmtime` crate to be enabled, and the feature is enabled by default.
31 ///
32 /// If the text format is not desired then the [`CodeBuilder::wat`] method
33 /// can be used to disable this conversion.
34 ///
35 /// [`compile_module_serialized`]: CodeBuilder::compile_module_serialized
36 /// [`compile_module`]: CodeBuilder::compile_module
37 /// [`wasm`]: CodeBuilder::wasm
38 /// [`wasm_file`]: CodeBuilder::wasm_file
39 pub struct CodeBuilder<'a> {
40     pub(super) engine: &'a Engine,
41     wasm: Option<Cow<'a, [u8]>>,
42     wasm_path: Option<Cow<'a, Path>>,
43     wat: bool,
44 }
45 
46 impl<'a> CodeBuilder<'a> {
47     /// Creates a new builder which will insert modules into the specified
48     /// [`Engine`].
49     pub fn new(engine: &'a Engine) -> CodeBuilder<'a> {
50         CodeBuilder {
51             engine,
52             wasm: None,
53             wasm_path: None,
54             wat: cfg!(feature = "wat"),
55         }
56     }
57 
58     /// Configures the WebAssembly binary or text that is being compiled.
59     ///
60     /// The `wasm_bytes` parameter is either a binary WebAssembly file or a
61     /// WebAssembly module in its text format. This will be stored within the
62     /// [`CodeBuilder`] for processing later when compilation is finalized.
63     ///
64     /// The optional `wasm_path` parameter is the path to the `wasm_bytes` on
65     /// disk, if any. This may be used for diagnostics and other
66     /// debugging-related purposes, but this method will not read the path
67     /// specified.
68     ///
69     /// # Errors
70     ///
71     /// If wasm bytes have already been configured via a call to this method or
72     /// [`CodeBuilder::wasm_file`] then an error will be returned.
73     pub fn wasm(&mut self, wasm_bytes: &'a [u8], wasm_path: Option<&'a Path>) -> Result<&mut Self> {
74         if self.wasm.is_some() {
75             bail!("cannot call `wasm` or `wasm_file` twice");
76         }
77         self.wasm = Some(wasm_bytes.into());
78         self.wasm_path = wasm_path.map(|p| p.into());
79         Ok(self)
80     }
81 
82     /// Configures whether the WebAssembly text format is supported in this
83     /// builder.
84     ///
85     /// This support is enabled by default if the `wat` crate feature is also
86     /// enabled.
87     ///
88     /// # Errors
89     ///
90     /// If this feature is explicitly enabled here via this method and the
91     /// `wat` crate feature is disabled then an error will be returned.
92     pub fn wat(&mut self, enable: bool) -> Result<&mut Self> {
93         if !cfg!(feature = "wat") && enable {
94             bail!("support for `wat` was disabled at compile time");
95         }
96         self.wat = enable;
97         Ok(self)
98     }
99 
100     /// Reads the `file` specified for the WebAssembly bytes that are going to
101     /// be compiled.
102     ///
103     /// This method will read `file` from the filesystem and interpret it
104     /// either as a WebAssembly binary or as a WebAssembly text file. The
105     /// contents are inspected to do this, the file extension is not consulted.
106     ///
107     /// # Errors
108     ///
109     /// If wasm bytes have already been configured via a call to this method or
110     /// [`CodeBuilder::wasm`] then an error will be returned.
111     ///
112     /// If `file` can't be read or an error happens reading it then that will
113     /// also be returned.
114     pub fn wasm_file(&mut self, file: &'a Path) -> Result<&mut Self> {
115         if self.wasm.is_some() {
116             bail!("cannot call `wasm` or `wasm_file` twice");
117         }
118         let wasm = std::fs::read(file)
119             .with_context(|| format!("failed to read input file: {}", file.display()))?;
120         self.wasm = Some(wasm.into());
121         self.wasm_path = Some(file.into());
122         Ok(self)
123     }
124 
125     pub(super) fn wasm_binary(&self) -> Result<Cow<'_, [u8]>> {
126         let wasm = self
127             .wasm
128             .as_ref()
129             .ok_or_else(|| anyhow!("no wasm bytes have been configured"))?;
130         if self.wat {
131             #[cfg(feature = "wat")]
132             return wat::parse_bytes(wasm).map_err(|mut e| {
133                 if let Some(path) = &self.wasm_path {
134                     e.set_path(path);
135                 }
136                 e.into()
137             });
138         }
139         Ok((&wasm[..]).into())
140     }
141 
142     /// Finishes this compilation and produces a serialized list of bytes.
143     ///
144     /// This method requires that either [`CodeBuilder::wasm`] or
145     /// [`CodeBuilder::wasm_file`] was invoked prior to indicate what is
146     /// being compiled.
147     ///
148     /// This method will block the current thread until compilation has
149     /// finished, and when done the serialized artifact will be returned.
150     ///
151     /// Note that this method will never cache compilations, even if the
152     /// `cache` feature is enabled.
153     ///
154     /// # Errors
155     ///
156     /// This can fail if the input wasm module was not valid or if another
157     /// compilation-related error is encountered.
158     pub fn compile_module_serialized(&self) -> Result<Vec<u8>> {
159         let wasm = self.wasm_binary()?;
160         let (v, _) = super::build_artifacts(self.engine, &wasm)?;
161         Ok(v)
162     }
163 
164     /// Same as [`CodeBuilder::compile_module_serialized`] except that it
165     /// compiles a serialized [`Component`](crate::component::Component)
166     /// instead of a module.
167     #[cfg(feature = "component-model")]
168     #[cfg_attr(docsrs, doc(cfg(feature = "component-model")))]
169     pub fn compile_component_serialized(&self) -> Result<Vec<u8>> {
170         let bytes = self.wasm_binary()?;
171         let (v, _) = super::build_component_artifacts(self.engine, &bytes)?;
172         Ok(v)
173     }
174 }
175 
176 /// This is a helper struct used when caching to hash the state of an `Engine`
177 /// used for module compilation.
178 ///
179 /// The hash computed for this structure is used to key the global wasmtime
180 /// cache and dictates whether artifacts are reused. Consequently the contents
181 /// of this hash dictate when artifacts are or aren't re-used.
182 pub struct HashedEngineCompileEnv<'a>(pub &'a Engine);
183 
184 impl std::hash::Hash for HashedEngineCompileEnv<'_> {
185     fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
186         // Hash the compiler's state based on its target and configuration.
187         let compiler = self.0.compiler();
188         compiler.triple().hash(hasher);
189         compiler.flags().hash(hasher);
190         compiler.isa_flags().hash(hasher);
191 
192         // Hash configuration state read for compilation
193         let config = self.0.config();
194         self.0.tunables().hash(hasher);
195         config.features.hash(hasher);
196         config.wmemcheck.hash(hasher);
197 
198         // Catch accidental bugs of reusing across crate versions.
199         config.module_version.hash(hasher);
200     }
201 }
202