wasmtime/compile/code_builder.rs
1use crate::prelude::*;
2use crate::Engine;
3use std::borrow::Cow;
4use 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
22/// be provided with either the [`wasm_binary`] or [`wasm_binary_file`] method.
23/// Note that only a single source of bytes can be provided.
24///
25/// # WebAssembly Text Format
26///
27/// This builder supports the WebAssembly Text Format (`*.wat` files) through
28/// the [`CodeBuilder::wasm_binary_or_text`] and
29/// [`CodeBuilder::wasm_binary_or_text_file`] methods. These methods
30/// automatically convert WebAssembly text files to binary. Note though that
31/// this behavior is disabled if the `wat` crate feature is not enabled.
32///
33/// [`compile_module_serialized`]: CodeBuilder::compile_module_serialized
34/// [`compile_module`]: CodeBuilder::compile_module
35/// [`wasm_binary`]: CodeBuilder::wasm_binary
36/// [`wasm_binary_file`]: CodeBuilder::wasm_binary_file
37pub struct CodeBuilder<'a> {
38 pub(super) engine: &'a Engine,
39 wasm: Option<Cow<'a, [u8]>>,
40 wasm_path: Option<Cow<'a, Path>>,
41 dwarf_package: Option<Cow<'a, [u8]>>,
42 dwarf_package_path: Option<Cow<'a, Path>>,
43}
44
45/// Return value of [`CodeBuilder::hint`]
46pub enum CodeHint {
47 /// Hint that the code being compiled is a module.
48 Module,
49 /// Hint that the code being compiled is a component.
50 Component,
51}
52
53impl<'a> CodeBuilder<'a> {
54 /// Creates a new builder which will insert modules into the specified
55 /// [`Engine`].
56 pub fn new(engine: &'a Engine) -> CodeBuilder<'a> {
57 CodeBuilder {
58 engine,
59 wasm: None,
60 wasm_path: None,
61 dwarf_package: None,
62 dwarf_package_path: None,
63 }
64 }
65
66 /// Configures the WebAssembly binary that is being compiled.
67 ///
68 /// The `wasm_bytes` parameter must be a binary WebAssembly file.
69 /// This will be stored within the [`CodeBuilder`] for processing later when
70 /// 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 /// This method will return an error if WebAssembly bytes have already been
80 /// configured.
81 pub fn wasm_binary(
82 &mut self,
83 wasm_bytes: impl Into<Cow<'a, [u8]>>,
84 wasm_path: Option<&'a Path>,
85 ) -> Result<&mut Self> {
86 if self.wasm.is_some() {
87 bail!("cannot configure wasm bytes twice");
88 }
89 self.wasm = Some(wasm_bytes.into());
90 self.wasm_path = wasm_path.map(|p| p.into());
91
92 if self.wasm_path.is_some() {
93 self.dwarf_package_from_wasm_path()?;
94 }
95
96 Ok(self)
97 }
98
99 /// Equivalent of [`CodeBuilder::wasm_binary`] that also accepts the
100 /// WebAssembly text format.
101 ///
102 /// This method will configure the WebAssembly binary to be compiled. The
103 /// input `wasm_bytes` may either be the wasm text format or the binary
104 /// format. If the `wat` crate feature is enabled, which is enabled by
105 /// default, then the text format will automatically be converted to the
106 /// binary format.
107 ///
108 /// # Errors
109 ///
110 /// This method will return an error if WebAssembly bytes have already been
111 /// configured. This method will also return an error if `wasm_bytes` is the
112 /// wasm text format and the text syntax is not valid.
113 pub fn wasm_binary_or_text(
114 &mut self,
115 wasm_bytes: &'a [u8],
116 wasm_path: Option<&'a Path>,
117 ) -> Result<&mut Self> {
118 #[cfg(feature = "wat")]
119 let wasm_bytes = wat::parse_bytes(wasm_bytes).map_err(|mut e| {
120 if let Some(path) = wasm_path {
121 e.set_path(path);
122 }
123 e
124 })?;
125 self.wasm_binary(wasm_bytes, wasm_path)
126 }
127
128 /// Reads the `file` specified for the WebAssembly bytes that are going to
129 /// be compiled.
130 ///
131 /// This method will read `file` from the filesystem and interpret it
132 /// as a WebAssembly binary.
133 ///
134 /// A DWARF package file will be probed using the root of `file` and with a
135 /// `.dwp` extension. If found, it will be loaded and DWARF fusion
136 /// performed.
137 ///
138 /// # Errors
139 ///
140 /// This method will return an error if WebAssembly bytes have already been
141 /// configured.
142 ///
143 /// If `file` can't be read or an error happens reading it then that will
144 /// also be returned.
145 ///
146 /// If DWARF fusion is performed and the DWARF packaged file cannot be read
147 /// then an error will be returned.
148 pub fn wasm_binary_file(&mut self, file: &'a Path) -> Result<&mut Self> {
149 let wasm = std::fs::read(file)
150 .with_context(|| format!("failed to read input file: {}", file.display()))?;
151 self.wasm_binary(wasm, Some(file))
152 }
153
154 /// Equivalent of [`CodeBuilder::wasm_binary_file`] that also accepts the
155 /// WebAssembly text format.
156 ///
157 /// This method is will read the file at `path` and interpret the contents
158 /// to determine if it's the wasm text format or binary format. The file
159 /// extension of `file` is not consulted. The text format is automatically
160 /// converted to the binary format if the crate feature `wat` is active.
161 ///
162 /// # Errors
163 ///
164 /// In addition to the errors returned by [`CodeBuilder::wasm_binary_file`]
165 /// this may also fail if the text format is read and the syntax is invalid.
166 pub fn wasm_binary_or_text_file(&mut self, file: &'a Path) -> Result<&mut Self> {
167 #[cfg(feature = "wat")]
168 {
169 let wasm = wat::parse_file(file)?;
170 self.wasm_binary(wasm, Some(file))
171 }
172 #[cfg(not(feature = "wat"))]
173 {
174 self.wasm_binary_file(file)
175 }
176 }
177
178 pub(super) fn get_wasm(&self) -> Result<&[u8]> {
179 self.wasm
180 .as_deref()
181 .ok_or_else(|| anyhow!("no wasm bytes have been configured"))
182 }
183
184 /// Explicitly specify DWARF `.dwp` path.
185 ///
186 /// # Errors
187 ///
188 /// This method will return an error if the `.dwp` file has already been set
189 /// through [`CodeBuilder::dwarf_package`] or auto-detection in
190 /// [`CodeBuilder::wasm_binary_file`].
191 ///
192 /// This method will also return an error if `file` cannot be read.
193 pub fn dwarf_package_file(&mut self, file: &Path) -> Result<&mut Self> {
194 if self.dwarf_package.is_some() {
195 bail!("cannot call `dwarf_package` or `dwarf_package_file` twice");
196 }
197
198 let dwarf_package = std::fs::read(file)
199 .with_context(|| format!("failed to read dwarf input file: {}", file.display()))?;
200 self.dwarf_package_path = Some(Cow::Owned(file.to_owned()));
201 self.dwarf_package = Some(dwarf_package.into());
202
203 Ok(self)
204 }
205
206 fn dwarf_package_from_wasm_path(&mut self) -> Result<&mut Self> {
207 let dwarf_package_path_buf = self.wasm_path.as_ref().unwrap().with_extension("dwp");
208 if dwarf_package_path_buf.exists() {
209 return self.dwarf_package_file(dwarf_package_path_buf.as_path());
210 }
211
212 Ok(self)
213 }
214
215 /// Gets the DWARF package.
216 pub(super) fn get_dwarf_package(&self) -> Option<&[u8]> {
217 self.dwarf_package.as_deref()
218 }
219
220 /// Set the DWARF package binary.
221 ///
222 /// Initializes `dwarf_package` from `dwp_bytes` in preparation for
223 /// DWARF fusion. Allows the DWARF package to be supplied as a byte array
224 /// when the file probing performed in `wasm_file` is not appropriate.
225 ///
226 /// # Errors
227 ///
228 /// Returns an error if the `*.dwp` file is already set via auto-probing in
229 /// [`CodeBuilder::wasm_binary_file`] or explicitly via
230 /// [`CodeBuilder::dwarf_package_file`].
231 pub fn dwarf_package(&mut self, dwp_bytes: &'a [u8]) -> Result<&mut Self> {
232 if self.dwarf_package.is_some() {
233 bail!("cannot call `dwarf_package` or `dwarf_package_file` twice");
234 }
235 self.dwarf_package = Some(dwp_bytes.into());
236 Ok(self)
237 }
238
239 /// Returns a hint, if possible, of what the provided bytes are.
240 ///
241 /// This method can be use to detect what the previously supplied bytes to
242 /// methods such as [`CodeBuilder::wasm_binary_or_text`] are. This will
243 /// return whether a module or a component was found in the provided bytes.
244 ///
245 /// This method will return `None` if wasm bytes have not been configured
246 /// or if the provided bytes don't look like either a component or a
247 /// module.
248 pub fn hint(&self) -> Option<CodeHint> {
249 let wasm = self.wasm.as_ref()?;
250 if wasmparser::Parser::is_component(wasm) {
251 Some(CodeHint::Component)
252 } else if wasmparser::Parser::is_core_wasm(wasm) {
253 Some(CodeHint::Module)
254 } else {
255 None
256 }
257 }
258
259 /// Finishes this compilation and produces a serialized list of bytes.
260 ///
261 /// This method requires that either [`CodeBuilder::wasm_binary`] or
262 /// related methods were invoked prior to indicate what is being compiled.
263 ///
264 /// This method will block the current thread until compilation has
265 /// finished, and when done the serialized artifact will be returned.
266 ///
267 /// Note that this method will never cache compilations, even if the
268 /// `cache` feature is enabled.
269 ///
270 /// # Errors
271 ///
272 /// This can fail if the input wasm module was not valid or if another
273 /// compilation-related error is encountered.
274 pub fn compile_module_serialized(&self) -> Result<Vec<u8>> {
275 let wasm = self.get_wasm()?;
276 let dwarf_package = self.get_dwarf_package();
277 let (v, _) = super::build_artifacts(self.engine, &wasm, dwarf_package.as_deref(), &())?;
278 Ok(v)
279 }
280
281 /// Same as [`CodeBuilder::compile_module_serialized`] except that it
282 /// compiles a serialized [`Component`](crate::component::Component)
283 /// instead of a module.
284 #[cfg(feature = "component-model")]
285 pub fn compile_component_serialized(&self) -> Result<Vec<u8>> {
286 let bytes = self.get_wasm()?;
287 let (v, _) = super::build_component_artifacts(self.engine, &bytes, None, &())?;
288 Ok(v)
289 }
290}
291
292/// This is a helper struct used when caching to hash the state of an `Engine`
293/// used for module compilation.
294///
295/// The hash computed for this structure is used to key the global wasmtime
296/// cache and dictates whether artifacts are reused. Consequently the contents
297/// of this hash dictate when artifacts are or aren't re-used.
298pub struct HashedEngineCompileEnv<'a>(pub &'a Engine);
299
300impl std::hash::Hash for HashedEngineCompileEnv<'_> {
301 fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
302 // Hash the compiler's state based on its target and configuration.
303 let compiler = self.0.compiler();
304 compiler.triple().hash(hasher);
305 compiler.flags().hash(hasher);
306 compiler.isa_flags().hash(hasher);
307
308 // Hash configuration state read for compilation
309 let config = self.0.config();
310 self.0.tunables().hash(hasher);
311 self.0.features().hash(hasher);
312 config.wmemcheck.hash(hasher);
313
314 // Catch accidental bugs of reusing across crate versions.
315 config.module_version.hash(hasher);
316 }
317}