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