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