1 #[cfg(all(feature = "runtime", feature = "component-model"))] 2 use crate::component::Component; 3 use crate::Engine; 4 #[cfg(feature = "runtime")] 5 use crate::{instantiate::MmapVecWrapper, CodeMemory, Module}; 6 use anyhow::{anyhow, bail, Context, Result}; 7 use std::borrow::Cow; 8 use std::path::Path; 9 use std::sync::Arc; 10 use wasmtime_environ::ObjectKind; 11 #[cfg(feature = "runtime")] 12 use wasmtime_runtime::MmapVec; 13 14 /// Builder-style structure used to create a [`Module`](crate::Module) or 15 /// pre-compile a module to a serialized list of bytes. 16 /// 17 /// This structure can be used for more advanced configuration when compiling a 18 /// WebAssembly module. Most configuration can use simpler constructors such as: 19 /// 20 /// * [`Module::new`](crate::Module::new) 21 /// * [`Module::from_file`](crate::Module::from_file) 22 /// * [`Module::from_binary`](crate::Module::from_binary) 23 /// 24 /// Note that a [`CodeBuilder`] always involves compiling WebAssembly bytes 25 /// to machine code. To deserialize a list of bytes use 26 /// [`Module::deserialize`](crate::Module::deserialize) instead. 27 /// 28 /// A [`CodeBuilder`] requires a source of WebAssembly bytes to be configured 29 /// before calling [`compile_module_serialized`] or [`compile_module`]. This can be 30 /// provided with either the [`wasm`] or [`wasm_file`] method. Note that only 31 /// a single source of bytes can be provided. 32 /// 33 /// # WebAssembly Text Format 34 /// 35 /// This builder supports the WebAssembly Text Format (`*.wat` files). 36 /// WebAssembly text files are automatically converted to a WebAssembly binary 37 /// and then the binary is compiled. This requires the `wat` feature of the 38 /// `wasmtime` crate to be enabled, and the feature is enabled by default. 39 /// 40 /// If the text format is not desired then the [`CodeBuilder::wat`] method 41 /// can be used to disable this conversion. 42 /// 43 /// [`compile_module_serialized`]: CodeBuilder::compile_module_serialized 44 /// [`compile_module`]: CodeBuilder::compile_module 45 /// [`wasm`]: CodeBuilder::wasm 46 /// [`wasm_file`]: CodeBuilder::wasm_file 47 pub struct CodeBuilder<'a> { 48 engine: &'a Engine, 49 wasm: Option<Cow<'a, [u8]>>, 50 wasm_path: Option<Cow<'a, Path>>, 51 wat: bool, 52 } 53 54 impl<'a> CodeBuilder<'a> { 55 /// Creates a new builder which will insert modules into the specified 56 /// [`Engine`]. 57 pub fn new(engine: &'a Engine) -> CodeBuilder<'a> { 58 CodeBuilder { 59 engine, 60 wasm: None, 61 wasm_path: None, 62 wat: cfg!(feature = "wat"), 63 } 64 } 65 66 /// Configures the WebAssembly binary or text that is being compiled. 67 /// 68 /// The `wasm_bytes` parameter is either a binary WebAssembly file or a 69 /// WebAssembly module in its text format. This will be stored within the 70 /// [`CodeBuilder`] for processing later when 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 /// If wasm bytes have already been configured via a call to this method or 80 /// [`CodeBuilder::wasm_file`] then an error will be returned. 81 pub fn wasm(&mut self, wasm_bytes: &'a [u8], wasm_path: Option<&'a Path>) -> Result<&mut Self> { 82 if self.wasm.is_some() { 83 bail!("cannot call `wasm` or `wasm_file` twice"); 84 } 85 self.wasm = Some(wasm_bytes.into()); 86 self.wasm_path = wasm_path.map(|p| p.into()); 87 Ok(self) 88 } 89 90 /// Configures whether the WebAssembly text format is supported in this 91 /// builder. 92 /// 93 /// This support is enabled by default if the `wat` crate feature is also 94 /// enabled. 95 /// 96 /// # Errors 97 /// 98 /// If this feature is explicitly enabled here via this method and the 99 /// `wat` crate feature is disabled then an error will be returned. 100 pub fn wat(&mut self, enable: bool) -> Result<&mut Self> { 101 if !cfg!(feature = "wat") && enable { 102 bail!("support for `wat` was disabled at compile time"); 103 } 104 self.wat = enable; 105 Ok(self) 106 } 107 108 /// Reads the `file` specified for the WebAssembly bytes that are going to 109 /// be compiled. 110 /// 111 /// This method will read `file` from the filesystem and interpret it 112 /// either as a WebAssembly binary or as a WebAssembly text file. The 113 /// contents are inspected to do this, the file extension is not consulted. 114 /// 115 /// # Errors 116 /// 117 /// If wasm bytes have already been configured via a call to this method or 118 /// [`CodeBuilder::wasm`] then an error will be returned. 119 /// 120 /// If `file` can't be read or an error happens reading it then that will 121 /// also be returned. 122 pub fn wasm_file(&mut self, file: &'a Path) -> Result<&mut Self> { 123 if self.wasm.is_some() { 124 bail!("cannot call `wasm` or `wasm_file` twice"); 125 } 126 let wasm = std::fs::read(file) 127 .with_context(|| format!("failed to read input file: {}", file.display()))?; 128 self.wasm = Some(wasm.into()); 129 self.wasm_path = Some(file.into()); 130 Ok(self) 131 } 132 133 fn wasm_binary(&self) -> Result<Cow<'_, [u8]>> { 134 let wasm = self 135 .wasm 136 .as_ref() 137 .ok_or_else(|| anyhow!("no wasm bytes have been configured"))?; 138 if self.wat { 139 #[cfg(feature = "wat")] 140 return wat::parse_bytes(wasm).map_err(|mut e| { 141 if let Some(path) = &self.wasm_path { 142 e.set_path(path); 143 } 144 e.into() 145 }); 146 } 147 Ok((&wasm[..]).into()) 148 } 149 150 #[cfg(feature = "runtime")] 151 fn compile_cached<T>( 152 &self, 153 build_artifacts: fn(&Engine, &[u8]) -> Result<(MmapVecWrapper, Option<T>)>, 154 ) -> Result<(Arc<CodeMemory>, Option<T>)> { 155 let wasm = self.wasm_binary()?; 156 157 self.engine 158 .check_compatible_with_native_host() 159 .context("compilation settings are not compatible with the native host")?; 160 161 #[cfg(feature = "cache")] 162 { 163 let state = ( 164 HashedEngineCompileEnv(self.engine), 165 &wasm, 166 // Don't hash this as it's just its own "pure" function pointer. 167 NotHashed(build_artifacts), 168 ); 169 let (code, info_and_types) = 170 wasmtime_cache::ModuleCacheEntry::new("wasmtime", self.engine.cache_config()) 171 .get_data_raw( 172 &state, 173 // Cache miss, compute the actual artifacts 174 |(engine, wasm, build_artifacts)| -> Result<_> { 175 let (mmap, info) = (build_artifacts.0)(engine.0, wasm)?; 176 let code = publish_mmap(mmap.0)?; 177 Ok((code, info)) 178 }, 179 // Implementation of how to serialize artifacts 180 |(_engine, _wasm, _), (code, _info_and_types)| Some(code.mmap().to_vec()), 181 // Cache hit, deserialize the provided artifacts 182 |(engine, wasm, _), serialized_bytes| { 183 let kind = if wasmparser::Parser::is_component(&wasm) { 184 ObjectKind::Component 185 } else { 186 ObjectKind::Module 187 }; 188 let code = engine.0.load_code_bytes(&serialized_bytes, kind).ok()?; 189 Some((code, None)) 190 }, 191 )?; 192 return Ok((code, info_and_types)); 193 } 194 195 #[cfg(not(feature = "cache"))] 196 { 197 let (mmap, info_and_types) = build_artifacts(self.engine, &wasm)?; 198 let code = publish_mmap(mmap.0)?; 199 return Ok((code, info_and_types)); 200 } 201 202 struct NotHashed<T>(T); 203 204 impl<T> std::hash::Hash for NotHashed<T> { 205 fn hash<H: std::hash::Hasher>(&self, _hasher: &mut H) {} 206 } 207 } 208 209 /// Finishes this compilation and produces a serialized list of bytes. 210 /// 211 /// This method requires that either [`CodeBuilder::wasm`] or 212 /// [`CodeBuilder::wasm_file`] was invoked prior to indicate what is 213 /// being compiled. 214 /// 215 /// This method will block the current thread until compilation has 216 /// finished, and when done the serialized artifact will be returned. 217 /// 218 /// Note that this method will never cache compilations, even if the 219 /// `cache` feature is enabled. 220 /// 221 /// # Errors 222 /// 223 /// This can fail if the input wasm module was not valid or if another 224 /// compilation-related error is encountered. 225 pub fn compile_module_serialized(&self) -> Result<Vec<u8>> { 226 let wasm = self.wasm_binary()?; 227 let (v, _) = super::build_artifacts(self.engine, &wasm)?; 228 Ok(v) 229 } 230 231 /// Same as [`CodeBuilder::compile_module_serialized`] except that a 232 /// [`Module`](crate::Module) is produced instead. 233 /// 234 /// Note that this method will cache compilations if the `cache` feature is 235 /// enabled and turned on in [`Config`](crate::Config). 236 #[cfg(feature = "runtime")] 237 #[cfg_attr(docsrs, doc(cfg(feature = "runtime")))] 238 pub fn compile_module(&self) -> Result<Module> { 239 let (code, info_and_types) = self.compile_cached(super::build_artifacts)?; 240 Module::from_parts(self.engine, code, info_and_types) 241 } 242 243 /// Same as [`CodeBuilder::compile_module_serialized`] except that it 244 /// compiles a serialized [`Component`] instead of a module. 245 #[cfg(feature = "component-model")] 246 #[cfg_attr(docsrs, doc(cfg(feature = "component-model")))] 247 pub fn compile_component_serialized(&self) -> Result<Vec<u8>> { 248 let bytes = self.wasm_binary()?; 249 let (v, _) = super::build_component_artifacts(self.engine, &bytes)?; 250 Ok(v) 251 } 252 253 /// Same as [`CodeBuilder::compile_module`] except that it compiles a 254 /// [`Component`] instead of a module. 255 #[cfg(all(feature = "runtime", feature = "component-model"))] 256 #[cfg_attr( 257 docsrs, 258 doc(cfg(all(feature = "runtime", feature = "component-model"))) 259 )] 260 pub fn compile_component(&self) -> Result<Component> { 261 let (code, artifacts) = self.compile_cached(super::build_component_artifacts)?; 262 Component::from_parts(self.engine, code, artifacts) 263 } 264 } 265 266 /// This is a helper struct used when caching to hash the state of an `Engine` 267 /// used for module compilation. 268 /// 269 /// The hash computed for this structure is used to key the global wasmtime 270 /// cache and dictates whether artifacts are reused. Consequently the contents 271 /// of this hash dictate when artifacts are or aren't re-used. 272 pub struct HashedEngineCompileEnv<'a>(pub &'a Engine); 273 274 impl std::hash::Hash for HashedEngineCompileEnv<'_> { 275 fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) { 276 // Hash the compiler's state based on its target and configuration. 277 let compiler = self.0.compiler(); 278 compiler.triple().hash(hasher); 279 compiler.flags().hash(hasher); 280 compiler.isa_flags().hash(hasher); 281 282 // Hash configuration state read for compilation 283 let config = self.0.config(); 284 self.0.tunables().hash(hasher); 285 config.features.hash(hasher); 286 config.wmemcheck.hash(hasher); 287 288 // Catch accidental bugs of reusing across crate versions. 289 config.module_version.hash(hasher); 290 } 291 } 292 293 #[cfg(feature = "runtime")] 294 fn publish_mmap(mmap: MmapVec) -> Result<Arc<CodeMemory>> { 295 let mut code = CodeMemory::new(mmap)?; 296 code.publish()?; 297 Ok(Arc::new(code)) 298 } 299