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     dwarf_package: Option<Cow<'a, [u8]>>,
44     dwarf_package_path: Option<Cow<'a, Path>>,
45     wat: bool,
46 }
47 
48 impl<'a> CodeBuilder<'a> {
49     /// Creates a new builder which will insert modules into the specified
50     /// [`Engine`].
51     pub fn new(engine: &'a Engine) -> CodeBuilder<'a> {
52         CodeBuilder {
53             engine,
54             wasm: None,
55             wasm_path: None,
56             dwarf_package: None,
57             dwarf_package_path: None,
58             wat: cfg!(feature = "wat"),
59         }
60     }
61 
62     /// Configures the WebAssembly binary or text that is being compiled.
63     ///
64     /// The `wasm_bytes` parameter is either a binary WebAssembly file or a
65     /// WebAssembly module in its text format. This will be stored within the
66     /// [`CodeBuilder`] for processing later when compilation is finalized.
67     ///
68     /// The optional `wasm_path` parameter is the path to the `wasm_bytes` on
69     /// disk, if any. This may be used for diagnostics and other
70     /// debugging-related purposes, but this method will not read the path
71     /// specified.
72     ///
73     /// # Errors
74     ///
75     /// If wasm bytes have already been configured via a call to this method or
76     /// [`CodeBuilder::wasm_file`] then an error will be returned.
77     pub fn wasm(&mut self, wasm_bytes: &'a [u8], wasm_path: Option<&'a Path>) -> Result<&mut Self> {
78         if self.wasm.is_some() {
79             bail!("cannot call `wasm` or `wasm_file` twice");
80         }
81         self.wasm = Some(wasm_bytes.into());
82         self.wasm_path = wasm_path.map(|p| p.into());
83 
84         if self.wasm_path.is_some() {
85             self.dwarf_package_from_wasm_path()?;
86         }
87 
88         Ok(self)
89     }
90 
91     /// Configures whether the WebAssembly text format is supported in this
92     /// builder.
93     ///
94     /// This support is enabled by default if the `wat` crate feature is also
95     /// enabled.
96     ///
97     /// # Errors
98     ///
99     /// If this feature is explicitly enabled here via this method and the
100     /// `wat` crate feature is disabled then an error will be returned.
101     pub fn wat(&mut self, enable: bool) -> Result<&mut Self> {
102         if !cfg!(feature = "wat") && enable {
103             bail!("support for `wat` was disabled at compile time");
104         }
105         self.wat = enable;
106         Ok(self)
107     }
108 
109     /// Reads the `file` specified for the WebAssembly bytes that are going to
110     /// be compiled.
111     ///
112     /// This method will read `file` from the filesystem and interpret it
113     /// either as a WebAssembly binary or as a WebAssembly text file. The
114     /// contents are inspected to do this, the file extension is not consulted.
115     ///
116     /// A DWARF package file will be probed using the root of `file` and with a
117     /// `.dwp` extension.  If found, it will be loaded and DWARF fusion
118     /// performed.
119     ///
120     /// # Errors
121     ///
122     /// If wasm bytes have already been configured via a call to this method or
123     /// [`CodeBuilder::wasm`] then an error will be returned.
124     ///
125     /// If `file` can't be read or an error happens reading it then that will
126     /// also be returned.
127     ///
128     /// If DWARF fusion is performed and the DWARF packaged file cannot be read
129     /// then an error will be returned.
130     pub fn wasm_file(&mut self, file: &'a Path) -> Result<&mut Self> {
131         if self.wasm.is_some() {
132             bail!("cannot call `wasm` or `wasm_file` twice");
133         }
134         let wasm = std::fs::read(file)
135             .with_context(|| format!("failed to read input file: {}", file.display()))?;
136         self.wasm = Some(wasm.into());
137         self.wasm_path = Some(file.into());
138         self.dwarf_package_from_wasm_path()?;
139 
140         Ok(self)
141     }
142 
143     pub(super) fn wasm_binary(&self) -> Result<Cow<'_, [u8]>> {
144         let wasm = self
145             .wasm
146             .as_ref()
147             .ok_or_else(|| anyhow!("no wasm bytes have been configured"))?;
148         if self.wat {
149             #[cfg(feature = "wat")]
150             return wat::parse_bytes(wasm).map_err(|mut e| {
151                 if let Some(path) = &self.wasm_path {
152                     e.set_path(path);
153                 }
154                 e.into()
155             });
156         }
157         Ok((&wasm[..]).into())
158     }
159 
160     /// Explicitly specify DWARF `.dwp` path.
161     ///
162     /// # Errors
163     ///
164     /// This method will return an error if the `.dwp` file has already been set
165     /// through [`CodeBuilder::dwarf_package`] or auto-detection in
166     /// [`CodeBuilder::wasm_file`].
167     ///
168     /// This method will also return an error if `file` cannot be read.
169     pub fn dwarf_package_file(&mut self, file: &Path) -> Result<&mut Self> {
170         if self.dwarf_package.is_some() {
171             bail!("cannot call `dwarf_package` or `dwarf_package_file` twice");
172         }
173 
174         let dwarf_package = std::fs::read(file)
175             .with_context(|| format!("failed to read dwarf input file: {}", file.display()))?;
176         self.dwarf_package_path = Some(Cow::Owned(file.to_owned()));
177         self.dwarf_package = Some(dwarf_package.into());
178 
179         Ok(self)
180     }
181 
182     fn dwarf_package_from_wasm_path(&mut self) -> Result<&mut Self> {
183         let dwarf_package_path_buf = self.wasm_path.as_ref().unwrap().with_extension("dwp");
184         if dwarf_package_path_buf.exists() {
185             return self.dwarf_package_file(dwarf_package_path_buf.as_path());
186         }
187 
188         Ok(self)
189     }
190 
191     /// Gets the DWARF package.
192     pub(super) fn dwarf_package_binary(&self) -> Option<&[u8]> {
193         return self.dwarf_package.as_deref();
194     }
195 
196     /// Set the DWARF package binary.
197     ///
198     /// Initializes `dwarf_package` from `dwp_bytes` in preparation for
199     /// DWARF fusion. Allows the DWARF package to be supplied as a byte array
200     /// when the file probing performed in `wasm_file` is not appropriate.
201     ///
202     /// # Errors
203     ///
204     /// Returns an error if the `*.dwp` file is already set via auto-probing in
205     /// [`CodeBuilder::wasm_file`] or explicitly via
206     /// [`CodeBuilder::dwarf_package_file`].
207     pub fn dwarf_package(&mut self, dwp_bytes: &'a [u8]) -> Result<&mut Self> {
208         if self.dwarf_package.is_some() {
209             bail!("cannot call `dwarf_package` or `dwarf_package_file` twice");
210         }
211         self.dwarf_package = Some(dwp_bytes.into());
212         Ok(self)
213     }
214 
215     /// Finishes this compilation and produces a serialized list of bytes.
216     ///
217     /// This method requires that either [`CodeBuilder::wasm`] or
218     /// [`CodeBuilder::wasm_file`] was invoked prior to indicate what is
219     /// being compiled.
220     ///
221     /// This method will block the current thread until compilation has
222     /// finished, and when done the serialized artifact will be returned.
223     ///
224     /// Note that this method will never cache compilations, even if the
225     /// `cache` feature is enabled.
226     ///
227     /// # Errors
228     ///
229     /// This can fail if the input wasm module was not valid or if another
230     /// compilation-related error is encountered.
231     pub fn compile_module_serialized(&self) -> Result<Vec<u8>> {
232         let wasm = self.wasm_binary()?;
233         let dwarf_package = self.dwarf_package_binary();
234         let (v, _) = super::build_artifacts(self.engine, &wasm, dwarf_package.as_deref())?;
235         Ok(v)
236     }
237 
238     /// Same as [`CodeBuilder::compile_module_serialized`] except that it
239     /// compiles a serialized [`Component`](crate::component::Component)
240     /// instead of a module.
241     #[cfg(feature = "component-model")]
242     #[cfg_attr(docsrs, doc(cfg(feature = "component-model")))]
243     pub fn compile_component_serialized(&self) -> Result<Vec<u8>> {
244         let bytes = self.wasm_binary()?;
245         let (v, _) = super::build_component_artifacts(self.engine, &bytes, None)?;
246         Ok(v)
247     }
248 }
249 
250 /// This is a helper struct used when caching to hash the state of an `Engine`
251 /// used for module compilation.
252 ///
253 /// The hash computed for this structure is used to key the global wasmtime
254 /// cache and dictates whether artifacts are reused. Consequently the contents
255 /// of this hash dictate when artifacts are or aren't re-used.
256 pub struct HashedEngineCompileEnv<'a>(pub &'a Engine);
257 
258 impl std::hash::Hash for HashedEngineCompileEnv<'_> {
259     fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
260         // Hash the compiler's state based on its target and configuration.
261         let compiler = self.0.compiler();
262         compiler.triple().hash(hasher);
263         compiler.flags().hash(hasher);
264         compiler.isa_flags().hash(hasher);
265 
266         // Hash configuration state read for compilation
267         let config = self.0.config();
268         self.0.tunables().hash(hasher);
269         config.features.hash(hasher);
270         config.wmemcheck.hash(hasher);
271 
272         // Catch accidental bugs of reusing across crate versions.
273         config.module_version.hash(hasher);
274     }
275 }
276