mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-23 21:51:32 +00:00
Add caching. (#134)
* Allow a module to have a different signature registry than the process-specific * Add core ability to build compiled code caches * Remove timing printouts * Serialize/Deserialize memories to reduce copies * Work more on api * Relocate local functions relatively before external functions * Fix incorrect definition in test * merge errors caused by merge * Fix emscripten compile * Fix review comments
This commit is contained in:
@ -6,6 +6,12 @@ use crate::{
|
||||
types::{FuncIndex, LocalFuncIndex, Value},
|
||||
vm,
|
||||
};
|
||||
#[cfg(feature = "cache")]
|
||||
use crate::{
|
||||
cache::{Cache, Error as CacheError},
|
||||
module::ModuleInfo,
|
||||
sys::Memory,
|
||||
};
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub mod sys {
|
||||
@ -13,6 +19,12 @@ pub mod sys {
|
||||
}
|
||||
pub use crate::sig_registry::SigRegistry;
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Backend {
|
||||
Cranelift,
|
||||
}
|
||||
|
||||
/// This type cannot be constructed from
|
||||
/// outside the runtime crate.
|
||||
pub struct Token {
|
||||
@ -30,6 +42,16 @@ pub trait Compiler {
|
||||
/// The `CompileToken` parameter ensures that this can only
|
||||
/// be called from inside the runtime.
|
||||
fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner>;
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result<ModuleInner, CacheError>;
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
fn compile_to_backend_cache_data(
|
||||
&self,
|
||||
wasm: &[u8],
|
||||
_: Token,
|
||||
) -> CompileResult<(Box<ModuleInfo>, Vec<u8>, Memory)>;
|
||||
}
|
||||
|
||||
/// The functionality exposed by this trait is expected to be used
|
||||
|
@ -5,6 +5,7 @@ use crate::{
|
||||
import::ImportObject,
|
||||
memory::Memory,
|
||||
module::{ImportName, ModuleInner},
|
||||
sig_registry::SigRegistry,
|
||||
structures::{BoxedMap, Map, SliceMap, TypedIndex},
|
||||
table::Table,
|
||||
types::{
|
||||
@ -13,7 +14,7 @@ use crate::{
|
||||
},
|
||||
vm,
|
||||
};
|
||||
use std::slice;
|
||||
use std::{slice, sync::Arc};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LocalBacking {
|
||||
@ -58,9 +59,8 @@ impl LocalBacking {
|
||||
}
|
||||
|
||||
fn generate_memories(module: &ModuleInner) -> BoxedMap<LocalMemoryIndex, Memory> {
|
||||
let mut memories = Map::with_capacity(module.memories.len());
|
||||
|
||||
for (_, &desc) in &module.memories {
|
||||
let mut memories = Map::with_capacity(module.info.memories.len());
|
||||
for (_, &desc) in &module.info.memories {
|
||||
memories.push(Memory::new(desc).expect("unable to create memory"));
|
||||
}
|
||||
|
||||
@ -74,6 +74,7 @@ impl LocalBacking {
|
||||
) -> BoxedMap<LocalMemoryIndex, *mut vm::LocalMemory> {
|
||||
// For each init that has some data...
|
||||
for init in module
|
||||
.info
|
||||
.data_initializers
|
||||
.iter()
|
||||
.filter(|init| init.data.len() > 0)
|
||||
@ -92,7 +93,7 @@ impl LocalBacking {
|
||||
|
||||
match init.memory_index.local_or_import(module) {
|
||||
LocalOrImport::Local(local_memory_index) => {
|
||||
let memory_desc = module.memories[local_memory_index];
|
||||
let memory_desc = module.info.memories[local_memory_index];
|
||||
let data_top = init_base + init.data.len();
|
||||
assert!(memory_desc.minimum.bytes().0 >= data_top);
|
||||
|
||||
@ -128,9 +129,9 @@ impl LocalBacking {
|
||||
}
|
||||
|
||||
fn generate_tables(module: &ModuleInner) -> BoxedMap<LocalTableIndex, Table> {
|
||||
let mut tables = Map::with_capacity(module.tables.len());
|
||||
let mut tables = Map::with_capacity(module.info.tables.len());
|
||||
|
||||
for (_, &table_desc) in module.tables.iter() {
|
||||
for (_, &table_desc) in module.info.tables.iter() {
|
||||
let table = Table::new(table_desc).unwrap();
|
||||
tables.push(table);
|
||||
}
|
||||
@ -145,7 +146,7 @@ impl LocalBacking {
|
||||
tables: &mut SliceMap<LocalTableIndex, Table>,
|
||||
vmctx: *mut vm::Ctx,
|
||||
) -> BoxedMap<LocalTableIndex, *mut vm::LocalTable> {
|
||||
for init in &module.elem_initializers {
|
||||
for init in &module.info.elem_initializers {
|
||||
let init_base = match init.base {
|
||||
Initializer::Const(Value::I32(offset)) => offset as u32,
|
||||
Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
|
||||
@ -170,8 +171,11 @@ impl LocalBacking {
|
||||
|
||||
table.anyfunc_direct_access_mut(|elements| {
|
||||
for (i, &func_index) in init.elements.iter().enumerate() {
|
||||
let sig_index = module.func_assoc[func_index];
|
||||
let sig_id = vm::SigId(sig_index.index() as u32);
|
||||
let sig_index = module.info.func_assoc[func_index];
|
||||
let signature = &module.info.signatures[sig_index];
|
||||
let sig_id = vm::SigId(
|
||||
SigRegistry.lookup_sig_index(Arc::clone(&signature)).index() as u32,
|
||||
);
|
||||
|
||||
let (func, ctx) = match func_index.local_or_import(module) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
@ -205,8 +209,11 @@ impl LocalBacking {
|
||||
|
||||
table.anyfunc_direct_access_mut(|elements| {
|
||||
for (i, &func_index) in init.elements.iter().enumerate() {
|
||||
let sig_index = module.func_assoc[func_index];
|
||||
let sig_id = vm::SigId(sig_index.index() as u32);
|
||||
let sig_index = module.info.func_assoc[func_index];
|
||||
let signature = &module.info.signatures[sig_index];
|
||||
let sig_id = vm::SigId(
|
||||
SigRegistry.lookup_sig_index(Arc::clone(&signature)).index() as u32,
|
||||
);
|
||||
|
||||
let (func, ctx) = match func_index.local_or_import(module) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
@ -243,9 +250,9 @@ impl LocalBacking {
|
||||
module: &ModuleInner,
|
||||
imports: &ImportBacking,
|
||||
) -> BoxedMap<LocalGlobalIndex, Global> {
|
||||
let mut globals = Map::with_capacity(module.globals.len());
|
||||
let mut globals = Map::with_capacity(module.info.globals.len());
|
||||
|
||||
for (_, global_init) in module.globals.iter() {
|
||||
for (_, global_init) in module.info.globals.iter() {
|
||||
let value = match &global_init.init {
|
||||
Initializer::Const(value) => value.clone(),
|
||||
Initializer::GetGlobal(import_global_index) => {
|
||||
@ -348,10 +355,21 @@ fn import_functions(
|
||||
vmctx: *mut vm::Ctx,
|
||||
) -> LinkResult<BoxedMap<ImportedFuncIndex, vm::ImportedFunc>> {
|
||||
let mut link_errors = vec![];
|
||||
let mut functions = Map::with_capacity(module.imported_functions.len());
|
||||
for (index, ImportName { namespace, name }) in &module.imported_functions {
|
||||
let sig_index = module.func_assoc[index.convert_up(module)];
|
||||
let expected_sig = module.sig_registry.lookup_signature(sig_index);
|
||||
let mut functions = Map::with_capacity(module.info.imported_functions.len());
|
||||
for (
|
||||
index,
|
||||
ImportName {
|
||||
namespace_index,
|
||||
name_index,
|
||||
},
|
||||
) in &module.info.imported_functions
|
||||
{
|
||||
let sig_index = module.info.func_assoc[index.convert_up(module)];
|
||||
let expected_sig = &module.info.signatures[sig_index];
|
||||
|
||||
let namespace = module.info.namespace_table.get(*namespace_index);
|
||||
let name = module.info.name_table.get(*name_index);
|
||||
|
||||
let import = imports
|
||||
.get_namespace(namespace)
|
||||
.and_then(|namespace| namespace.get_export(name));
|
||||
@ -361,7 +379,7 @@ fn import_functions(
|
||||
ctx,
|
||||
signature,
|
||||
}) => {
|
||||
if expected_sig == signature {
|
||||
if *expected_sig == signature {
|
||||
functions.push(vm::ImportedFunc {
|
||||
func: func.inner(),
|
||||
vmctx: match ctx {
|
||||
@ -371,8 +389,8 @@ fn import_functions(
|
||||
});
|
||||
} else {
|
||||
link_errors.push(LinkError::IncorrectImportSignature {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
expected: expected_sig.clone(),
|
||||
found: signature.clone(),
|
||||
});
|
||||
@ -387,16 +405,16 @@ fn import_functions(
|
||||
}
|
||||
.to_string();
|
||||
link_errors.push(LinkError::IncorrectImportType {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
expected: "function".to_string(),
|
||||
found: export_type_name,
|
||||
});
|
||||
}
|
||||
None => {
|
||||
link_errors.push(LinkError::ImportNotFound {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -417,11 +435,22 @@ fn import_memories(
|
||||
BoxedMap<ImportedMemoryIndex, *mut vm::LocalMemory>,
|
||||
)> {
|
||||
let mut link_errors = vec![];
|
||||
let mut memories = Map::with_capacity(module.imported_memories.len());
|
||||
let mut vm_memories = Map::with_capacity(module.imported_memories.len());
|
||||
for (_index, (ImportName { namespace, name }, expected_memory_desc)) in
|
||||
&module.imported_memories
|
||||
let mut memories = Map::with_capacity(module.info.imported_memories.len());
|
||||
let mut vm_memories = Map::with_capacity(module.info.imported_memories.len());
|
||||
for (
|
||||
_index,
|
||||
(
|
||||
ImportName {
|
||||
namespace_index,
|
||||
name_index,
|
||||
},
|
||||
expected_memory_desc,
|
||||
),
|
||||
) in &module.info.imported_memories
|
||||
{
|
||||
let namespace = module.info.namespace_table.get(*namespace_index);
|
||||
let name = module.info.name_table.get(*name_index);
|
||||
|
||||
let memory_import = imports
|
||||
.get_namespace(&namespace)
|
||||
.and_then(|namespace| namespace.get_export(&name));
|
||||
@ -432,8 +461,8 @@ fn import_memories(
|
||||
vm_memories.push(memory.vm_local_memory());
|
||||
} else {
|
||||
link_errors.push(LinkError::IncorrectMemoryDescriptor {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
expected: *expected_memory_desc,
|
||||
found: memory.descriptor(),
|
||||
});
|
||||
@ -448,16 +477,16 @@ fn import_memories(
|
||||
}
|
||||
.to_string();
|
||||
link_errors.push(LinkError::IncorrectImportType {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
expected: "memory".to_string(),
|
||||
found: export_type_name,
|
||||
});
|
||||
}
|
||||
None => {
|
||||
link_errors.push(LinkError::ImportNotFound {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -478,9 +507,22 @@ fn import_tables(
|
||||
BoxedMap<ImportedTableIndex, *mut vm::LocalTable>,
|
||||
)> {
|
||||
let mut link_errors = vec![];
|
||||
let mut tables = Map::with_capacity(module.imported_tables.len());
|
||||
let mut vm_tables = Map::with_capacity(module.imported_tables.len());
|
||||
for (_index, (ImportName { namespace, name }, expected_table_desc)) in &module.imported_tables {
|
||||
let mut tables = Map::with_capacity(module.info.imported_tables.len());
|
||||
let mut vm_tables = Map::with_capacity(module.info.imported_tables.len());
|
||||
for (
|
||||
_index,
|
||||
(
|
||||
ImportName {
|
||||
namespace_index,
|
||||
name_index,
|
||||
},
|
||||
expected_table_desc,
|
||||
),
|
||||
) in &module.info.imported_tables
|
||||
{
|
||||
let namespace = module.info.namespace_table.get(*namespace_index);
|
||||
let name = module.info.name_table.get(*name_index);
|
||||
|
||||
let table_import = imports
|
||||
.get_namespace(&namespace)
|
||||
.and_then(|namespace| namespace.get_export(&name));
|
||||
@ -491,8 +533,8 @@ fn import_tables(
|
||||
tables.push(table);
|
||||
} else {
|
||||
link_errors.push(LinkError::IncorrectTableDescriptor {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
expected: *expected_table_desc,
|
||||
found: table.descriptor(),
|
||||
});
|
||||
@ -507,16 +549,16 @@ fn import_tables(
|
||||
}
|
||||
.to_string();
|
||||
link_errors.push(LinkError::IncorrectImportType {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
expected: "table".to_string(),
|
||||
found: export_type_name,
|
||||
});
|
||||
}
|
||||
None => {
|
||||
link_errors.push(LinkError::ImportNotFound {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -537,9 +579,21 @@ fn import_globals(
|
||||
BoxedMap<ImportedGlobalIndex, *mut vm::LocalGlobal>,
|
||||
)> {
|
||||
let mut link_errors = vec![];
|
||||
let mut globals = Map::with_capacity(module.imported_globals.len());
|
||||
let mut vm_globals = Map::with_capacity(module.imported_globals.len());
|
||||
for (_, (ImportName { namespace, name }, imported_global_desc)) in &module.imported_globals {
|
||||
let mut globals = Map::with_capacity(module.info.imported_globals.len());
|
||||
let mut vm_globals = Map::with_capacity(module.info.imported_globals.len());
|
||||
for (
|
||||
_,
|
||||
(
|
||||
ImportName {
|
||||
namespace_index,
|
||||
name_index,
|
||||
},
|
||||
imported_global_desc,
|
||||
),
|
||||
) in &module.info.imported_globals
|
||||
{
|
||||
let namespace = module.info.namespace_table.get(*namespace_index);
|
||||
let name = module.info.name_table.get(*name_index);
|
||||
let import = imports
|
||||
.get_namespace(namespace)
|
||||
.and_then(|namespace| namespace.get_export(name));
|
||||
@ -550,8 +604,8 @@ fn import_globals(
|
||||
globals.push(global);
|
||||
} else {
|
||||
link_errors.push(LinkError::IncorrectGlobalDescriptor {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
expected: *imported_global_desc,
|
||||
found: global.descriptor(),
|
||||
});
|
||||
@ -566,16 +620,16 @@ fn import_globals(
|
||||
}
|
||||
.to_string();
|
||||
link_errors.push(LinkError::IncorrectImportType {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
expected: "global".to_string(),
|
||||
found: export_type_name,
|
||||
});
|
||||
}
|
||||
None => {
|
||||
link_errors.push(LinkError::ImportNotFound {
|
||||
namespace: namespace.clone(),
|
||||
name: name.clone(),
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
181
lib/runtime-core/src/cache.rs
Normal file
181
lib/runtime-core/src/cache.rs
Normal file
@ -0,0 +1,181 @@
|
||||
use crate::{module::ModuleInfo, sys::Memory};
|
||||
use memmap::Mmap;
|
||||
use serde_bench::{deserialize, serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, Seek, SeekFrom, Write},
|
||||
mem,
|
||||
path::Path,
|
||||
slice,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InvalidFileType {
|
||||
InvalidSize,
|
||||
InvalidMagic,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
IoError(io::Error),
|
||||
DeserializeError(String),
|
||||
SerializeError(String),
|
||||
Unknown(String),
|
||||
InvalidFile(InvalidFileType),
|
||||
InvalidatedCache,
|
||||
}
|
||||
|
||||
const CURRENT_CACHE_VERSION: u64 = 0;
|
||||
|
||||
/// The header of a cache file.
|
||||
#[repr(C, packed)]
|
||||
struct CacheHeader {
|
||||
magic: [u8; 8], // [W, A, S, M, E, R, \0, \0]
|
||||
version: u64,
|
||||
data_len: u64,
|
||||
wasm_hash: [u8; 32], // Sha256 of the wasm in binary format.
|
||||
}
|
||||
|
||||
impl CacheHeader {
|
||||
pub fn read_from_slice(buffer: &[u8]) -> Result<(&CacheHeader, &[u8]), Error> {
|
||||
if buffer.len() >= mem::size_of::<CacheHeader>() {
|
||||
if &buffer[..8] == "WASMER\0\0".as_bytes() {
|
||||
let (header_slice, body_slice) = buffer.split_at(mem::size_of::<CacheHeader>());
|
||||
let header = unsafe { &*(header_slice.as_ptr() as *const CacheHeader) };
|
||||
|
||||
if header.version == CURRENT_CACHE_VERSION {
|
||||
Ok((header, body_slice))
|
||||
} else {
|
||||
Err(Error::InvalidatedCache)
|
||||
}
|
||||
} else {
|
||||
Err(Error::InvalidFile(InvalidFileType::InvalidMagic))
|
||||
}
|
||||
} else {
|
||||
Err(Error::InvalidFile(InvalidFileType::InvalidSize))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
let ptr = self as *const CacheHeader as *const u8;
|
||||
unsafe { slice::from_raw_parts(ptr, mem::size_of::<CacheHeader>()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct CacheInner {
|
||||
info: Box<ModuleInfo>,
|
||||
#[serde(with = "serde_bytes")]
|
||||
backend_metadata: Vec<u8>,
|
||||
compiled_code: Memory,
|
||||
}
|
||||
|
||||
pub struct Cache {
|
||||
inner: CacheInner,
|
||||
wasm_hash: Box<[u8; 32]>,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
pub(crate) fn new(
|
||||
wasm: &[u8],
|
||||
info: Box<ModuleInfo>,
|
||||
backend_metadata: Vec<u8>,
|
||||
compiled_code: Memory,
|
||||
) -> Self {
|
||||
let wasm_hash = hash_data(wasm);
|
||||
|
||||
Self {
|
||||
inner: CacheInner {
|
||||
info,
|
||||
backend_metadata,
|
||||
compiled_code,
|
||||
},
|
||||
wasm_hash: Box::new(wasm_hash),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open<P>(path: P) -> Result<Cache, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let file = File::open(path).map_err(|e| Error::IoError(e))?;
|
||||
|
||||
let mmap = unsafe { Mmap::map(&file).map_err(|e| Error::IoError(e))? };
|
||||
|
||||
let (header, body_slice) = CacheHeader::read_from_slice(&mmap[..])?;
|
||||
|
||||
let inner =
|
||||
deserialize(body_slice).map_err(|e| Error::DeserializeError(format!("{:#?}", e)))?;
|
||||
|
||||
Ok(Cache {
|
||||
inner,
|
||||
wasm_hash: Box::new(header.wasm_hash),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn info(&self) -> &ModuleInfo {
|
||||
&self.inner.info
|
||||
}
|
||||
|
||||
pub fn wasm_hash(&self) -> &[u8; 32] {
|
||||
&self.wasm_hash
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn consume(self) -> (ModuleInfo, Vec<u8>, Memory) {
|
||||
(
|
||||
*self.inner.info,
|
||||
self.inner.backend_metadata,
|
||||
self.inner.compiled_code,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn store<P>(&self, path: P) -> Result<(), Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut file = File::create(path).map_err(|e| Error::IoError(e))?;
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
|
||||
serialize(&mut buffer, &self.inner).map_err(|e| Error::SerializeError(e.to_string()))?;
|
||||
|
||||
let data_len = buffer.len() as u64;
|
||||
|
||||
file.seek(SeekFrom::Start(mem::size_of::<CacheHeader>() as u64))
|
||||
.map_err(|e| Error::IoError(e))?;
|
||||
|
||||
file.write(buffer.as_slice())
|
||||
.map_err(|e| Error::IoError(e))?;
|
||||
|
||||
file.seek(SeekFrom::Start(0))
|
||||
.map_err(|e| Error::Unknown(e.to_string()))?;
|
||||
|
||||
let wasm_hash = {
|
||||
let mut array = [0u8; 32];
|
||||
array.copy_from_slice(&*self.wasm_hash);
|
||||
array
|
||||
};
|
||||
|
||||
let cache_header = CacheHeader {
|
||||
magic: [
|
||||
'W' as u8, 'A' as u8, 'S' as u8, 'M' as u8, 'E' as u8, 'R' as u8, 0, 0,
|
||||
],
|
||||
version: CURRENT_CACHE_VERSION,
|
||||
data_len,
|
||||
wasm_hash,
|
||||
};
|
||||
|
||||
file.write(cache_header.as_slice())
|
||||
.map_err(|e| Error::IoError(e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash_data(data: &[u8]) -> [u8; 32] {
|
||||
let mut array = [0u8; 32];
|
||||
array.copy_from_slice(Sha256::digest(data).as_slice());
|
||||
array
|
||||
}
|
@ -49,7 +49,7 @@ impl<'a> ExportIter<'a> {
|
||||
pub(crate) fn new(module: &'a ModuleInner, inner: &'a mut InstanceInner) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
iter: module.exports.iter(),
|
||||
iter: module.info.exports.iter(),
|
||||
module,
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use crate::{
|
||||
import::{ImportObject, LikeNamespace},
|
||||
memory::Memory,
|
||||
module::{ExportIndex, Module, ModuleInner},
|
||||
sig_registry::SigRegistry,
|
||||
table::Table,
|
||||
typed_func::{Func, Safe, WasmTypeList},
|
||||
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value},
|
||||
@ -65,7 +66,7 @@ impl Instance {
|
||||
|
||||
let instance = Instance { module, inner };
|
||||
|
||||
if let Some(start_index) = instance.module.start_func {
|
||||
if let Some(start_index) = instance.module.info.start_func {
|
||||
instance.call_with_index(start_index, &[])?;
|
||||
}
|
||||
|
||||
@ -98,6 +99,7 @@ impl Instance {
|
||||
{
|
||||
let export_index =
|
||||
self.module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
@ -107,10 +109,11 @@ impl Instance {
|
||||
if let ExportIndex::Func(func_index) = export_index {
|
||||
let sig_index = *self
|
||||
.module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(*func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
let signature = self.module.sig_registry.lookup_signature(sig_index);
|
||||
let signature = SigRegistry.lookup_signature(sig_index);
|
||||
|
||||
if signature.params() != Args::types() || signature.returns() != Rets::types() {
|
||||
Err(ResolveError::Signature {
|
||||
@ -167,6 +170,7 @@ impl Instance {
|
||||
pub fn dyn_func(&self, name: &str) -> ResolveResult<DynFunc> {
|
||||
let export_index =
|
||||
self.module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
@ -176,10 +180,11 @@ impl Instance {
|
||||
if let ExportIndex::Func(func_index) = export_index {
|
||||
let sig_index = *self
|
||||
.module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(*func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
let signature = self.module.sig_registry.lookup_signature(sig_index);
|
||||
let signature = Arc::clone(&self.module.info.signatures[sig_index]);
|
||||
|
||||
Ok(DynFunc {
|
||||
signature,
|
||||
@ -220,6 +225,7 @@ impl Instance {
|
||||
pub fn call(&self, name: &str, args: &[Value]) -> CallResult<Vec<Value>> {
|
||||
let export_index =
|
||||
self.module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
@ -270,10 +276,11 @@ impl Instance {
|
||||
fn call_with_index(&self, func_index: FuncIndex, args: &[Value]) -> CallResult<Vec<Value>> {
|
||||
let sig_index = *self
|
||||
.module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
let signature = self.module.sig_registry.lookup_signature(sig_index);
|
||||
let signature = &self.module.info.signatures[sig_index];
|
||||
|
||||
if !signature.check_param_value_types(args) {
|
||||
Err(ResolveError::Signature {
|
||||
@ -344,6 +351,7 @@ impl InstanceInner {
|
||||
func_index: FuncIndex,
|
||||
) -> (FuncPointer, Context, Arc<FuncSig>) {
|
||||
let sig_index = *module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
@ -367,9 +375,13 @@ impl InstanceInner {
|
||||
}
|
||||
};
|
||||
|
||||
let signature = module.sig_registry.lookup_signature(sig_index);
|
||||
let signature = &module.info.signatures[sig_index];
|
||||
|
||||
(unsafe { FuncPointer::new(func_ptr) }, ctx, signature)
|
||||
(
|
||||
unsafe { FuncPointer::new(func_ptr) },
|
||||
ctx,
|
||||
Arc::clone(signature),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_memory_from_index(&self, module: &ModuleInner, mem_index: MemoryIndex) -> Memory {
|
||||
@ -406,7 +418,7 @@ impl InstanceInner {
|
||||
|
||||
impl LikeNamespace for Instance {
|
||||
fn get_export(&self, name: &str) -> Option<Export> {
|
||||
let export_index = self.module.exports.get(name)?;
|
||||
let export_index = self.module.info.exports.get(name)?;
|
||||
|
||||
Some(self.inner.get_export_from_index(&self.module, export_index))
|
||||
}
|
||||
|
@ -2,11 +2,17 @@
|
||||
#[macro_use]
|
||||
extern crate field_offset;
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
#[doc(hidden)]
|
||||
pub mod backend;
|
||||
mod backing;
|
||||
#[cfg(feature = "cache")]
|
||||
pub mod cache;
|
||||
pub mod error;
|
||||
pub mod export;
|
||||
pub mod global;
|
||||
@ -36,6 +42,9 @@ pub use self::module::Module;
|
||||
pub use self::typed_func::Func;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
use self::cache::{Cache, Error as CacheError};
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::import::{ImportObject, Namespace};
|
||||
pub use crate::types::{
|
||||
@ -79,5 +88,28 @@ pub fn validate(wasm: &[u8]) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
pub fn compile_to_cache_with(
|
||||
wasm: &[u8],
|
||||
compiler: &dyn backend::Compiler,
|
||||
) -> CompileResult<Cache> {
|
||||
let token = backend::Token::generate();
|
||||
let (info, backend_metadata, compiled_code) =
|
||||
compiler.compile_to_backend_cache_data(wasm, token)?;
|
||||
|
||||
Ok(Cache::new(wasm, info, backend_metadata, compiled_code))
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
pub unsafe fn load_cache_with(
|
||||
cache: Cache,
|
||||
compiler: &dyn backend::Compiler,
|
||||
) -> std::result::Result<module::Module, CacheError> {
|
||||
let token = backend::Token::generate();
|
||||
compiler
|
||||
.from_cache(cache, token)
|
||||
.map(|inner| module::Module::new(Arc::new(inner)))
|
||||
}
|
||||
|
||||
/// The current version of this crate
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::{
|
||||
backend::{FuncResolver, ProtectedCaller},
|
||||
backend::{Backend, FuncResolver, ProtectedCaller},
|
||||
error::Result,
|
||||
import::ImportObject,
|
||||
sig_registry::SigRegistry,
|
||||
structures::Map,
|
||||
structures::{Map, TypedIndex},
|
||||
types::{
|
||||
FuncIndex, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex,
|
||||
FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex,
|
||||
ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer,
|
||||
LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryDescriptor, MemoryIndex,
|
||||
SigIndex, TableDescriptor, TableIndex,
|
||||
@ -13,6 +12,7 @@ use crate::{
|
||||
Instance,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use indexmap::IndexMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This is used to instantiate a new WebAssembly module.
|
||||
@ -21,6 +21,11 @@ pub struct ModuleInner {
|
||||
pub func_resolver: Box<dyn FuncResolver>,
|
||||
pub protected_caller: Box<dyn ProtectedCaller>,
|
||||
|
||||
pub info: ModuleInfo,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
pub struct ModuleInfo {
|
||||
// This are strictly local and the typsystem ensures that.
|
||||
pub memories: Map<LocalMemoryIndex, MemoryDescriptor>,
|
||||
pub globals: Map<LocalGlobalIndex, GlobalInit>,
|
||||
@ -40,7 +45,11 @@ pub struct ModuleInner {
|
||||
pub start_func: Option<FuncIndex>,
|
||||
|
||||
pub func_assoc: Map<FuncIndex, SigIndex>,
|
||||
pub sig_registry: SigRegistry,
|
||||
pub signatures: Map<SigIndex, Arc<FuncSig>>,
|
||||
pub backend: Backend,
|
||||
|
||||
pub namespace_table: StringTable<NamespaceIndex>,
|
||||
pub name_table: StringTable<NameIndex>,
|
||||
}
|
||||
|
||||
/// A compiled WebAssembly module.
|
||||
@ -87,21 +96,14 @@ impl Module {
|
||||
impl ModuleInner {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImportName {
|
||||
pub namespace: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl From<(String, String)> for ImportName {
|
||||
fn from(n: (String, String)) -> Self {
|
||||
ImportName {
|
||||
namespace: n.0,
|
||||
name: n.1,
|
||||
}
|
||||
}
|
||||
pub namespace_index: NamespaceIndex,
|
||||
pub name_index: NameIndex,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ExportIndex {
|
||||
Func(FuncIndex),
|
||||
@ -111,6 +113,7 @@ pub enum ExportIndex {
|
||||
}
|
||||
|
||||
/// A data initializer for linear memory.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DataInitializer {
|
||||
/// The index of the memory to initialize.
|
||||
@ -118,10 +121,12 @@ pub struct DataInitializer {
|
||||
/// Either a constant offset or a `get_global`
|
||||
pub base: Initializer,
|
||||
/// The initialization data.
|
||||
#[cfg_attr(feature = "cache", serde(with = "serde_bytes"))]
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
/// A WebAssembly table initializer.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TableInitializer {
|
||||
/// The index of a table to initialize.
|
||||
@ -131,3 +136,110 @@ pub struct TableInitializer {
|
||||
/// The values to write into the table elements.
|
||||
pub elements: Vec<FuncIndex>,
|
||||
}
|
||||
|
||||
pub struct StringTableBuilder<K: TypedIndex> {
|
||||
map: IndexMap<String, (K, u32, u32)>,
|
||||
buffer: String,
|
||||
count: u32,
|
||||
}
|
||||
|
||||
impl<K: TypedIndex> StringTableBuilder<K> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: IndexMap::new(),
|
||||
buffer: String::new(),
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register<S>(&mut self, s: S) -> K
|
||||
where
|
||||
S: Into<String> + AsRef<str>,
|
||||
{
|
||||
let s_str = s.as_ref();
|
||||
|
||||
if self.map.contains_key(s_str) {
|
||||
self.map[s_str].0
|
||||
} else {
|
||||
let offset = self.buffer.len();
|
||||
let length = s_str.len();
|
||||
let index = TypedIndex::new(self.count as _);
|
||||
|
||||
self.buffer.push_str(s_str);
|
||||
self.map
|
||||
.insert(s.into(), (index, offset as u32, length as u32));
|
||||
self.count += 1;
|
||||
|
||||
index
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(self) -> StringTable<K> {
|
||||
let table = self
|
||||
.map
|
||||
.values()
|
||||
.map(|(_, offset, length)| (*offset, *length))
|
||||
.collect();
|
||||
|
||||
StringTable {
|
||||
table,
|
||||
buffer: self.buffer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StringTable<K: TypedIndex> {
|
||||
table: Map<K, (u32, u32)>,
|
||||
buffer: String,
|
||||
}
|
||||
|
||||
impl<K: TypedIndex> StringTable<K> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
table: Map::new(),
|
||||
buffer: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, index: K) -> &str {
|
||||
let (offset, length) = self.table[index];
|
||||
let offset = offset as usize;
|
||||
let length = length as usize;
|
||||
|
||||
&self.buffer[offset..offset + length]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct NamespaceIndex(u32);
|
||||
|
||||
impl TypedIndex for NamespaceIndex {
|
||||
#[doc(hidden)]
|
||||
fn new(index: usize) -> Self {
|
||||
NamespaceIndex(index as _)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn index(&self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct NameIndex(u32);
|
||||
|
||||
impl TypedIndex for NameIndex {
|
||||
#[doc(hidden)]
|
||||
fn new(index: usize) -> Self {
|
||||
NameIndex(index as _)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn index(&self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
/// Dense item map
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Map<K, V>
|
||||
where
|
||||
|
@ -6,7 +6,7 @@ pub use self::boxed::BoxedMap;
|
||||
pub use self::map::{Iter, IterMut, Map};
|
||||
pub use self::slice::SliceMap;
|
||||
|
||||
pub trait TypedIndex {
|
||||
pub trait TypedIndex: Copy + Clone {
|
||||
#[doc(hidden)]
|
||||
fn new(index: usize) -> Self;
|
||||
#[doc(hidden)]
|
||||
|
@ -9,3 +9,77 @@ pub use self::unix::*;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub use self::windows::*;
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
use serde::{
|
||||
de::{self, SeqAccess, Visitor},
|
||||
ser::SerializeStruct,
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
#[cfg(feature = "cache")]
|
||||
use serde_bytes::Bytes;
|
||||
#[cfg(feature = "cache")]
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
impl Serialize for Memory {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
assert!(self.protection().is_readable());
|
||||
|
||||
let mut state = serializer.serialize_struct("Memory", 2)?;
|
||||
state.serialize_field("protection", &self.protection())?;
|
||||
state.serialize_field("data", &Bytes::new(unsafe { self.as_slice() }))?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
impl<'de> Deserialize<'de> for Memory {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct MemoryVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for MemoryVisitor {
|
||||
type Value = Memory;
|
||||
|
||||
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "struct Memory")
|
||||
}
|
||||
|
||||
fn visit_seq<V>(self, mut seq: V) -> Result<Memory, V::Error>
|
||||
where
|
||||
V: SeqAccess<'de>,
|
||||
{
|
||||
let original_protection = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
|
||||
|
||||
let bytes: Bytes = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
|
||||
|
||||
let mut memory = Memory::with_size_protect(bytes.len(), Protect::ReadWrite)
|
||||
.expect("Could not create a memory");
|
||||
|
||||
unsafe {
|
||||
memory.as_slice_mut().copy_from_slice(&*bytes);
|
||||
|
||||
if memory.protection() != original_protection {
|
||||
memory
|
||||
.protect(.., original_protection)
|
||||
.expect("Could not protect memory as its original protection");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(memory)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_struct("Memory", &["protection", "data"], MemoryVisitor)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use errno;
|
||||
use nix::libc;
|
||||
use page_size;
|
||||
use std::ops::{Bound, RangeBounds};
|
||||
use std::{ptr, slice};
|
||||
use std::{fs::File, os::unix::io::IntoRawFd, path::Path, ptr, rc::Rc, slice};
|
||||
|
||||
unsafe impl Send for Memory {}
|
||||
unsafe impl Sync for Memory {}
|
||||
@ -11,14 +11,86 @@ unsafe impl Sync for Memory {}
|
||||
pub struct Memory {
|
||||
ptr: *mut u8,
|
||||
size: usize,
|
||||
protection: Protect,
|
||||
fd: Option<Rc<RawFd>>,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn from_file_path<P>(path: P, protection: Protect) -> Result<Self, String>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let file = File::open(path).map_err(|e| e.to_string())?;
|
||||
|
||||
let file_len = file.metadata().map_err(|e| e.to_string())?.len();
|
||||
|
||||
let raw_fd = RawFd::from_file(file);
|
||||
|
||||
let ptr = unsafe {
|
||||
libc::mmap(
|
||||
ptr::null_mut(),
|
||||
file_len as usize,
|
||||
protection.to_protect_const() as i32,
|
||||
libc::MAP_PRIVATE,
|
||||
raw_fd.0,
|
||||
0,
|
||||
)
|
||||
};
|
||||
|
||||
if ptr == -1 as _ {
|
||||
Err(errno::errno().to_string())
|
||||
} else {
|
||||
Ok(Self {
|
||||
ptr: ptr as *mut u8,
|
||||
size: file_len as usize,
|
||||
protection,
|
||||
fd: Some(Rc::new(raw_fd)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_size_protect(size: usize, protection: Protect) -> Result<Self, String> {
|
||||
if size == 0 {
|
||||
return Ok(Self {
|
||||
ptr: ptr::null_mut(),
|
||||
size: 0,
|
||||
protection,
|
||||
fd: None,
|
||||
});
|
||||
}
|
||||
|
||||
let size = round_up_to_page_size(size, page_size::get());
|
||||
|
||||
let ptr = unsafe {
|
||||
libc::mmap(
|
||||
ptr::null_mut(),
|
||||
size,
|
||||
protection.to_protect_const() as i32,
|
||||
libc::MAP_PRIVATE | libc::MAP_ANON,
|
||||
-1,
|
||||
0,
|
||||
)
|
||||
};
|
||||
|
||||
if ptr == -1 as _ {
|
||||
Err(errno::errno().to_string())
|
||||
} else {
|
||||
Ok(Self {
|
||||
ptr: ptr as *mut u8,
|
||||
size,
|
||||
protection,
|
||||
fd: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_size(size: usize) -> Result<Self, String> {
|
||||
if size == 0 {
|
||||
return Ok(Self {
|
||||
ptr: ptr::null_mut(),
|
||||
size: 0,
|
||||
protection: Protect::None,
|
||||
fd: None,
|
||||
});
|
||||
}
|
||||
|
||||
@ -41,6 +113,8 @@ impl Memory {
|
||||
Ok(Self {
|
||||
ptr: ptr as *mut u8,
|
||||
size,
|
||||
protection: Protect::None,
|
||||
fd: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -48,9 +122,9 @@ impl Memory {
|
||||
pub unsafe fn protect(
|
||||
&mut self,
|
||||
range: impl RangeBounds<usize>,
|
||||
protect: Protect,
|
||||
protection: Protect,
|
||||
) -> Result<(), String> {
|
||||
let protect = protect.to_protect_const();
|
||||
let protect = protection.to_protect_const();
|
||||
|
||||
let range_start = match range.start_bound() {
|
||||
Bound::Included(start) => *start,
|
||||
@ -75,10 +149,32 @@ impl Memory {
|
||||
if success == -1 {
|
||||
Err(errno::errno().to_string())
|
||||
} else {
|
||||
self.protection = protection;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split_at(mut self, offset: usize) -> (Memory, Memory) {
|
||||
let page_size = page_size::get();
|
||||
if offset % page_size == 0 {
|
||||
let second_ptr = unsafe { self.ptr.add(offset) };
|
||||
let second_size = self.size - offset;
|
||||
|
||||
self.size = offset;
|
||||
|
||||
let second = Memory {
|
||||
ptr: second_ptr,
|
||||
size: second_size,
|
||||
protection: self.protection,
|
||||
fd: self.fd.clone(),
|
||||
};
|
||||
|
||||
(self, second)
|
||||
} else {
|
||||
panic!("offset must be multiple of page size: {}", offset)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
@ -91,6 +187,10 @@ impl Memory {
|
||||
slice::from_raw_parts_mut(self.ptr, self.size)
|
||||
}
|
||||
|
||||
pub fn protection(&self) -> Protect {
|
||||
self.protection
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *mut u8 {
|
||||
self.ptr
|
||||
}
|
||||
@ -105,6 +205,7 @@ impl Drop for Memory {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Protect {
|
||||
@ -123,6 +224,41 @@ impl Protect {
|
||||
Protect::ReadExec => 1 | 4,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_readable(self) -> bool {
|
||||
match self {
|
||||
Protect::Read | Protect::ReadWrite | Protect::ReadExec => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_writable(self) -> bool {
|
||||
match self {
|
||||
Protect::ReadWrite => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RawFd(i32);
|
||||
|
||||
impl RawFd {
|
||||
fn from_file(f: File) -> Self {
|
||||
RawFd(f.into_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RawFd {
|
||||
fn drop(&mut self) {
|
||||
let success = unsafe { libc::close(self.0) };
|
||||
assert_eq!(
|
||||
success,
|
||||
0,
|
||||
"failed to close mmapped file descriptor: {}",
|
||||
errno::errno()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Round `size` up to the nearest multiple of `page_size`.
|
||||
|
@ -14,6 +14,7 @@ unsafe impl Sync for Memory {}
|
||||
pub struct Memory {
|
||||
ptr: *mut u8,
|
||||
size: usize,
|
||||
protection: Protect,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
@ -22,6 +23,7 @@ impl Memory {
|
||||
return Ok(Self {
|
||||
ptr: ptr::null_mut(),
|
||||
size: 0,
|
||||
protection: Protect::None,
|
||||
});
|
||||
}
|
||||
|
||||
@ -35,6 +37,7 @@ impl Memory {
|
||||
Ok(Self {
|
||||
ptr: ptr as *mut u8,
|
||||
size,
|
||||
protection: Protect::None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -71,10 +74,31 @@ impl Memory {
|
||||
if ptr.is_null() {
|
||||
Err("unable to protect memory".to_string())
|
||||
} else {
|
||||
self.protection = protection;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split_at(mut self, offset: usize) -> (Memory, Memory) {
|
||||
let page_size = page_size::get();
|
||||
if offset % page_size == 0 {
|
||||
let second_ptr = unsafe { self.ptr.add(offset) };
|
||||
let second_size = self.size - offset;
|
||||
|
||||
self.size = offset;
|
||||
|
||||
let second = Memory {
|
||||
ptr: second_ptr,
|
||||
size: second_size,
|
||||
protection: self.protection,
|
||||
};
|
||||
|
||||
(self, second)
|
||||
} else {
|
||||
panic!("offset must be multiple of page size: {}", offset)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
@ -87,6 +111,10 @@ impl Memory {
|
||||
slice::from_raw_parts_mut(self.ptr, self.size)
|
||||
}
|
||||
|
||||
pub fn protection(&self) -> Protect {
|
||||
self.protection
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *mut u8 {
|
||||
self.ptr
|
||||
}
|
||||
@ -101,6 +129,7 @@ impl Drop for Memory {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Protect {
|
||||
@ -119,6 +148,20 @@ impl Protect {
|
||||
Protect::ReadExec => PAGE_EXECUTE_READ,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_readable(self) -> bool {
|
||||
match self {
|
||||
Protect::Read | Protect::ReadWrite | Protect::ReadExec => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_writable(self) -> bool {
|
||||
match self {
|
||||
Protect::ReadWrite => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Round `size` up to the nearest multiple of `page_size`.
|
||||
|
@ -2,6 +2,7 @@ use crate::{memory::MemoryType, module::ModuleInner, structures::TypedIndex, uni
|
||||
use std::{borrow::Cow, mem};
|
||||
|
||||
/// Represents a WebAssembly type.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Type {
|
||||
/// The `i32` type.
|
||||
@ -18,6 +19,7 @@ pub enum Type {
|
||||
///
|
||||
/// As the number of types in WebAssembly expand,
|
||||
/// this structure will expand as well.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Value {
|
||||
/// The `i32` type.
|
||||
@ -145,12 +147,32 @@ macro_rules! convert_value_impl {
|
||||
|
||||
convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64);
|
||||
|
||||
impl ValueType for f32 {
|
||||
fn into_le(self, buffer: &mut [u8]) {
|
||||
self.to_bits().into_le(buffer);
|
||||
}
|
||||
fn from_le(buffer: &[u8]) -> Result<Self, ValueError> {
|
||||
Ok(f32::from_bits(<u32 as ValueType>::from_le(buffer)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueType for f64 {
|
||||
fn into_le(self, buffer: &mut [u8]) {
|
||||
self.to_bits().into_le(buffer);
|
||||
}
|
||||
fn from_le(buffer: &[u8]) -> Result<Self, ValueError> {
|
||||
Ok(f64::from_bits(<u64 as ValueType>::from_le(buffer)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ElementType {
|
||||
/// Any wasm function.
|
||||
Anyfunc,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TableDescriptor {
|
||||
/// Type of data stored in this table.
|
||||
@ -175,6 +197,7 @@ impl TableDescriptor {
|
||||
/// A const value initializer.
|
||||
/// Over time, this will be able to represent more and more
|
||||
/// complex expressions.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Initializer {
|
||||
/// Corresponds to a `const.*` instruction.
|
||||
@ -183,6 +206,7 @@ pub enum Initializer {
|
||||
GetGlobal(ImportedGlobalIndex),
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct GlobalDescriptor {
|
||||
pub mutable: bool,
|
||||
@ -190,6 +214,7 @@ pub struct GlobalDescriptor {
|
||||
}
|
||||
|
||||
/// A wasm global.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GlobalInit {
|
||||
pub desc: GlobalDescriptor,
|
||||
@ -197,6 +222,7 @@ pub struct GlobalInit {
|
||||
}
|
||||
|
||||
/// A wasm memory.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct MemoryDescriptor {
|
||||
/// The minimum number of allowed pages.
|
||||
@ -229,6 +255,7 @@ impl MemoryDescriptor {
|
||||
|
||||
/// The signature of a function that is either implemented
|
||||
/// in a wasm module or exposed to wasm by the host.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FuncSig {
|
||||
params: Cow<'static, [Type]>,
|
||||
@ -273,6 +300,7 @@ pub trait LocalImport {
|
||||
#[rustfmt::skip]
|
||||
macro_rules! define_map_index {
|
||||
($ty:ident) => {
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct $ty (u32);
|
||||
impl TypedIndex for $ty {
|
||||
@ -313,17 +341,17 @@ macro_rules! define_local_or_import {
|
||||
($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => {
|
||||
impl $ty {
|
||||
pub fn local_or_import(self, module: &ModuleInner) -> LocalOrImport<$ty> {
|
||||
if self.index() < module.$imports.len() {
|
||||
if self.index() < module.info.$imports.len() {
|
||||
LocalOrImport::Import(<Self as LocalImport>::Import::new(self.index()))
|
||||
} else {
|
||||
LocalOrImport::Local(<Self as LocalImport>::Local::new(self.index() - module.$imports.len()))
|
||||
LocalOrImport::Local(<Self as LocalImport>::Local::new(self.index() - module.info.$imports.len()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $local_ty {
|
||||
pub fn convert_up(self, module: &ModuleInner) -> $ty {
|
||||
$ty ((self.index() + module.$imports.len()) as u32)
|
||||
$ty ((self.index() + module.info.$imports.len()) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,6 +376,7 @@ define_local_or_import![
|
||||
(GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals),
|
||||
];
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SigIndex(u32);
|
||||
impl TypedIndex for SigIndex {
|
||||
|
@ -7,6 +7,7 @@ const WASM_PAGE_SIZE: usize = 65_536;
|
||||
const WASM_MAX_PAGES: usize = 65_536;
|
||||
|
||||
/// Units of WebAssembly pages (as specified to be 65,536 bytes).
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Pages(pub u32);
|
||||
|
||||
@ -32,6 +33,7 @@ impl fmt::Debug for Pages {
|
||||
}
|
||||
|
||||
/// Units of WebAssembly memory in terms of 8-bit bytes.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Bytes(pub usize);
|
||||
|
||||
|
@ -423,7 +423,7 @@ mod vm_offset_tests {
|
||||
#[cfg(test)]
|
||||
mod vm_ctx_tests {
|
||||
use super::{Ctx, ImportBacking, LocalBacking};
|
||||
use crate::module::ModuleInner;
|
||||
use crate::module::{ModuleInfo, ModuleInner, StringTable};
|
||||
use crate::structures::Map;
|
||||
use std::ffi::c_void;
|
||||
|
||||
@ -493,7 +493,7 @@ mod vm_ctx_tests {
|
||||
|
||||
fn generate_module() -> ModuleInner {
|
||||
use super::Func;
|
||||
use crate::backend::{FuncResolver, ProtectedCaller, SigRegistry, Token};
|
||||
use crate::backend::{Backend, FuncResolver, ProtectedCaller, Token};
|
||||
use crate::error::RuntimeResult;
|
||||
use crate::types::{FuncIndex, LocalFuncIndex, Value};
|
||||
use hashbrown::HashMap;
|
||||
@ -525,25 +525,31 @@ mod vm_ctx_tests {
|
||||
ModuleInner {
|
||||
func_resolver: Box::new(Placeholder),
|
||||
protected_caller: Box::new(Placeholder),
|
||||
memories: Map::new(),
|
||||
globals: Map::new(),
|
||||
tables: Map::new(),
|
||||
info: ModuleInfo {
|
||||
memories: Map::new(),
|
||||
globals: Map::new(),
|
||||
tables: Map::new(),
|
||||
|
||||
// These are strictly imported and the typesystem ensures that.
|
||||
imported_functions: Map::new(),
|
||||
imported_memories: Map::new(),
|
||||
imported_tables: Map::new(),
|
||||
imported_globals: Map::new(),
|
||||
// These are strictly imported and the typesystem ensures that.
|
||||
imported_functions: Map::new(),
|
||||
imported_memories: Map::new(),
|
||||
imported_tables: Map::new(),
|
||||
imported_globals: Map::new(),
|
||||
|
||||
exports: HashMap::new(),
|
||||
exports: HashMap::new(),
|
||||
|
||||
data_initializers: Vec::new(),
|
||||
elem_initializers: Vec::new(),
|
||||
data_initializers: Vec::new(),
|
||||
elem_initializers: Vec::new(),
|
||||
|
||||
start_func: None,
|
||||
start_func: None,
|
||||
|
||||
func_assoc: Map::new(),
|
||||
sig_registry: SigRegistry,
|
||||
func_assoc: Map::new(),
|
||||
signatures: Map::new(),
|
||||
backend: Backend::Cranelift,
|
||||
|
||||
namespace_table: StringTable::new(),
|
||||
name_table: StringTable::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user