mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-18 03:11:21 +00:00
Merge branch 'master' into feature/dynasm-backend
This commit is contained in:
@ -1,128 +1,108 @@
|
||||
use crate::Module;
|
||||
use std::path::Path;
|
||||
use wasmer_runtime_core::cache::{hash_data, Cache as CoreCache};
|
||||
use memmap::Mmap;
|
||||
use std::{
|
||||
fs::{create_dir_all, File},
|
||||
io::{self, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
pub use wasmer_runtime_core::cache::Error;
|
||||
use wasmer_runtime_core::cache::Error as CacheError;
|
||||
pub use wasmer_runtime_core::cache::{Artifact, Cache, WasmHash};
|
||||
|
||||
/// On-disk storage of compiled WebAssembly.
|
||||
/// Representation of a directory that contains compiled wasm artifacts.
|
||||
///
|
||||
/// A `Cache` can be used to quickly reload already
|
||||
/// compiled WebAssembly from a previous execution
|
||||
/// during which the wasm was explicitly compiled
|
||||
/// as a `Cache`.
|
||||
/// 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
|
||||
/// use wasmer_runtime::cache::{Cache, FileSystemCache, WasmHash};
|
||||
///
|
||||
/// # use wasmer_runtime::{Module, error::CacheError};
|
||||
/// fn store_module(module: Module) -> Result<Module, CacheError> {
|
||||
/// // 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")? };
|
||||
/// // Compute a key for a given WebAssembly binary
|
||||
/// let key = WasmHash::generate(&[]);
|
||||
/// // Store a module into the cache given a key
|
||||
/// fs_cache.store(key, module.clone())?;
|
||||
/// Ok(module)
|
||||
/// }
|
||||
/// ```
|
||||
/// use wasmer_runtime::{compile_cache, Cache};
|
||||
///
|
||||
/// # use wasmer_runtime::error::{CompileResult, CacheError};
|
||||
/// # fn make_cache(wasm: &[u8]) -> CompileResult<()> {
|
||||
/// // Make a cache.
|
||||
/// let cache = compile_cache(wasm)?;
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # fn usage_cache(cache: Cache) -> Result<(), CacheError> {
|
||||
/// // Store the cache in a file.
|
||||
/// cache.store("some_cache_file")?;
|
||||
///
|
||||
/// // Load the cache.
|
||||
/// let cache = Cache::load("some_cache_file")?;
|
||||
/// let module = unsafe { cache.into_module()? };
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Performance Characteristics:
|
||||
///
|
||||
/// Loading caches from files has been optimized for latency.
|
||||
/// There is still more work to do that will reduce
|
||||
/// loading time, especially for very large modules,
|
||||
/// but it will require signifigant internal work.
|
||||
///
|
||||
/// # Drawbacks:
|
||||
///
|
||||
/// Due to internal shortcomings, you cannot convert
|
||||
/// a module into a `Cache`. This means that compiling
|
||||
/// into a `Cache` and then converting into a module
|
||||
/// has more overhead than directly compiling
|
||||
/// into a [`Module`].
|
||||
///
|
||||
/// [`Module`]: struct.Module.html
|
||||
pub struct Cache(pub(crate) CoreCache);
|
||||
pub struct FileSystemCache {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
/// Load a `Cache` from the file specified by `path`.
|
||||
impl FileSystemCache {
|
||||
/// Construct a new `FileSystemCache` around the specified directory.
|
||||
///
|
||||
/// # Usage:
|
||||
///
|
||||
/// ```
|
||||
/// use wasmer_runtime::Cache;
|
||||
/// # use wasmer_runtime::error::CacheError;
|
||||
///
|
||||
/// # fn load_cache() -> Result<(), CacheError> {
|
||||
/// let cache = Cache::load("some_file.cache")?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
|
||||
CoreCache::open(path).map(|core_cache| Cache(core_cache))
|
||||
}
|
||||
/// # 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> {
|
||||
let path: PathBuf = path.into();
|
||||
|
||||
/// Convert a `Cache` into a [`Module`].
|
||||
///
|
||||
/// [`Module`]: struct.Module.html
|
||||
///
|
||||
/// # Usage:
|
||||
///
|
||||
/// ```
|
||||
/// use wasmer_runtime::Cache;
|
||||
///
|
||||
/// # use wasmer_runtime::error::CacheError;
|
||||
/// # fn cache2module(cache: Cache) -> Result<(), CacheError> {
|
||||
/// let module = unsafe { cache.into_module()? };
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Notes:
|
||||
///
|
||||
/// This method is unsafe because the runtime cannot confirm
|
||||
/// that this cache was not tampered with or corrupted.
|
||||
pub unsafe fn into_module(self) -> Result<Module, Error> {
|
||||
let default_compiler = super::default_compiler();
|
||||
|
||||
wasmer_runtime_core::load_cache_with(self.0, default_compiler)
|
||||
}
|
||||
|
||||
/// Compare the Sha256 hash of the wasm this cache was build
|
||||
/// from with some other WebAssembly.
|
||||
///
|
||||
/// The main use-case for this is invalidating old caches.
|
||||
pub fn compare_wasm(&self, wasm: &[u8]) -> bool {
|
||||
let param_wasm_hash = hash_data(wasm);
|
||||
self.0.wasm_hash() as &[u8] == ¶m_wasm_hash as &[u8]
|
||||
}
|
||||
|
||||
/// Store this cache in a file.
|
||||
///
|
||||
/// # Notes:
|
||||
///
|
||||
/// If a file exists at the specified path, it will be overwritten.
|
||||
///
|
||||
/// # Usage:
|
||||
///
|
||||
/// ```
|
||||
/// use wasmer_runtime::Cache;
|
||||
///
|
||||
/// # use wasmer_runtime::error::CacheError;
|
||||
/// # fn store_cache(cache: Cache) -> Result<(), CacheError> {
|
||||
/// cache.store("some_file.cache")?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn store<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
|
||||
self.0.store(path)
|
||||
if path.exists() {
|
||||
let metadata = path.metadata()?;
|
||||
if metadata.is_dir() {
|
||||
if !metadata.permissions().readonly() {
|
||||
Ok(Self { path })
|
||||
} 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)?;
|
||||
Ok(Self { path })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cache for FileSystemCache {
|
||||
type LoadError = CacheError;
|
||||
type StoreError = CacheError;
|
||||
|
||||
fn load(&self, key: WasmHash) -> Result<Module, CacheError> {
|
||||
let filename = key.encode();
|
||||
let mut new_path_buf = self.path.clone();
|
||||
new_path_buf.push(filename);
|
||||
let file = File::open(new_path_buf)?;
|
||||
let mmap = unsafe { Mmap::map(&file)? };
|
||||
|
||||
let serialized_cache = Artifact::deserialize(&mmap[..])?;
|
||||
unsafe { wasmer_runtime_core::load_cache_with(serialized_cache, super::default_compiler()) }
|
||||
}
|
||||
|
||||
fn store(&mut self, key: WasmHash, module: Module) -> Result<(), CacheError> {
|
||||
let filename = key.encode();
|
||||
let mut new_path_buf = self.path.clone();
|
||||
new_path_buf.push(filename);
|
||||
|
||||
let serialized_cache = module.cache()?;
|
||||
let buffer = serialized_cache.serialize()?;
|
||||
|
||||
let mut file = File::create(new_path_buf)?;
|
||||
file.write_all(&buffer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -99,8 +99,7 @@ pub mod wasm {
|
||||
}
|
||||
|
||||
pub mod error {
|
||||
#[cfg(feature = "cache")]
|
||||
pub use super::cache::Error as CacheError;
|
||||
pub use wasmer_runtime_core::cache::Error as CacheError;
|
||||
pub use wasmer_runtime_core::error::*;
|
||||
}
|
||||
|
||||
@ -109,15 +108,10 @@ pub mod units {
|
||||
pub use wasmer_runtime_core::units::{Bytes, Pages};
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
mod cache;
|
||||
pub mod cache;
|
||||
|
||||
#[cfg(feature = "default-compiler")]
|
||||
use wasmer_runtime_core::backend::Compiler;
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
pub use self::cache::Cache;
|
||||
|
||||
/// Compile WebAssembly binary code into a [`Module`].
|
||||
/// This function is useful if it is necessary to
|
||||
/// compile a module before it can be instantiated
|
||||
@ -131,7 +125,6 @@ pub use self::cache::Cache;
|
||||
/// binary code of the wasm module you want to compile.
|
||||
/// # Errors:
|
||||
/// If the operation fails, the function returns `Err(error::CompileError::...)`.
|
||||
#[cfg(feature = "default-compiler")]
|
||||
pub fn compile(wasm: &[u8]) -> error::CompileResult<Module> {
|
||||
wasmer_runtime_core::compile_with(&wasm[..], default_compiler())
|
||||
}
|
||||
@ -154,44 +147,23 @@ pub fn compile(wasm: &[u8]) -> error::CompileResult<Module> {
|
||||
/// `error::CompileError`, `error::LinkError`, or
|
||||
/// `error::RuntimeError` (all combined into an `error::Error`),
|
||||
/// depending on the cause of the failure.
|
||||
#[cfg(feature = "default-compiler")]
|
||||
pub fn instantiate(wasm: &[u8], import_object: &ImportObject) -> error::Result<Instance> {
|
||||
let module = compile(wasm)?;
|
||||
module.instantiate(import_object)
|
||||
}
|
||||
|
||||
/// Compile wasm into a [`Cache`] that can be stored to a file or
|
||||
/// converted into [`Module`].
|
||||
///
|
||||
/// [`Cache`]: struct.Cache.html
|
||||
/// [`Module`]: struct.Module.html
|
||||
///
|
||||
/// # Usage:
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer_runtime::error::CompileResult;
|
||||
/// use wasmer_runtime::compile_cache;
|
||||
///
|
||||
/// # fn make_cache(wasm: &[u8]) -> CompileResult<()> {
|
||||
/// let cache = compile_cache(wasm)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "cache")]
|
||||
pub fn compile_cache(wasm: &[u8]) -> error::CompileResult<Cache> {
|
||||
let default_compiler = default_compiler();
|
||||
|
||||
wasmer_runtime_core::compile_to_cache_with(wasm, default_compiler)
|
||||
.map(|core_cache| Cache(core_cache))
|
||||
}
|
||||
|
||||
#[cfg(feature = "default-compiler")]
|
||||
fn default_compiler() -> &'static dyn Compiler {
|
||||
use lazy_static::lazy_static;
|
||||
use wasmer_dynasm_backend::SinglePassCompiler;
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
use wasmer_llvm_backend::LLVMCompiler as DefaultCompiler;
|
||||
|
||||
#[cfg(not(feature = "llvm"))]
|
||||
use wasmer_dynasm_backend::SinglePassCompiler as DefaultCompiler;
|
||||
// use wasmer_clif_backend::CraneliftCompiler as DefaultCompiler; // TODO Fix default
|
||||
|
||||
lazy_static! {
|
||||
static ref DEFAULT_COMPILER: SinglePassCompiler = { SinglePassCompiler {} };
|
||||
static ref DEFAULT_COMPILER: DefaultCompiler = { DefaultCompiler::new() };
|
||||
}
|
||||
|
||||
&*DEFAULT_COMPILER as &dyn Compiler
|
||||
|
Reference in New Issue
Block a user