2019-02-07 00:26:45 +00:00
|
|
|
use crate::Module;
|
2019-02-22 01:06:49 +00:00
|
|
|
use memmap::Mmap;
|
|
|
|
use std::{
|
|
|
|
fs::{create_dir_all, File},
|
|
|
|
io::{self, Write},
|
|
|
|
path::PathBuf,
|
|
|
|
};
|
2019-02-07 00:26:45 +00:00
|
|
|
|
2019-03-19 00:46:06 +00:00
|
|
|
use wasmer_runtime_core::cache::Error as CacheError;
|
2019-07-09 00:05:54 +00:00
|
|
|
pub use wasmer_runtime_core::{
|
|
|
|
backend::Backend,
|
2019-07-30 01:29:20 +00:00
|
|
|
cache::{Artifact, Cache, WasmHash},
|
2019-07-09 00:05:54 +00:00
|
|
|
};
|
2019-02-07 00:26:45 +00:00
|
|
|
|
2019-02-21 22:00:33 +00:00
|
|
|
/// Representation of a directory that contains compiled wasm artifacts.
|
|
|
|
///
|
|
|
|
/// The `FileSystemCache` type implements the [`Cache`] trait, which allows it to be used
|
|
|
|
/// generically when some sort of cache is required.
|
|
|
|
///
|
|
|
|
/// [`Cache`]: trait.Cache.html
|
|
|
|
///
|
|
|
|
/// # Usage:
|
|
|
|
///
|
|
|
|
/// ```rust
|
2019-02-22 19:42:30 +00:00
|
|
|
/// use wasmer_runtime::cache::{Cache, FileSystemCache, WasmHash};
|
2019-02-21 22:00:33 +00:00
|
|
|
///
|
|
|
|
/// # use wasmer_runtime::{Module, error::CacheError};
|
2019-02-22 19:42:30 +00:00
|
|
|
/// fn store_module(module: Module) -> Result<Module, CacheError> {
|
2019-02-21 22:00:33 +00:00
|
|
|
/// // Create a new file system cache.
|
|
|
|
/// // This is unsafe because we can't ensure that the artifact wasn't
|
|
|
|
/// // corrupted or tampered with.
|
|
|
|
/// let mut fs_cache = unsafe { FileSystemCache::new("some/directory/goes/here")? };
|
2019-02-22 20:00:30 +00:00
|
|
|
/// // Compute a key for a given WebAssembly binary
|
2019-07-09 00:05:54 +00:00
|
|
|
/// let key = WasmHash::generate(&[]);
|
2019-02-22 19:42:30 +00:00
|
|
|
/// // Store a module into the cache given a key
|
2019-02-22 20:06:22 +00:00
|
|
|
/// fs_cache.store(key, module.clone())?;
|
2019-02-22 20:17:49 +00:00
|
|
|
/// Ok(module)
|
2019-02-21 22:00:33 +00:00
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
pub struct FileSystemCache {
|
2019-02-21 19:47:28 +00:00
|
|
|
path: PathBuf,
|
|
|
|
}
|
|
|
|
|
2019-02-21 22:00:33 +00:00
|
|
|
impl FileSystemCache {
|
|
|
|
/// Construct a new `FileSystemCache` around the specified directory.
|
2019-03-19 00:27:23 +00:00
|
|
|
/// The contents of the cache are stored in sub-versioned directories.
|
2019-02-21 22:00:33 +00:00
|
|
|
///
|
|
|
|
/// # Note:
|
|
|
|
/// This method is unsafe because there's no way to ensure the artifacts
|
|
|
|
/// stored in this cache haven't been corrupted or tampered with.
|
|
|
|
pub unsafe fn new<P: Into<PathBuf>>(path: P) -> io::Result<Self> {
|
2019-07-30 00:33:50 +00:00
|
|
|
let path: PathBuf = path.into();
|
2019-02-21 19:47:28 +00:00
|
|
|
if path.exists() {
|
|
|
|
let metadata = path.metadata()?;
|
|
|
|
if metadata.is_dir() {
|
|
|
|
if !metadata.permissions().readonly() {
|
2019-03-19 17:58:58 +00:00
|
|
|
Ok(Self { path })
|
2019-02-21 19:47:28 +00:00
|
|
|
} else {
|
|
|
|
// This directory is readonly.
|
|
|
|
Err(io::Error::new(
|
|
|
|
io::ErrorKind::PermissionDenied,
|
|
|
|
format!("the supplied path is readonly: {}", path.display()),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// This path points to a file.
|
|
|
|
Err(io::Error::new(
|
|
|
|
io::ErrorKind::PermissionDenied,
|
|
|
|
format!(
|
|
|
|
"the supplied path already points to a file: {}",
|
|
|
|
path.display()
|
|
|
|
),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Create the directory and any parent directories if they don't yet exist.
|
|
|
|
create_dir_all(&path)?;
|
2019-03-19 17:58:58 +00:00
|
|
|
Ok(Self { path })
|
2019-02-21 19:47:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 22:00:33 +00:00
|
|
|
impl Cache for FileSystemCache {
|
2019-02-21 19:47:28 +00:00
|
|
|
type LoadError = CacheError;
|
|
|
|
type StoreError = CacheError;
|
|
|
|
|
2019-02-21 22:00:33 +00:00
|
|
|
fn load(&self, key: WasmHash) -> Result<Module, CacheError> {
|
2019-07-09 00:05:54 +00:00
|
|
|
self.load_with_backend(key, Backend::default())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_with_backend(&self, key: WasmHash, backend: Backend) -> Result<Module, CacheError> {
|
2019-02-21 19:47:28 +00:00
|
|
|
let filename = key.encode();
|
|
|
|
let mut new_path_buf = self.path.clone();
|
2019-07-09 00:05:54 +00:00
|
|
|
new_path_buf.push(backend.to_string());
|
2019-02-21 19:47:28 +00:00
|
|
|
new_path_buf.push(filename);
|
2019-02-22 01:06:49 +00:00
|
|
|
let file = File::open(new_path_buf)?;
|
|
|
|
let mmap = unsafe { Mmap::map(&file)? };
|
2019-02-21 19:47:28 +00:00
|
|
|
|
2019-02-22 01:06:49 +00:00
|
|
|
let serialized_cache = Artifact::deserialize(&mmap[..])?;
|
2019-05-14 23:19:58 +00:00
|
|
|
unsafe {
|
2019-07-25 00:31:59 +00:00
|
|
|
wasmer_runtime_core::load_cache_with(
|
|
|
|
serialized_cache,
|
|
|
|
super::compiler_for_backend(backend)
|
|
|
|
.ok_or_else(|| CacheError::UnsupportedBackend(backend))?
|
|
|
|
.as_ref(),
|
|
|
|
)
|
2019-05-14 23:19:58 +00:00
|
|
|
}
|
2019-02-21 19:47:28 +00:00
|
|
|
}
|
|
|
|
|
2019-02-22 19:42:30 +00:00
|
|
|
fn store(&mut self, key: WasmHash, module: Module) -> Result<(), CacheError> {
|
2019-02-21 19:47:28 +00:00
|
|
|
let filename = key.encode();
|
2019-07-09 00:05:54 +00:00
|
|
|
let backend_str = module.info().backend.to_string();
|
2019-02-21 19:47:28 +00:00
|
|
|
let mut new_path_buf = self.path.clone();
|
2019-07-09 00:05:54 +00:00
|
|
|
new_path_buf.push(backend_str);
|
2019-02-21 19:47:28 +00:00
|
|
|
|
|
|
|
let serialized_cache = module.cache()?;
|
2019-02-22 01:06:49 +00:00
|
|
|
let buffer = serialized_cache.serialize()?;
|
|
|
|
|
2019-07-09 00:05:54 +00:00
|
|
|
std::fs::create_dir_all(&new_path_buf)?;
|
|
|
|
new_path_buf.push(filename);
|
2019-02-22 01:06:49 +00:00
|
|
|
let mut file = File::create(new_path_buf)?;
|
2019-02-22 01:11:28 +00:00
|
|
|
file.write_all(&buffer)?;
|
2019-02-18 19:56:20 +00:00
|
|
|
|
2019-02-22 19:42:30 +00:00
|
|
|
Ok(())
|
2019-02-21 19:47:28 +00:00
|
|
|
}
|
2019-02-07 00:26:45 +00:00
|
|
|
}
|
2019-06-01 16:35:26 +00:00
|
|
|
|
|
|
|
#[cfg(all(test, not(feature = "singlepass")))]
|
|
|
|
mod tests {
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
use std::env;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_file_system_cache_run() {
|
|
|
|
use crate::{compile, imports, Func};
|
|
|
|
use wabt::wat2wasm;
|
|
|
|
|
|
|
|
static WAT: &'static str = r#"
|
|
|
|
(module
|
|
|
|
(type $t0 (func (param i32) (result i32)))
|
|
|
|
(func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32)
|
|
|
|
get_local $p0
|
|
|
|
i32.const 1
|
|
|
|
i32.add))
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let wasm = wat2wasm(WAT).unwrap();
|
|
|
|
|
|
|
|
let module = compile(&wasm).unwrap();
|
|
|
|
|
|
|
|
let cache_dir = env::temp_dir();
|
|
|
|
println!("test temp_dir {:?}", cache_dir);
|
|
|
|
|
|
|
|
let mut fs_cache = unsafe {
|
|
|
|
FileSystemCache::new(cache_dir)
|
|
|
|
.map_err(|e| format!("Cache error: {:?}", e))
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
// store module
|
2019-07-08 20:05:25 +00:00
|
|
|
let key = WasmHash::generate(&wasm);
|
2019-06-01 16:35:26 +00:00
|
|
|
fs_cache.store(key, module.clone()).unwrap();
|
|
|
|
|
|
|
|
// load module
|
|
|
|
let cached_module = fs_cache.load(key).unwrap();
|
|
|
|
|
|
|
|
let import_object = imports! {};
|
|
|
|
let instance = cached_module.instantiate(&import_object).unwrap();
|
|
|
|
let add_one: Func<i32, i32> = instance.func("add_one").unwrap();
|
|
|
|
|
|
|
|
let value = add_one.call(42).unwrap();
|
|
|
|
|
|
|
|
// verify it works
|
|
|
|
assert_eq!(value, 43);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|