use crate::Config;
use anyhow::Result;
use std::sync::Arc;
#[cfg(feature = "cache")]
use wasmtime_cache::CacheConfig;
use wasmtime_jit::Compiler;
use wasmtime_runtime::{debug_builtins, InstanceAllocator};
#[derive(Clone)]
pub struct Engine {
inner: Arc<EngineInner>,
}
struct EngineInner {
config: Config,
compiler: Compiler,
allocator: Box<dyn InstanceAllocator>,
}
impl Engine {
pub fn new(config: &Config) -> Result<Engine> {
debug_builtins::ensure_exported();
config.validate()?;
let allocator = config.build_allocator()?;
Ok(Engine {
inner: Arc::new(EngineInner {
config: config.clone(),
compiler: config.build_compiler(allocator.as_ref()),
allocator,
}),
})
}
#[inline]
pub fn config(&self) -> &Config {
&self.inner.config
}
pub(crate) fn compiler(&self) -> &Compiler {
&self.inner.compiler
}
pub(crate) fn allocator(&self) -> &dyn InstanceAllocator {
self.inner.allocator.as_ref()
}
#[cfg(feature = "cache")]
pub(crate) fn cache_config(&self) -> &CacheConfig {
&self.config().cache_config
}
pub fn same(a: &Engine, b: &Engine) -> bool {
Arc::ptr_eq(&a.inner, &b.inner)
}
pub fn precompile_module(&self, bytes: &[u8]) -> Result<Vec<u8>> {
const USE_PAGED_MEM_INIT: bool = cfg!(all(feature = "uffd", target_os = "linux"));
#[cfg(feature = "wat")]
let bytes = wat::parse_bytes(&bytes)?;
let (_, artifacts, types) = wasmtime_jit::CompilationArtifacts::build(
&self.inner.compiler,
&bytes,
USE_PAGED_MEM_INIT,
)?;
crate::module::SerializedModule::from_artifacts(&self.inner.compiler, &artifacts, &types)
.to_bytes()
}
}
impl Default for Engine {
fn default() -> Engine {
Engine::new(&Config::default()).unwrap()
}
}
#[cfg(test)]
mod tests {
use crate::{Config, Engine, Module, OptLevel};
use anyhow::Result;
use tempfile::TempDir;
#[test]
fn cache_accounts_for_opt_level() -> Result<()> {
let td = TempDir::new()?;
let config_path = td.path().join("config.toml");
std::fs::write(
&config_path,
&format!(
"
[cache]
enabled = true
directory = '{}'
",
td.path().join("cache").display()
),
)?;
let mut cfg = Config::new();
cfg.cranelift_opt_level(OptLevel::None)
.cache_config_load(&config_path)?;
let engine = Engine::new(&cfg)?;
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 0);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
let mut cfg = Config::new();
cfg.cranelift_opt_level(OptLevel::Speed)
.cache_config_load(&config_path)?;
let engine = Engine::new(&cfg)?;
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 0);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
let mut cfg = Config::new();
cfg.cranelift_opt_level(OptLevel::SpeedAndSize)
.cache_config_load(&config_path)?;
let engine = Engine::new(&cfg)?;
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 0);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
if !cfg!(target_arch = "aarch64") {
let mut cfg = Config::new();
cfg.debug_info(true).cache_config_load(&config_path)?;
let engine = Engine::new(&cfg)?;
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 0);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
}
Ok(())
}
}