mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-12 00:21:20 +00:00
Merge branch 'master' into feature/dynasm-backend
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmer-runtime-core"
|
||||
version = "0.1.2"
|
||||
version = "0.2.1"
|
||||
description = "Wasmer runtime core library"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
@ -8,7 +8,6 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
hashbrown = "0.1"
|
||||
nix = "0.12.0"
|
||||
page_size = "0.4.1"
|
||||
wasmparser = "0.23.0"
|
||||
@ -16,36 +15,34 @@ parking_lot = "0.7.1"
|
||||
lazy_static = "1.2.0"
|
||||
indexmap = "1.0.2"
|
||||
errno = "0.2.4"
|
||||
libc = "0.2.48"
|
||||
libc = "0.2.49"
|
||||
hex = "0.3.2"
|
||||
|
||||
# Dependencies for caching.
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
optional = true
|
||||
# This feature is required for serde to support serializing/deserializing reference counted pointers (e.g. Rc and Arc).
|
||||
features = ["rc"]
|
||||
[dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
optional = true
|
||||
[dependencies.serde_bytes]
|
||||
version = "0.10"
|
||||
optional = true
|
||||
[dependencies.serde-bench]
|
||||
version = "0.0.7"
|
||||
optional = true
|
||||
[dependencies.memmap]
|
||||
version = "0.7.0"
|
||||
optional = true
|
||||
[dependencies.sha2]
|
||||
[dependencies.blake2b_simd]
|
||||
version = "0.4.1"
|
||||
[dependencies.digest]
|
||||
version = "0.8.0"
|
||||
optional = true
|
||||
[dependencies.hashbrown]
|
||||
version = "0.1"
|
||||
features = ["serde"]
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["memoryapi"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.1.2" }
|
||||
field-offset = "0.1.1"
|
||||
|
||||
[features]
|
||||
debug = []
|
||||
cache = ["serde/rc", "serde_derive", "serde_bytes", "hashbrown/serde", "serde-bench", "memmap", "sha2"]
|
||||
|
||||
|
@ -6,24 +6,24 @@ use crate::{
|
||||
types::{FuncIndex, LocalFuncIndex, Value},
|
||||
vm,
|
||||
};
|
||||
#[cfg(feature = "cache")]
|
||||
|
||||
use crate::{
|
||||
cache::{Cache, Error as CacheError},
|
||||
cache::{Artifact, Error as CacheError},
|
||||
module::ModuleInfo,
|
||||
sys::Memory,
|
||||
};
|
||||
use std::ptr::NonNull;
|
||||
use std::{any::Any, ptr::NonNull};
|
||||
|
||||
pub mod sys {
|
||||
pub use crate::sys::*;
|
||||
}
|
||||
pub use crate::sig_registry::SigRegistry;
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Backend {
|
||||
Cranelift,
|
||||
Dynasm,
|
||||
LLVM,
|
||||
}
|
||||
|
||||
/// This type cannot be constructed from
|
||||
@ -44,15 +44,7 @@ pub trait Compiler {
|
||||
/// 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)>;
|
||||
unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result<ModuleInner, CacheError>;
|
||||
}
|
||||
|
||||
/// The functionality exposed by this trait is expected to be used
|
||||
@ -88,7 +80,7 @@ pub trait ProtectedCaller: Send + Sync {
|
||||
}
|
||||
|
||||
pub trait UserTrapper {
|
||||
unsafe fn do_early_trap(&self, msg: String) -> !;
|
||||
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> !;
|
||||
}
|
||||
|
||||
pub trait FuncResolver: Send + Sync {
|
||||
@ -100,3 +92,10 @@ pub trait FuncResolver: Send + Sync {
|
||||
local_func_index: LocalFuncIndex,
|
||||
) -> Option<NonNull<vm::Func>>;
|
||||
}
|
||||
|
||||
pub trait CacheGen: Send + Sync {
|
||||
fn generate_cache(
|
||||
&self,
|
||||
module: &ModuleInner,
|
||||
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), CacheError>;
|
||||
}
|
||||
|
@ -4,17 +4,18 @@ use crate::{
|
||||
global::Global,
|
||||
import::ImportObject,
|
||||
memory::Memory,
|
||||
module::{ImportName, ModuleInner},
|
||||
module::{ImportName, ModuleInfo, ModuleInner},
|
||||
sig_registry::SigRegistry,
|
||||
structures::{BoxedMap, Map, SliceMap, TypedIndex},
|
||||
table::Table,
|
||||
types::{
|
||||
ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex,
|
||||
Initializer, LocalGlobalIndex, LocalMemoryIndex, LocalOrImport, LocalTableIndex, Value,
|
||||
Initializer, LocalFuncIndex, LocalGlobalIndex, LocalMemoryIndex, LocalOrImport,
|
||||
LocalTableIndex, SigIndex, Value,
|
||||
},
|
||||
vm,
|
||||
};
|
||||
use std::{slice, sync::Arc};
|
||||
use std::slice;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LocalBacking {
|
||||
@ -25,6 +26,9 @@ pub struct LocalBacking {
|
||||
pub(crate) vm_memories: BoxedMap<LocalMemoryIndex, *mut vm::LocalMemory>,
|
||||
pub(crate) vm_tables: BoxedMap<LocalTableIndex, *mut vm::LocalTable>,
|
||||
pub(crate) vm_globals: BoxedMap<LocalGlobalIndex, *mut vm::LocalGlobal>,
|
||||
|
||||
pub(crate) dynamic_sigindices: BoxedMap<SigIndex, vm::SigId>,
|
||||
pub(crate) local_functions: BoxedMap<LocalFuncIndex, *const vm::Func>,
|
||||
}
|
||||
|
||||
// impl LocalBacking {
|
||||
@ -47,6 +51,9 @@ impl LocalBacking {
|
||||
let vm_tables = Self::finalize_tables(module, imports, &mut tables, vmctx);
|
||||
let vm_globals = Self::finalize_globals(&mut globals);
|
||||
|
||||
let dynamic_sigindices = Self::generate_sigindices(&module.info);
|
||||
let local_functions = Self::generate_local_functions(module);
|
||||
|
||||
Self {
|
||||
memories,
|
||||
tables,
|
||||
@ -55,9 +62,37 @@ impl LocalBacking {
|
||||
vm_memories,
|
||||
vm_tables,
|
||||
vm_globals,
|
||||
|
||||
dynamic_sigindices,
|
||||
local_functions,
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_local_functions(module: &ModuleInner) -> BoxedMap<LocalFuncIndex, *const vm::Func> {
|
||||
(0..module.info.func_assoc.len() - module.info.imported_functions.len())
|
||||
.map(|index| {
|
||||
module
|
||||
.func_resolver
|
||||
.get(module, LocalFuncIndex::new(index))
|
||||
.unwrap()
|
||||
.as_ptr() as *const _
|
||||
})
|
||||
.collect::<Map<_, _>>()
|
||||
.into_boxed_map()
|
||||
}
|
||||
|
||||
fn generate_sigindices(info: &ModuleInfo) -> BoxedMap<SigIndex, vm::SigId> {
|
||||
info.signatures
|
||||
.iter()
|
||||
.map(|(_, signature)| {
|
||||
let signature = SigRegistry.lookup_signature_ref(signature);
|
||||
let sig_index = SigRegistry.lookup_sig_index(signature);
|
||||
vm::SigId(sig_index.index() as u32)
|
||||
})
|
||||
.collect::<Map<_, _>>()
|
||||
.into_boxed_map()
|
||||
}
|
||||
|
||||
fn generate_memories(module: &ModuleInner) -> BoxedMap<LocalMemoryIndex, Memory> {
|
||||
let mut memories = Map::with_capacity(module.info.memories.len());
|
||||
for (_, &desc) in &module.info.memories {
|
||||
@ -91,7 +126,7 @@ impl LocalBacking {
|
||||
}
|
||||
} as usize;
|
||||
|
||||
match init.memory_index.local_or_import(module) {
|
||||
match init.memory_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_memory_index) => {
|
||||
let memory_desc = module.info.memories[local_memory_index];
|
||||
let data_top = init_base + init.data.len();
|
||||
@ -159,7 +194,7 @@ impl LocalBacking {
|
||||
}
|
||||
} as usize;
|
||||
|
||||
match init.table_index.local_or_import(module) {
|
||||
match init.table_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_table_index) => {
|
||||
let table = &tables[local_table_index];
|
||||
|
||||
@ -172,12 +207,13 @@ impl LocalBacking {
|
||||
table.anyfunc_direct_access_mut(|elements| {
|
||||
for (i, &func_index) in init.elements.iter().enumerate() {
|
||||
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 signature = &module.info.signatures[sig_index];
|
||||
let signature = SigRegistry
|
||||
.lookup_signature_ref(&module.info.signatures[sig_index]);
|
||||
let sig_id =
|
||||
vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32);
|
||||
|
||||
let (func, ctx) = match func_index.local_or_import(module) {
|
||||
let (func, ctx) = match func_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.func_resolver
|
||||
@ -210,12 +246,13 @@ impl LocalBacking {
|
||||
table.anyfunc_direct_access_mut(|elements| {
|
||||
for (i, &func_index) in init.elements.iter().enumerate() {
|
||||
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 signature = SigRegistry
|
||||
.lookup_signature_ref(&module.info.signatures[sig_index]);
|
||||
// let signature = &module.info.signatures[sig_index];
|
||||
let sig_id =
|
||||
vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32);
|
||||
|
||||
let (func, ctx) = match func_index.local_or_import(module) {
|
||||
let (func, ctx) = match func_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.func_resolver
|
||||
@ -369,7 +406,7 @@ fn import_functions(
|
||||
},
|
||||
) in &module.info.imported_functions
|
||||
{
|
||||
let sig_index = module.info.func_assoc[index.convert_up(module)];
|
||||
let sig_index = module.info.func_assoc[index.convert_up(&module.info)];
|
||||
let expected_sig = &module.info.signatures[sig_index];
|
||||
|
||||
let namespace = module.info.namespace_table.get(*namespace_index);
|
||||
@ -384,7 +421,7 @@ fn import_functions(
|
||||
ctx,
|
||||
signature,
|
||||
}) => {
|
||||
if *expected_sig == signature {
|
||||
if *expected_sig == *signature {
|
||||
functions.push(vm::ImportedFunc {
|
||||
func: func.inner(),
|
||||
vmctx: match ctx {
|
||||
@ -396,8 +433,8 @@ fn import_functions(
|
||||
link_errors.push(LinkError::IncorrectImportSignature {
|
||||
namespace: namespace.to_string(),
|
||||
name: name.to_string(),
|
||||
expected: expected_sig.clone(),
|
||||
found: signature.clone(),
|
||||
expected: (*expected_sig).clone(),
|
||||
found: (*signature).clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,9 @@
|
||||
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,
|
||||
use crate::{
|
||||
module::{Module, ModuleInfo},
|
||||
sys::Memory,
|
||||
};
|
||||
use blake2b_simd::blake2bp;
|
||||
use std::{fmt, io, mem, slice};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InvalidFileType {
|
||||
@ -26,23 +21,92 @@ pub enum Error {
|
||||
InvalidatedCache,
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(io_err: io::Error) -> Self {
|
||||
Error::IoError(io_err)
|
||||
}
|
||||
}
|
||||
|
||||
/// The hash of a wasm module.
|
||||
///
|
||||
/// Used as a key when loading and storing modules in a [`Cache`].
|
||||
///
|
||||
/// [`Cache`]: trait.Cache.html
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct WasmHash([u8; 32], [u8; 32]);
|
||||
|
||||
impl WasmHash {
|
||||
/// Hash a wasm module.
|
||||
///
|
||||
/// # Note:
|
||||
/// This does no verification that the supplied data
|
||||
/// is, in fact, a wasm module.
|
||||
pub fn generate(wasm: &[u8]) -> Self {
|
||||
let mut first_part = [0u8; 32];
|
||||
let mut second_part = [0u8; 32];
|
||||
|
||||
let mut state = blake2bp::State::new();
|
||||
state.update(wasm);
|
||||
|
||||
let hasher = state.finalize();
|
||||
let generic_array = hasher.as_bytes();
|
||||
|
||||
first_part.copy_from_slice(&generic_array[0..32]);
|
||||
second_part.copy_from_slice(&generic_array[32..64]);
|
||||
WasmHash(first_part, second_part)
|
||||
}
|
||||
|
||||
/// Create the hexadecimal representation of the
|
||||
/// stored hash.
|
||||
pub fn encode(self) -> String {
|
||||
hex::encode(&self.into_array() as &[u8])
|
||||
}
|
||||
|
||||
pub(crate) fn into_array(self) -> [u8; 64] {
|
||||
let mut total = [0u8; 64];
|
||||
total[0..32].copy_from_slice(&self.0);
|
||||
total[32..64].copy_from_slice(&self.1);
|
||||
total
|
||||
}
|
||||
}
|
||||
|
||||
const CURRENT_CACHE_VERSION: u64 = 0;
|
||||
static WASMER_CACHE_MAGIC: [u8; 8] = *b"WASMER\0\0";
|
||||
|
||||
/// The header of a cache file.
|
||||
#[repr(C, packed)]
|
||||
struct CacheHeader {
|
||||
struct ArtifactHeader {
|
||||
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) };
|
||||
impl ArtifactHeader {
|
||||
pub fn read_from_slice(buffer: &[u8]) -> Result<(&Self, &[u8]), Error> {
|
||||
if buffer.len() >= mem::size_of::<ArtifactHeader>() {
|
||||
if &buffer[..8] == &WASMER_CACHE_MAGIC {
|
||||
let (header_slice, body_slice) = buffer.split_at(mem::size_of::<ArtifactHeader>());
|
||||
let header = unsafe { &*(header_slice.as_ptr() as *const ArtifactHeader) };
|
||||
|
||||
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 read_from_slice_mut(buffer: &mut [u8]) -> Result<(&mut Self, &mut [u8]), Error> {
|
||||
if buffer.len() >= mem::size_of::<ArtifactHeader>() {
|
||||
if &buffer[..8] == &WASMER_CACHE_MAGIC {
|
||||
let (header_slice, body_slice) =
|
||||
buffer.split_at_mut(mem::size_of::<ArtifactHeader>());
|
||||
let header = unsafe { &mut *(header_slice.as_ptr() as *mut ArtifactHeader) };
|
||||
|
||||
if header.version == CURRENT_CACHE_VERSION {
|
||||
Ok((header, body_slice))
|
||||
@ -58,72 +122,53 @@ impl CacheHeader {
|
||||
}
|
||||
|
||||
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>()) }
|
||||
let ptr = self as *const ArtifactHeader as *const u8;
|
||||
unsafe { slice::from_raw_parts(ptr, mem::size_of::<ArtifactHeader>()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct CacheInner {
|
||||
struct ArtifactInner {
|
||||
info: Box<ModuleInfo>,
|
||||
#[serde(with = "serde_bytes")]
|
||||
backend_metadata: Vec<u8>,
|
||||
backend_metadata: Box<[u8]>,
|
||||
compiled_code: Memory,
|
||||
}
|
||||
|
||||
pub struct Cache {
|
||||
inner: CacheInner,
|
||||
wasm_hash: Box<[u8; 32]>,
|
||||
pub struct Artifact {
|
||||
inner: ArtifactInner,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
pub(crate) fn new(
|
||||
wasm: &[u8],
|
||||
impl Artifact {
|
||||
pub(crate) fn from_parts(
|
||||
info: Box<ModuleInfo>,
|
||||
backend_metadata: Vec<u8>,
|
||||
backend_metadata: Box<[u8]>,
|
||||
compiled_code: Memory,
|
||||
) -> Self {
|
||||
let wasm_hash = hash_data(wasm);
|
||||
|
||||
Self {
|
||||
inner: CacheInner {
|
||||
inner: ArtifactInner {
|
||||
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))?;
|
||||
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
let (_, body_slice) = ArtifactHeader::read_from_slice(bytes)?;
|
||||
|
||||
let mmap = unsafe { Mmap::map(&file).map_err(|e| Error::IoError(e))? };
|
||||
let inner = serde_bench::deserialize(body_slice)
|
||||
.map_err(|e| Error::DeserializeError(format!("{:#?}", 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),
|
||||
})
|
||||
Ok(Artifact { inner })
|
||||
}
|
||||
|
||||
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) {
|
||||
pub fn consume(self) -> (ModuleInfo, Box<[u8]>, Memory) {
|
||||
(
|
||||
*self.inner.info,
|
||||
self.inner.backend_metadata,
|
||||
@ -131,51 +176,34 @@ impl Cache {
|
||||
)
|
||||
}
|
||||
|
||||
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,
|
||||
],
|
||||
pub fn serialize(&self) -> Result<Vec<u8>, Error> {
|
||||
let cache_header = ArtifactHeader {
|
||||
magic: WASMER_CACHE_MAGIC,
|
||||
version: CURRENT_CACHE_VERSION,
|
||||
data_len,
|
||||
wasm_hash,
|
||||
data_len: 0,
|
||||
};
|
||||
|
||||
file.write(cache_header.as_slice())
|
||||
.map_err(|e| Error::IoError(e))?;
|
||||
let mut buffer = cache_header.as_slice().to_vec();
|
||||
|
||||
Ok(())
|
||||
serde_bench::serialize(&mut buffer, &self.inner)
|
||||
.map_err(|e| Error::SerializeError(e.to_string()))?;
|
||||
|
||||
let data_len = (buffer.len() - mem::size_of::<ArtifactHeader>()) as u64;
|
||||
|
||||
let (header, _) = ArtifactHeader::read_from_slice_mut(&mut buffer)?;
|
||||
header.data_len = data_len;
|
||||
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash_data(data: &[u8]) -> [u8; 32] {
|
||||
let mut array = [0u8; 32];
|
||||
array.copy_from_slice(Sha256::digest(data).as_slice());
|
||||
array
|
||||
/// A generic cache for storing and loading compiled wasm modules.
|
||||
///
|
||||
/// The `wasmer-runtime` supplies a naive `FileSystemCache` api.
|
||||
pub trait Cache {
|
||||
type LoadError: fmt::Debug;
|
||||
type StoreError: fmt::Debug;
|
||||
|
||||
fn load(&self, key: WasmHash) -> Result<Module, Self::LoadError>;
|
||||
fn store(&mut self, key: WasmHash, module: Module) -> Result<(), Self::StoreError>;
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
use crate::types::{
|
||||
FuncSig, GlobalDescriptor, MemoryDescriptor, MemoryIndex, TableDescriptor, TableIndex, Type,
|
||||
FuncSig, GlobalDescriptor, MemoryDescriptor, MemoryIndex, TableDescriptor, TableIndex, Type, Value,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use wasmparser::BinaryReaderError;
|
||||
use core::borrow::Borrow;
|
||||
use std::any::Any;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
pub type CompileResult<T> = std::result::Result<T, CompileError>;
|
||||
@ -36,6 +38,19 @@ impl PartialEq for CompileError {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CompileError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
CompileError::InternalError { msg } => {
|
||||
write!(f, "Internal compiler error: \"{}\"", msg)
|
||||
}
|
||||
CompileError::ValidationError { msg } => write!(f, "Validation error \"{}\"", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CompileError {}
|
||||
|
||||
/// This is returned when the runtime is unable to
|
||||
/// correctly link the module with the provided imports.
|
||||
///
|
||||
@ -51,8 +66,8 @@ pub enum LinkError {
|
||||
IncorrectImportSignature {
|
||||
namespace: String,
|
||||
name: String,
|
||||
expected: Arc<FuncSig>,
|
||||
found: Arc<FuncSig>,
|
||||
expected: FuncSig,
|
||||
found: FuncSig,
|
||||
},
|
||||
ImportNotFound {
|
||||
namespace: String,
|
||||
@ -84,34 +99,42 @@ impl PartialEq for LinkError {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LinkError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
LinkError::ImportNotFound {namespace, name} => write!(f, "Import not found, namespace: {}, name: {}", namespace, name),
|
||||
LinkError::IncorrectGlobalDescriptor {namespace, name,expected,found} => {
|
||||
write!(f, "Incorrect global descriptor, namespace: {}, name: {}, expected global descriptor: {:?}, found global descriptor: {:?}", namespace, name, expected, found)
|
||||
},
|
||||
LinkError::IncorrectImportSignature{namespace, name,expected,found} => {
|
||||
write!(f, "Incorrect import signature, namespace: {}, name: {}, expected signature: {}, found signature: {}", namespace, name, expected, found)
|
||||
}
|
||||
LinkError::IncorrectImportType{namespace, name,expected,found} => {
|
||||
write!(f, "Incorrect import type, namespace: {}, name: {}, expected type: {}, found type: {}", namespace, name, expected, found)
|
||||
}
|
||||
LinkError::IncorrectMemoryDescriptor{namespace, name,expected,found} => {
|
||||
write!(f, "Incorrect memory descriptor, namespace: {}, name: {}, expected memory descriptor: {:?}, found memory descriptor: {:?}", namespace, name, expected, found)
|
||||
},
|
||||
LinkError::IncorrectTableDescriptor{namespace, name,expected,found} => {
|
||||
write!(f, "Incorrect table descriptor, namespace: {}, name: {}, expected table descriptor: {:?}, found table descriptor: {:?}", namespace, name, expected, found)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for LinkError {}
|
||||
|
||||
/// This is the error type returned when calling
|
||||
/// a webassembly function.
|
||||
///
|
||||
/// The main way to do this is `Instance.call`.
|
||||
///
|
||||
/// Comparing two `RuntimeError`s always evaluates to false.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub enum RuntimeError {
|
||||
OutOfBoundsAccess {
|
||||
memory: MemoryIndex,
|
||||
addr: Option<u32>,
|
||||
},
|
||||
TableOutOfBounds {
|
||||
table: TableIndex,
|
||||
},
|
||||
IndirectCallSignature {
|
||||
table: TableIndex,
|
||||
},
|
||||
IndirectCallToNull {
|
||||
table: TableIndex,
|
||||
},
|
||||
IllegalArithmeticOperation,
|
||||
User {
|
||||
msg: String,
|
||||
},
|
||||
Unknown {
|
||||
msg: String,
|
||||
},
|
||||
Trap { msg: Box<str> },
|
||||
Exception { data: Box<[Value]> },
|
||||
Panic { data: Box<dyn Any> },
|
||||
}
|
||||
|
||||
impl PartialEq for RuntimeError {
|
||||
@ -120,22 +143,31 @@ impl PartialEq for RuntimeError {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
RuntimeError::Trap { ref msg } => {
|
||||
write!(f, "WebAssembly trap occured during runtime: {}", msg)
|
||||
}
|
||||
RuntimeError::Exception { ref data } => {
|
||||
write!(f, "Uncaught WebAssembly exception: {:?}", data)
|
||||
}
|
||||
RuntimeError::Panic { data: _ } => write!(f, "User-defined \"panic\""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RuntimeError {}
|
||||
|
||||
/// This error type is produced by resolving a wasm function
|
||||
/// given its name.
|
||||
///
|
||||
/// Comparing two `ResolveError`s always evaluates to false.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ResolveError {
|
||||
Signature {
|
||||
expected: Arc<FuncSig>,
|
||||
found: Vec<Type>,
|
||||
},
|
||||
ExportNotFound {
|
||||
name: String,
|
||||
},
|
||||
ExportWrongType {
|
||||
name: String,
|
||||
},
|
||||
Signature { expected: FuncSig, found: Vec<Type> },
|
||||
ExportNotFound { name: String },
|
||||
ExportWrongType { name: String },
|
||||
}
|
||||
|
||||
impl PartialEq for ResolveError {
|
||||
@ -144,6 +176,31 @@ impl PartialEq for ResolveError {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ResolveError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
ResolveError::ExportNotFound { name } => write!(f, "Export not found: {}", name),
|
||||
ResolveError::ExportWrongType { name } => write!(f, "Export wrong type: {}", name),
|
||||
ResolveError::Signature { expected, found } => {
|
||||
let found = found
|
||||
.as_slice()
|
||||
.iter()
|
||||
.map(|p| p.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let expected: &FuncSig = expected.borrow();
|
||||
write!(
|
||||
f,
|
||||
"Parameters of type [{}] did not match signature {}",
|
||||
found, expected
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ResolveError {}
|
||||
|
||||
/// This error type is produced by calling a wasm function
|
||||
/// exported from a module.
|
||||
///
|
||||
@ -151,7 +208,7 @@ impl PartialEq for ResolveError {
|
||||
/// be the `CallError::Runtime(RuntimeError)` variant.
|
||||
///
|
||||
/// Comparing two `CallError`s always evaluates to false.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub enum CallError {
|
||||
Resolve(ResolveError),
|
||||
Runtime(RuntimeError),
|
||||
@ -163,12 +220,24 @@ impl PartialEq for CallError {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CallError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
CallError::Resolve(resolve_error) => write!(f, "Call error: {}", resolve_error),
|
||||
CallError::Runtime(runtime_error) => write!(f, "Call error: {}", runtime_error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CallError {}
|
||||
|
||||
/// This error type is produced when creating something,
|
||||
/// like a `Memory` or a `Table`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CreationError {
|
||||
UnableToCreateMemory,
|
||||
UnableToCreateTable,
|
||||
InvalidDescriptor(String),
|
||||
}
|
||||
|
||||
impl PartialEq for CreationError {
|
||||
@ -177,12 +246,28 @@ impl PartialEq for CreationError {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CreationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
CreationError::UnableToCreateMemory => write!(f, "Unable to Create Memory"),
|
||||
CreationError::UnableToCreateTable => write!(f, "Unable to Create Table"),
|
||||
CreationError::InvalidDescriptor(msg) => write!(
|
||||
f,
|
||||
"Unable to create because the supplied descriptor is invalid: \"{}\"",
|
||||
msg
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CreationError {}
|
||||
|
||||
/// The amalgamation of all errors that can occur
|
||||
/// during the compilation, instantiation, or execution
|
||||
/// of a webassembly module.
|
||||
///
|
||||
/// Comparing two `Error`s always evaluates to false.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
CompileError(CompileError),
|
||||
LinkError(Vec<LinkError>),
|
||||
@ -245,3 +330,130 @@ impl From<ResolveError> for CallError {
|
||||
CallError::Resolve(resolve_err)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::CompileError(err) => write!(f, "compile error: {}", err),
|
||||
Error::LinkError(errs) => {
|
||||
if errs.len() == 1 {
|
||||
write!(f, "link error: {}", errs[0])
|
||||
} else {
|
||||
write!(f, "{} link errors:", errs.len())?;
|
||||
for (i, err) in errs.iter().enumerate() {
|
||||
write!(f, " ({} of {}) {}", i + 1, errs.len(), err)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Error::RuntimeError(err) => write!(f, "runtime error: {}", err),
|
||||
Error::ResolveError(err) => write!(f, "resolve error: {}", err),
|
||||
Error::CallError(err) => write!(f, "call error: {}", err),
|
||||
Error::CreationError(err) => write!(f, "creation error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GrowError {
|
||||
MemoryGrowError,
|
||||
TableGrowError,
|
||||
ExceededMaxPages(PageError),
|
||||
ExceededMaxPagesForMemory(usize, usize),
|
||||
CouldNotProtectMemory(MemoryProtectionError),
|
||||
CouldNotCreateMemory(MemoryCreationError),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GrowError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
GrowError::MemoryGrowError => write!(f, "Unable to grow memory"),
|
||||
GrowError::TableGrowError => write!(f, "Unable to grow table"),
|
||||
GrowError::ExceededMaxPages(e) => write!(f, "Grow Error: {}", e),
|
||||
GrowError::ExceededMaxPagesForMemory(left, added) => write!(f, "Failed to add pages because would exceed maximum number of pages for the memory. Left: {}, Added: {}", left, added),
|
||||
GrowError::CouldNotCreateMemory(e) => write!(f, "Grow Error: {}", e),
|
||||
GrowError::CouldNotProtectMemory(e) => write!(f, "Grow Error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for GrowError {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PageError {
|
||||
// left, right, added
|
||||
ExceededMaxPages(usize, usize, usize),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PageError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
PageError::ExceededMaxPages(left, right, added) => write!(f, "Failed to add pages because would exceed maximum number of pages. Left: {}, Right: {}, Pages added: {}", left, right, added),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::error::Error for PageError {}
|
||||
|
||||
impl Into<GrowError> for PageError {
|
||||
fn into(self) -> GrowError {
|
||||
GrowError::ExceededMaxPages(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MemoryCreationError {
|
||||
VirtualMemoryAllocationFailed(usize, String),
|
||||
CouldNotCreateMemoryFromFile(std::io::Error),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MemoryCreationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
MemoryCreationError::VirtualMemoryAllocationFailed(size, msg) => write!(
|
||||
f,
|
||||
"Allocation virtual memory with size {} failed. \nErrno message: {}",
|
||||
size, msg
|
||||
),
|
||||
MemoryCreationError::CouldNotCreateMemoryFromFile(e) => write!(f, "IO Error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::error::Error for MemoryCreationError {}
|
||||
|
||||
impl Into<GrowError> for MemoryCreationError {
|
||||
fn into(self) -> GrowError {
|
||||
GrowError::CouldNotCreateMemory(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for MemoryCreationError {
|
||||
fn from(io_error: std::io::Error) -> Self {
|
||||
MemoryCreationError::CouldNotCreateMemoryFromFile(io_error)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MemoryProtectionError {
|
||||
ProtectionFailed(usize, usize, String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MemoryProtectionError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
MemoryProtectionError::ProtectionFailed(start, size, msg) => write!(
|
||||
f,
|
||||
"Allocation virtual memory starting at {} with size {} failed. \nErrno message: {}",
|
||||
start, size, msg
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::error::Error for MemoryProtectionError {}
|
||||
|
||||
impl Into<GrowError> for MemoryProtectionError {
|
||||
fn into(self) -> GrowError {
|
||||
GrowError::CouldNotProtectMemory(self)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
use crate::export::Export;
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
pub trait LikeNamespace {
|
||||
fn get_export(&self, name: &str) -> Option<Export>;
|
||||
@ -32,19 +36,19 @@ impl IsExport for Export {
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// fn foo(n: i32, _: &mut Ctx) -> i32 {
|
||||
/// fn foo(_: &mut Ctx, n: i32) -> i32 {
|
||||
/// n
|
||||
/// }
|
||||
/// ```
|
||||
pub struct ImportObject {
|
||||
map: HashMap<String, Box<dyn LikeNamespace>>,
|
||||
map: Rc<RefCell<HashMap<String, Box<dyn LikeNamespace>>>>,
|
||||
}
|
||||
|
||||
impl ImportObject {
|
||||
/// Create a new `ImportObject`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: HashMap::new(),
|
||||
map: Rc::new(RefCell::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +71,9 @@ impl ImportObject {
|
||||
S: Into<String>,
|
||||
N: LikeNamespace + 'static,
|
||||
{
|
||||
match self.map.entry(name.into()) {
|
||||
let mut map = self.map.borrow_mut();
|
||||
|
||||
match map.entry(name.into()) {
|
||||
Entry::Vacant(empty) => {
|
||||
empty.insert(Box::new(namespace));
|
||||
None
|
||||
@ -76,8 +82,20 @@ impl ImportObject {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_namespace(&self, namespace: &str) -> Option<&(dyn LikeNamespace + 'static)> {
|
||||
self.map.get(namespace).map(|namespace| &**namespace)
|
||||
pub fn get_namespace(&self, namespace: &str) -> Option<Ref<dyn LikeNamespace + 'static>> {
|
||||
let map_ref = self.map.borrow();
|
||||
|
||||
if map_ref.contains_key(namespace) {
|
||||
Some(Ref::map(map_ref, |map| &*map[namespace]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone_ref(&self) -> Self {
|
||||
Self {
|
||||
map: Rc::clone(&self.map),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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},
|
||||
@ -38,6 +39,8 @@ impl Drop for InstanceInner {
|
||||
pub struct Instance {
|
||||
module: Arc<ModuleInner>,
|
||||
inner: Box<InstanceInner>,
|
||||
#[allow(dead_code)]
|
||||
import_object: ImportObject,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
@ -63,7 +66,11 @@ impl Instance {
|
||||
*inner.vmctx = vm::Ctx::new(&mut inner.backing, &mut inner.import_backing, &module)
|
||||
};
|
||||
|
||||
let instance = Instance { module, inner };
|
||||
let instance = Instance {
|
||||
module,
|
||||
inner,
|
||||
import_object: imports.clone_ref(),
|
||||
};
|
||||
|
||||
if let Some(start_index) = instance.module.info.start_func {
|
||||
instance.call_with_index(start_index, &[])?;
|
||||
@ -112,23 +119,24 @@ impl Instance {
|
||||
.func_assoc
|
||||
.get(*func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
let signature = &self.module.info.signatures[sig_index];
|
||||
let signature =
|
||||
SigRegistry.lookup_signature_ref(&self.module.info.signatures[sig_index]);
|
||||
|
||||
if signature.params() != Args::types() || signature.returns() != Rets::types() {
|
||||
Err(ResolveError::Signature {
|
||||
expected: Arc::clone(&signature),
|
||||
expected: (*signature).clone(),
|
||||
found: Args::types().to_vec(),
|
||||
})?;
|
||||
}
|
||||
|
||||
let ctx = match func_index.local_or_import(&*self.module) {
|
||||
let ctx = match func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(_) => self.inner.vmctx,
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
self.inner.import_backing.vm_functions[imported_func_index].vmctx
|
||||
}
|
||||
};
|
||||
|
||||
let func_ptr = match func_index.local_or_import(&self.module) {
|
||||
let func_ptr = match func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(local_func_index) => self
|
||||
.module
|
||||
.func_resolver
|
||||
@ -183,7 +191,8 @@ impl Instance {
|
||||
.func_assoc
|
||||
.get(*func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
let signature = Arc::clone(&self.module.info.signatures[sig_index]);
|
||||
let signature =
|
||||
SigRegistry.lookup_signature_ref(&self.module.info.signatures[sig_index]);
|
||||
|
||||
Ok(DynFunc {
|
||||
signature,
|
||||
@ -288,7 +297,7 @@ impl Instance {
|
||||
})?
|
||||
}
|
||||
|
||||
let vmctx = match func_index.local_or_import(&self.module) {
|
||||
let vmctx = match func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(_) => self.inner.vmctx,
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
self.inner.import_backing.vm_functions[imported_func_index].vmctx
|
||||
@ -355,7 +364,7 @@ impl InstanceInner {
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
|
||||
let (func_ptr, ctx) = match func_index.local_or_import(module) {
|
||||
let (func_ptr, ctx) = match func_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.func_resolver
|
||||
@ -374,17 +383,14 @@ impl InstanceInner {
|
||||
}
|
||||
};
|
||||
|
||||
let signature = &module.info.signatures[sig_index];
|
||||
let signature = SigRegistry.lookup_signature_ref(&module.info.signatures[sig_index]);
|
||||
// let signature = &module.info.signatures[sig_index];
|
||||
|
||||
(
|
||||
unsafe { FuncPointer::new(func_ptr) },
|
||||
ctx,
|
||||
Arc::clone(signature),
|
||||
)
|
||||
(unsafe { FuncPointer::new(func_ptr) }, ctx, signature)
|
||||
}
|
||||
|
||||
fn get_memory_from_index(&self, module: &ModuleInner, mem_index: MemoryIndex) -> Memory {
|
||||
match mem_index.local_or_import(module) {
|
||||
match mem_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_mem_index) => self.backing.memories[local_mem_index].clone(),
|
||||
LocalOrImport::Import(imported_mem_index) => {
|
||||
self.import_backing.memories[imported_mem_index].clone()
|
||||
@ -393,7 +399,7 @@ impl InstanceInner {
|
||||
}
|
||||
|
||||
fn get_global_from_index(&self, module: &ModuleInner, global_index: GlobalIndex) -> Global {
|
||||
match global_index.local_or_import(module) {
|
||||
match global_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_global_index) => {
|
||||
self.backing.globals[local_global_index].clone()
|
||||
}
|
||||
@ -404,7 +410,7 @@ impl InstanceInner {
|
||||
}
|
||||
|
||||
fn get_table_from_index(&self, module: &ModuleInner, table_index: TableIndex) -> Table {
|
||||
match table_index.local_or_import(module) {
|
||||
match table_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_table_index) => {
|
||||
self.backing.tables[local_table_index].clone()
|
||||
}
|
||||
@ -454,15 +460,15 @@ impl<'a> DynFunc<'a> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn call(&mut self, params: &[Value]) -> CallResult<Vec<Value>> {
|
||||
pub fn call(&self, params: &[Value]) -> CallResult<Vec<Value>> {
|
||||
if !self.signature.check_param_value_types(params) {
|
||||
Err(ResolveError::Signature {
|
||||
expected: self.signature.clone(),
|
||||
expected: (*self.signature).clone(),
|
||||
found: params.iter().map(|val| val.ty()).collect(),
|
||||
})?
|
||||
}
|
||||
|
||||
let vmctx = match self.func_index.local_or_import(self.module) {
|
||||
let vmctx = match self.func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(_) => self.instance_inner.vmctx,
|
||||
LocalOrImport::Import(imported_func_index) => {
|
||||
self.instance_inner.import_backing.vm_functions[imported_func_index].vmctx
|
||||
@ -488,7 +494,7 @@ impl<'a> DynFunc<'a> {
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> *const vm::Func {
|
||||
match self.func_index.local_or_import(self.module) {
|
||||
match self.func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(local_func_index) => self
|
||||
.module
|
||||
.func_resolver
|
||||
|
@ -2,7 +2,6 @@
|
||||
#[macro_use]
|
||||
extern crate field_offset;
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
@ -11,7 +10,7 @@ mod macros;
|
||||
#[doc(hidden)]
|
||||
pub mod backend;
|
||||
mod backing;
|
||||
#[cfg(feature = "cache")]
|
||||
|
||||
pub mod cache;
|
||||
pub mod error;
|
||||
pub mod export;
|
||||
@ -35,6 +34,8 @@ use self::error::CompileResult;
|
||||
#[doc(inline)]
|
||||
pub use self::error::Result;
|
||||
#[doc(inline)]
|
||||
pub use self::import::IsExport;
|
||||
#[doc(inline)]
|
||||
pub use self::instance::Instance;
|
||||
#[doc(inline)]
|
||||
pub use self::module::Module;
|
||||
@ -42,8 +43,7 @@ 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};
|
||||
use self::cache::{Artifact, Error as CacheError};
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::import::{ImportObject, Namespace};
|
||||
@ -88,21 +88,8 @@ 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,
|
||||
cache: Artifact,
|
||||
compiler: &dyn backend::Compiler,
|
||||
) -> std::result::Result<module::Module, CacheError> {
|
||||
let token = backend::Token::generate();
|
||||
|
@ -38,7 +38,7 @@ macro_rules! func {
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// fn foo(n: i32, _: &mut Ctx) -> i32 {
|
||||
/// fn foo(_: &mut Ctx, n: i32) -> i32 {
|
||||
/// n
|
||||
/// }
|
||||
/// ```
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::error::GrowError;
|
||||
use crate::{
|
||||
error::CreationError,
|
||||
sys,
|
||||
@ -14,9 +15,9 @@ pub const DYNAMIC_GUARD_SIZE: usize = 4096;
|
||||
/// when first created. Over time, as it grows, it may reallocate to
|
||||
/// a different location and size.
|
||||
///
|
||||
/// Dynamic memories are signifigantly faster to create than static
|
||||
/// Dynamic memories are significantly faster to create than static
|
||||
/// memories and use much less virtual memory, however, they require
|
||||
/// the webassembly module to bounds-check memory accesses.
|
||||
/// the WebAssembly module to bounds-check memory accesses.
|
||||
///
|
||||
/// While, a dynamic memory could use a vector of some sort as its
|
||||
/// backing memory, we use mmap (or the platform-equivalent) to allow
|
||||
@ -65,26 +66,29 @@ impl DynamicMemory {
|
||||
self.current
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Option<Pages> {
|
||||
pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Result<Pages, GrowError> {
|
||||
if delta == Pages(0) {
|
||||
return Some(self.current);
|
||||
return Ok(self.current);
|
||||
}
|
||||
|
||||
let new_pages = self.current.checked_add(delta)?;
|
||||
let new_pages = self.current.checked_add(delta).map_err(|e| e.into())?;
|
||||
|
||||
if let Some(max) = self.max {
|
||||
if new_pages > max {
|
||||
return None;
|
||||
return Err(GrowError::ExceededMaxPagesForMemory(
|
||||
new_pages.0 as usize,
|
||||
max.0 as usize,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_memory =
|
||||
sys::Memory::with_size(new_pages.bytes().0 + DYNAMIC_GUARD_SIZE).ok()?;
|
||||
let mut new_memory = sys::Memory::with_size(new_pages.bytes().0 + DYNAMIC_GUARD_SIZE)
|
||||
.map_err(|e| e.into())?;
|
||||
|
||||
unsafe {
|
||||
new_memory
|
||||
.protect(0..new_pages.bytes().0, sys::Protect::ReadWrite)
|
||||
.ok()?;
|
||||
.map_err(|e| e.into())?;
|
||||
|
||||
new_memory.as_slice_mut()[..self.current.bytes().0]
|
||||
.copy_from_slice(&self.memory.as_slice()[..self.current.bytes().0]);
|
||||
@ -97,7 +101,7 @@ impl DynamicMemory {
|
||||
|
||||
let old_pages = self.current;
|
||||
self.current = new_pages;
|
||||
Some(old_pages)
|
||||
Ok(old_pages)
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
error::CreationError,
|
||||
error::{CreationError, GrowError},
|
||||
export::Export,
|
||||
import::IsExport,
|
||||
memory::dynamic::DYNAMIC_GUARD_SIZE,
|
||||
@ -63,6 +63,15 @@ impl Memory {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new(desc: MemoryDescriptor) -> Result<Self, CreationError> {
|
||||
if let Some(max) = desc.maximum {
|
||||
if max < desc.minimum {
|
||||
return Err(CreationError::InvalidDescriptor(
|
||||
"Max number of memory pages is less than the minimum number of pages"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let variant = if !desc.shared {
|
||||
MemoryVariant::Unshared(UnsharedMemory::new(desc)?)
|
||||
} else {
|
||||
@ -80,8 +89,8 @@ impl Memory {
|
||||
self.desc
|
||||
}
|
||||
|
||||
/// Grow this memory by the specfied number of pages.
|
||||
pub fn grow(&self, delta: Pages) -> Option<Pages> {
|
||||
/// Grow this memory by the specified number of pages.
|
||||
pub fn grow(&self, delta: Pages) -> Result<Pages, GrowError> {
|
||||
match &self.variant {
|
||||
MemoryVariant::Unshared(unshared_mem) => unshared_mem.grow(delta),
|
||||
MemoryVariant::Shared(shared_mem) => shared_mem.grow(delta),
|
||||
@ -235,7 +244,7 @@ impl UnsharedMemory {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn grow(&self, delta: Pages) -> Option<Pages> {
|
||||
pub fn grow(&self, delta: Pages) -> Result<Pages, GrowError> {
|
||||
let mut storage = self.internal.storage.borrow_mut();
|
||||
|
||||
let mut local = self.internal.local.get();
|
||||
@ -283,7 +292,7 @@ impl SharedMemory {
|
||||
Ok(Self { desc })
|
||||
}
|
||||
|
||||
pub fn grow(&self, _delta: Pages) -> Option<Pages> {
|
||||
pub fn grow(&self, _delta: Pages) -> Result<Pages, GrowError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@ -297,3 +306,21 @@ impl Clone for SharedMemory {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod memory_tests {
|
||||
|
||||
use super::{Memory, MemoryDescriptor, Pages};
|
||||
|
||||
#[test]
|
||||
fn test_initial_memory_size() {
|
||||
let unshared_memory = Memory::new(MemoryDescriptor {
|
||||
minimum: Pages(10),
|
||||
maximum: Some(Pages(20)),
|
||||
shared: false,
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(unshared_memory.size(), Pages(10));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::error::GrowError;
|
||||
use crate::{
|
||||
error::CreationError,
|
||||
memory::static_::{SAFE_STATIC_GUARD_SIZE, SAFE_STATIC_HEAP_SIZE},
|
||||
@ -61,27 +62,30 @@ impl StaticMemory {
|
||||
self.current
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Option<Pages> {
|
||||
pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Result<Pages, GrowError> {
|
||||
if delta == Pages(0) {
|
||||
return Some(self.current);
|
||||
return Ok(self.current);
|
||||
}
|
||||
|
||||
let new_pages = self.current.checked_add(delta)?;
|
||||
let new_pages = self.current.checked_add(delta).map_err(|e| e.into())?;
|
||||
|
||||
if let Some(max) = self.max {
|
||||
if new_pages > max {
|
||||
return None;
|
||||
return Err(GrowError::ExceededMaxPagesForMemory(
|
||||
new_pages.0 as usize,
|
||||
max.0 as usize,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _ = unsafe {
|
||||
self.memory
|
||||
.protect(
|
||||
self.current.bytes().0..new_pages.bytes().0,
|
||||
sys::Protect::ReadWrite,
|
||||
)
|
||||
.ok()?;
|
||||
}
|
||||
.map_err(|e| e.into())
|
||||
}?;
|
||||
|
||||
local.bound = new_pages.bytes().0;
|
||||
|
||||
@ -89,7 +93,7 @@ impl StaticMemory {
|
||||
|
||||
self.current = new_pages;
|
||||
|
||||
Some(old_pages)
|
||||
Ok(old_pages)
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
backend::{Backend, FuncResolver, ProtectedCaller},
|
||||
error::Result,
|
||||
cache::{Artifact, Error as CacheError},
|
||||
error,
|
||||
import::ImportObject,
|
||||
structures::{Map, TypedIndex},
|
||||
typed_func::EARLY_TRAPPER,
|
||||
@ -12,6 +13,8 @@ use crate::{
|
||||
},
|
||||
Instance,
|
||||
};
|
||||
|
||||
use crate::backend::CacheGen;
|
||||
use hashbrown::HashMap;
|
||||
use indexmap::IndexMap;
|
||||
use std::sync::Arc;
|
||||
@ -22,10 +25,12 @@ pub struct ModuleInner {
|
||||
pub func_resolver: Box<dyn FuncResolver>,
|
||||
pub protected_caller: Box<dyn ProtectedCaller>,
|
||||
|
||||
pub cache_gen: Box<dyn CacheGen>,
|
||||
|
||||
pub info: ModuleInfo,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct ModuleInfo {
|
||||
// This are strictly local and the typsystem ensures that.
|
||||
pub memories: Map<LocalMemoryIndex, MemoryDescriptor>,
|
||||
@ -46,7 +51,7 @@ pub struct ModuleInfo {
|
||||
pub start_func: Option<FuncIndex>,
|
||||
|
||||
pub func_assoc: Map<FuncIndex, SigIndex>,
|
||||
pub signatures: Map<SigIndex, Arc<FuncSig>>,
|
||||
pub signatures: Map<SigIndex, FuncSig>,
|
||||
pub backend: Backend,
|
||||
|
||||
pub namespace_table: StringTable<NamespaceIndex>,
|
||||
@ -60,7 +65,9 @@ pub struct ModuleInfo {
|
||||
///
|
||||
/// [`compile`]: fn.compile.html
|
||||
/// [`compile_with`]: fn.compile_with.html
|
||||
pub struct Module(#[doc(hidden)] pub Arc<ModuleInner>);
|
||||
pub struct Module {
|
||||
inner: Arc<ModuleInner>,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub(crate) fn new(inner: Arc<ModuleInner>) -> Self {
|
||||
@ -68,7 +75,7 @@ impl Module {
|
||||
EARLY_TRAPPER
|
||||
.with(|ucell| *ucell.get() = Some(inner.protected_caller.get_early_trapper()));
|
||||
}
|
||||
Module(inner)
|
||||
Module { inner }
|
||||
}
|
||||
|
||||
/// Instantiate a WebAssembly module with the provided [`ImportObject`].
|
||||
@ -93,23 +100,38 @@ impl Module {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn instantiate(&self, import_object: &ImportObject) -> Result<Instance> {
|
||||
Instance::new(Arc::clone(&self.0), import_object)
|
||||
pub fn instantiate(&self, import_object: &ImportObject) -> error::Result<Instance> {
|
||||
Instance::new(Arc::clone(&self.inner), import_object)
|
||||
}
|
||||
|
||||
pub fn cache(&self) -> Result<Artifact, CacheError> {
|
||||
let (info, backend_metadata, code) = self.inner.cache_gen.generate_cache(&self.inner)?;
|
||||
Ok(Artifact::from_parts(info, backend_metadata, code))
|
||||
}
|
||||
|
||||
pub fn info(&self) -> &ModuleInfo {
|
||||
&self.inner.info
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Module {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: Arc::clone(&self.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleInner {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ImportName {
|
||||
pub namespace_index: NamespaceIndex,
|
||||
pub name_index: NameIndex,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ExportIndex {
|
||||
Func(FuncIndex),
|
||||
Memory(MemoryIndex),
|
||||
@ -118,8 +140,7 @@ pub enum ExportIndex {
|
||||
}
|
||||
|
||||
/// A data initializer for linear memory.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct DataInitializer {
|
||||
/// The index of the memory to initialize.
|
||||
pub memory_index: MemoryIndex,
|
||||
@ -131,8 +152,7 @@ pub struct DataInitializer {
|
||||
}
|
||||
|
||||
/// A WebAssembly table initializer.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TableInitializer {
|
||||
/// The index of a table to initialize.
|
||||
pub table_index: TableIndex,
|
||||
@ -193,8 +213,7 @@ impl<K: TypedIndex> StringTableBuilder<K> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct StringTable<K: TypedIndex> {
|
||||
table: Map<K, (u32, u32)>,
|
||||
buffer: String,
|
||||
@ -217,8 +236,7 @@ impl<K: TypedIndex> StringTable<K> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct NamespaceIndex(u32);
|
||||
|
||||
impl TypedIndex for NamespaceIndex {
|
||||
@ -233,8 +251,7 @@ impl TypedIndex for NamespaceIndex {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct NameIndex(u32);
|
||||
|
||||
impl TypedIndex for NameIndex {
|
||||
|
@ -49,4 +49,20 @@ impl SigRegistry {
|
||||
let global = (*GLOBAL_SIG_REGISTRY).read();
|
||||
Arc::clone(&global.sig_assoc[sig_index])
|
||||
}
|
||||
|
||||
pub fn lookup_signature_ref(&self, func_sig: &FuncSig) -> Arc<FuncSig> {
|
||||
let mut global = (*GLOBAL_SIG_REGISTRY).write();
|
||||
let global = &mut *global;
|
||||
|
||||
let func_table = &mut global.func_table;
|
||||
let sig_assoc = &mut global.sig_assoc;
|
||||
|
||||
if func_table.contains_key(func_sig) {
|
||||
Arc::clone(&sig_assoc[func_table[func_sig]])
|
||||
} else {
|
||||
let arc = Arc::new(func_sig.clone());
|
||||
func_table.insert(Arc::clone(&arc), sig_assoc.push(Arc::clone(&arc)));
|
||||
arc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
/// Dense item map
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Map<K, V>
|
||||
where
|
||||
K: TypedIndex,
|
||||
@ -57,6 +56,10 @@ where
|
||||
pub fn into_boxed_map(self) -> BoxedMap<K, V> {
|
||||
BoxedMap::new(self.elems.into_boxed_slice())
|
||||
}
|
||||
|
||||
pub fn into_vec(self) -> Vec<V> {
|
||||
self.elems
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Map<K, V>
|
||||
|
@ -10,18 +10,16 @@ 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
|
||||
@ -36,7 +34,6 @@ impl Serialize for Memory {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
impl<'de> Deserialize<'de> for Memory {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::error::MemoryCreationError;
|
||||
use crate::error::MemoryProtectionError;
|
||||
use errno;
|
||||
use nix::libc;
|
||||
use page_size;
|
||||
@ -16,13 +18,13 @@ pub struct Memory {
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn from_file_path<P>(path: P, protection: Protect) -> Result<Self, String>
|
||||
pub fn from_file_path<P>(path: P, protection: Protect) -> Result<Self, MemoryCreationError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let file = File::open(path).map_err(|e| e.to_string())?;
|
||||
let file = File::open(path)?;
|
||||
|
||||
let file_len = file.metadata().map_err(|e| e.to_string())?.len();
|
||||
let file_len = file.metadata()?.len();
|
||||
|
||||
let raw_fd = RawFd::from_file(file);
|
||||
|
||||
@ -38,7 +40,10 @@ impl Memory {
|
||||
};
|
||||
|
||||
if ptr == -1 as _ {
|
||||
Err(errno::errno().to_string())
|
||||
Err(MemoryCreationError::VirtualMemoryAllocationFailed(
|
||||
file_len as usize,
|
||||
errno::errno().to_string(),
|
||||
))
|
||||
} else {
|
||||
Ok(Self {
|
||||
ptr: ptr as *mut u8,
|
||||
@ -84,7 +89,7 @@ impl Memory {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_size(size: usize) -> Result<Self, String> {
|
||||
pub fn with_size(size: usize) -> Result<Self, MemoryCreationError> {
|
||||
if size == 0 {
|
||||
return Ok(Self {
|
||||
ptr: ptr::null_mut(),
|
||||
@ -108,7 +113,10 @@ impl Memory {
|
||||
};
|
||||
|
||||
if ptr == -1 as _ {
|
||||
Err(errno::errno().to_string())
|
||||
Err(MemoryCreationError::VirtualMemoryAllocationFailed(
|
||||
size,
|
||||
errno::errno().to_string(),
|
||||
))
|
||||
} else {
|
||||
Ok(Self {
|
||||
ptr: ptr as *mut u8,
|
||||
@ -123,7 +131,7 @@ impl Memory {
|
||||
&mut self,
|
||||
range: impl RangeBounds<usize>,
|
||||
protection: Protect,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), MemoryProtectionError> {
|
||||
let protect = protection.to_protect_const();
|
||||
|
||||
let range_start = match range.start_bound() {
|
||||
@ -147,7 +155,11 @@ impl Memory {
|
||||
|
||||
let success = libc::mprotect(start as _, size, protect as i32);
|
||||
if success == -1 {
|
||||
Err(errno::errno().to_string())
|
||||
Err(MemoryProtectionError::ProtectionFailed(
|
||||
start as usize,
|
||||
size,
|
||||
errno::errno().to_string(),
|
||||
))
|
||||
} else {
|
||||
self.protection = protection;
|
||||
Ok(())
|
||||
@ -205,14 +217,35 @@ impl Drop for Memory {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
impl Clone for Memory {
|
||||
fn clone(&self) -> Self {
|
||||
let temp_protection = if self.protection.is_writable() {
|
||||
self.protection
|
||||
} else {
|
||||
Protect::ReadWrite
|
||||
};
|
||||
|
||||
let mut new = Memory::with_size_protect(self.size, temp_protection).unwrap();
|
||||
unsafe {
|
||||
new.as_slice_mut().copy_from_slice(self.as_slice());
|
||||
|
||||
if temp_protection != self.protection {
|
||||
new.protect(.., self.protection).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Protect {
|
||||
None,
|
||||
Read,
|
||||
ReadWrite,
|
||||
ReadExec,
|
||||
ReadWriteExec,
|
||||
}
|
||||
|
||||
impl Protect {
|
||||
@ -222,6 +255,7 @@ impl Protect {
|
||||
Protect::Read => 1,
|
||||
Protect::ReadWrite => 1 | 2,
|
||||
Protect::ReadExec => 1 | 4,
|
||||
Protect::ReadWriteExec => 1 | 2 | 4,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::error::MemoryCreationError;
|
||||
use crate::error::MemoryProtectionError;
|
||||
use page_size;
|
||||
use std::ops::{Bound, RangeBounds};
|
||||
use std::{ptr, slice};
|
||||
@ -44,7 +46,7 @@ impl Memory {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_size(size: usize) -> Result<Self, String> {
|
||||
pub fn with_size(size: usize) -> Result<Self, MemoryCreationError> {
|
||||
if size == 0 {
|
||||
return Ok(Self {
|
||||
ptr: ptr::null_mut(),
|
||||
@ -58,7 +60,10 @@ impl Memory {
|
||||
let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, MEM_RESERVE, PAGE_NOACCESS) };
|
||||
|
||||
if ptr.is_null() {
|
||||
Err("unable to allocate memory".to_string())
|
||||
Err(MemoryCreationError::VirtualMemoryAllocationFailed(
|
||||
size,
|
||||
"unable to allocate memory".to_string(),
|
||||
))
|
||||
} else {
|
||||
Ok(Self {
|
||||
ptr: ptr as *mut u8,
|
||||
@ -72,7 +77,7 @@ impl Memory {
|
||||
&mut self,
|
||||
range: impl RangeBounds<usize>,
|
||||
protect: Protect,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), MemoryProtectionError> {
|
||||
let protect_const = protect.to_protect_const();
|
||||
|
||||
let range_start = match range.start_bound() {
|
||||
@ -98,7 +103,11 @@ impl Memory {
|
||||
let ptr = VirtualAlloc(start as _, size, MEM_COMMIT, protect_const);
|
||||
|
||||
if ptr.is_null() {
|
||||
Err("unable to protect memory".to_string())
|
||||
Err(MemoryProtectionError::ProtectionFailed(
|
||||
start as usize,
|
||||
size,
|
||||
"unable to protect memory".to_string(),
|
||||
))
|
||||
} else {
|
||||
self.protection = protect;
|
||||
Ok(())
|
||||
@ -150,13 +159,34 @@ impl Drop for Memory {
|
||||
fn drop(&mut self) {
|
||||
if !self.ptr.is_null() {
|
||||
let success = unsafe { VirtualFree(self.ptr as _, self.size, MEM_DECOMMIT) };
|
||||
assert_eq!(success, 0, "failed to unmap memory: {}", errno::errno());
|
||||
// If the function succeeds, the return value is nonzero.
|
||||
assert_eq!(success, 1, "failed to unmap memory: {}", errno::errno());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
impl Clone for Memory {
|
||||
fn clone(&self) -> Self {
|
||||
let temp_protection = if self.protection.is_writable() {
|
||||
self.protection
|
||||
} else {
|
||||
Protect::ReadWrite
|
||||
};
|
||||
|
||||
let mut new = Memory::with_size_protect(self.size, temp_protection).unwrap();
|
||||
unsafe {
|
||||
new.as_slice_mut().copy_from_slice(self.as_slice());
|
||||
|
||||
if temp_protection != self.protection {
|
||||
new.protect(.., self.protection).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Protect {
|
||||
None,
|
||||
|
@ -53,10 +53,7 @@ impl AnyfuncTable {
|
||||
desc: TableDescriptor,
|
||||
local: &mut vm::LocalTable,
|
||||
) -> Result<Box<Self>, CreationError> {
|
||||
let initial_table_backing_len = match desc.maximum {
|
||||
Some(max) => max,
|
||||
None => desc.minimum,
|
||||
} as usize;
|
||||
let initial_table_backing_len = desc.minimum as usize;
|
||||
|
||||
let mut storage = Box::new(AnyfuncTable {
|
||||
backing: vec![vm::Anyfunc::null(); initial_table_backing_len],
|
||||
|
@ -11,6 +11,7 @@ mod anyfunc;
|
||||
|
||||
pub use self::anyfunc::Anyfunc;
|
||||
use self::anyfunc::AnyfuncTable;
|
||||
use crate::error::GrowError;
|
||||
|
||||
pub enum Element<'a> {
|
||||
Anyfunc(Anyfunc<'a>),
|
||||
@ -50,6 +51,14 @@ impl Table {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new(desc: TableDescriptor) -> Result<Self, CreationError> {
|
||||
if let Some(max) = desc.maximum {
|
||||
if max < desc.minimum {
|
||||
return Err(CreationError::InvalidDescriptor(
|
||||
"Max table size is less than the minimum size".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let mut local = vm::LocalTable {
|
||||
base: ptr::null_mut(),
|
||||
count: 0,
|
||||
@ -100,15 +109,15 @@ impl Table {
|
||||
}
|
||||
|
||||
/// Grow this table by `delta`.
|
||||
pub fn grow(&self, delta: u32) -> Option<u32> {
|
||||
pub fn grow(&self, delta: u32) -> Result<u32, GrowError> {
|
||||
if delta == 0 {
|
||||
return Some(self.size());
|
||||
return Ok(self.size());
|
||||
}
|
||||
|
||||
match &mut *self.storage.borrow_mut() {
|
||||
(TableStorage::Anyfunc(ref mut anyfunc_table), ref mut local) => {
|
||||
anyfunc_table.grow(delta, local)
|
||||
}
|
||||
(TableStorage::Anyfunc(ref mut anyfunc_table), ref mut local) => anyfunc_table
|
||||
.grow(delta, local)
|
||||
.ok_or(GrowError::TableGrowError),
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,3 +149,21 @@ impl fmt::Debug for Table {
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod table_tests {
|
||||
|
||||
use super::{ElementType, Table, TableDescriptor};
|
||||
|
||||
#[test]
|
||||
fn test_initial_table_size() {
|
||||
let table = Table::new(TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: 10,
|
||||
maximum: Some(20),
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(table.size(), 10);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
types::{FuncSig, Type, WasmExternType},
|
||||
vm::Ctx,
|
||||
};
|
||||
use std::{cell::UnsafeCell, fmt, marker::PhantomData, mem, panic, ptr, sync::Arc};
|
||||
use std::{any::Any, cell::UnsafeCell, marker::PhantomData, mem, panic, ptr, sync::Arc};
|
||||
|
||||
thread_local! {
|
||||
pub static EARLY_TRAPPER: UnsafeCell<Option<Box<dyn UserTrapper>>> = UnsafeCell::new(None);
|
||||
@ -40,14 +40,14 @@ pub trait TrapEarly<Rets>
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn report(self) -> Result<Rets, String>;
|
||||
fn report(self) -> Result<Rets, Box<dyn Any>>;
|
||||
}
|
||||
|
||||
impl<Rets> TrapEarly<Rets> for Rets
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn report(self) -> Result<Rets, String> {
|
||||
fn report(self) -> Result<Rets, Box<dyn Any>> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@ -55,10 +55,10 @@ where
|
||||
impl<Rets, E> TrapEarly<Rets> for Result<Rets, E>
|
||||
where
|
||||
Rets: WasmTypeList,
|
||||
E: fmt::Debug,
|
||||
E: Any,
|
||||
{
|
||||
fn report(self) -> Result<Rets, String> {
|
||||
self.map_err(|err| format!("Error: {:?}", err))
|
||||
fn report(self) -> Result<Rets, Box<dyn Any>> {
|
||||
self.map_err(|err| Box::new(err) as Box<dyn Any>)
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,9 +138,9 @@ impl<A: WasmExternType> WasmTypeList for (A,) {
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn call<Rets: WasmTypeList>(self, f: *const (), ctx: *mut Ctx) -> Rets {
|
||||
let f: extern "C" fn(A, *mut Ctx) -> Rets = mem::transmute(f);
|
||||
let f: extern "C" fn(*mut Ctx, A) -> Rets = mem::transmute(f);
|
||||
let (a,) = self;
|
||||
f(a, ctx)
|
||||
f(ctx, a)
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,8 +154,8 @@ where
|
||||
}
|
||||
|
||||
macro_rules! impl_traits {
|
||||
( $struct_name:ident, $( $x:ident ),* ) => {
|
||||
#[repr(C)]
|
||||
( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => {
|
||||
#[repr($repr)]
|
||||
pub struct $struct_name <$( $x ),*> ( $( $x ),* );
|
||||
|
||||
impl< $( $x: WasmExternType, )* > WasmTypeList for ( $( $x ),* ) {
|
||||
@ -175,41 +175,33 @@ macro_rules! impl_traits {
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn call<Rets: WasmTypeList>(self, f: *const (), ctx: *mut Ctx) -> Rets {
|
||||
let f: extern fn( $( $x, )* *mut Ctx) -> Rets::CStruct = mem::transmute(f);
|
||||
let f: extern fn(*mut Ctx $( ,$x )*) -> Rets::CStruct = mem::transmute(f);
|
||||
#[allow(unused_parens)]
|
||||
let ( $( $x ),* ) = self;
|
||||
let c_struct = f( $( $x, )* ctx);
|
||||
let c_struct = f(ctx $( ,$x )*);
|
||||
Rets::from_c_struct(c_struct)
|
||||
}
|
||||
}
|
||||
|
||||
impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( $( $x, )* &mut Ctx) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN {
|
||||
impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN {
|
||||
#[allow(non_snake_case)]
|
||||
fn to_raw(&self) -> *const () {
|
||||
assert_eq!(mem::size_of::<Self>(), 0, "you cannot use a closure that captures state for `Func`.");
|
||||
|
||||
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( $( $x, )* &mut Ctx) -> Trap>( $( $x: $x, )* ctx: &mut Ctx) -> Rets::CStruct {
|
||||
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: $x )* ) -> Rets::CStruct {
|
||||
let f: FN = unsafe { mem::transmute_copy(&()) };
|
||||
|
||||
let msg = match panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
f( $( $x, )* ctx).report()
|
||||
let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
f( ctx $( ,$x )* ).report()
|
||||
})) {
|
||||
Ok(Ok(returns)) => return returns.into_c_struct(),
|
||||
Ok(Err(err)) => err,
|
||||
Err(err) => {
|
||||
if let Some(s) = err.downcast_ref::<&str>() {
|
||||
s.to_string()
|
||||
} else if let Some(s) = err.downcast_ref::<String>() {
|
||||
s.clone()
|
||||
} else {
|
||||
"a panic occurred, but no additional information is available".to_string()
|
||||
}
|
||||
},
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
if let Some(early_trapper) = &*EARLY_TRAPPER.with(|ucell| ucell.get()) {
|
||||
early_trapper.do_early_trap(msg)
|
||||
early_trapper.do_early_trap(err)
|
||||
} else {
|
||||
eprintln!("panic handling not setup");
|
||||
std::process::exit(1)
|
||||
@ -234,18 +226,19 @@ macro_rules! impl_traits {
|
||||
};
|
||||
}
|
||||
|
||||
impl_traits!(S0,);
|
||||
impl_traits!(S1, A);
|
||||
impl_traits!(S2, A, B);
|
||||
impl_traits!(S3, A, B, C);
|
||||
impl_traits!(S4, A, B, C, D);
|
||||
impl_traits!(S5, A, B, C, D, E);
|
||||
impl_traits!(S6, A, B, C, D, E, F);
|
||||
impl_traits!(S7, A, B, C, D, E, F, G);
|
||||
impl_traits!(S8, A, B, C, D, E, F, G, H);
|
||||
impl_traits!(S9, A, B, C, D, E, F, G, H, I);
|
||||
impl_traits!(S10, A, B, C, D, E, F, G, H, I, J);
|
||||
impl_traits!(S11, A, B, C, D, E, F, G, H, I, J, K);
|
||||
impl_traits!([C] S0,);
|
||||
impl_traits!([transparent] S1, A);
|
||||
impl_traits!([C] S2, A, B);
|
||||
impl_traits!([C] S3, A, B, C);
|
||||
impl_traits!([C] S4, A, B, C, D);
|
||||
impl_traits!([C] S5, A, B, C, D, E);
|
||||
impl_traits!([C] S6, A, B, C, D, E, F);
|
||||
impl_traits!([C] S7, A, B, C, D, E, F, G);
|
||||
impl_traits!([C] S8, A, B, C, D, E, F, G, H);
|
||||
impl_traits!([C] S9, A, B, C, D, E, F, G, H, I);
|
||||
impl_traits!([C] S10, A, B, C, D, E, F, G, H, I, J);
|
||||
impl_traits!([C] S11, A, B, C, D, E, F, G, H, I, J, K);
|
||||
impl_traits!([C] S12, A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
|
||||
impl<'a, Args, Rets, Safety> IsExport for Func<'a, Args, Rets, Safety>
|
||||
where
|
||||
@ -271,7 +264,7 @@ mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_call() {
|
||||
fn foo(a: i32, b: i32, _ctx: &mut Ctx) -> (i32, i32) {
|
||||
fn foo(_ctx: &mut Ctx, a: i32, b: i32) -> (i32, i32) {
|
||||
(a, b)
|
||||
}
|
||||
|
||||
@ -282,7 +275,7 @@ mod tests {
|
||||
fn test_imports() {
|
||||
use crate::{func, imports};
|
||||
|
||||
fn foo(a: i32, _ctx: &mut Ctx) -> i32 {
|
||||
fn foo(_ctx: &mut Ctx, a: i32) -> i32 {
|
||||
a
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
use crate::error::{CompileError, CompileResult};
|
||||
use crate::{memory::MemoryType, module::ModuleInner, structures::TypedIndex, units::Pages};
|
||||
use crate::{memory::MemoryType, module::ModuleInfo, module::ModuleInner, structures::TypedIndex, units::Pages};
|
||||
use std::{borrow::Cow, mem};
|
||||
|
||||
/// Represents a WebAssembly type.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Type {
|
||||
/// The `i32` type.
|
||||
I32,
|
||||
@ -30,12 +29,18 @@ impl Type {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a WebAssembly value.
|
||||
///
|
||||
/// 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)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub enum Value {
|
||||
/// The `i32` type.
|
||||
I32(i32),
|
||||
@ -180,15 +185,13 @@ impl ValueType for f64 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ElementType {
|
||||
/// Any wasm function.
|
||||
Anyfunc,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||
pub struct TableDescriptor {
|
||||
/// Type of data stored in this table.
|
||||
pub element: ElementType,
|
||||
@ -212,8 +215,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)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub enum Initializer {
|
||||
/// Corresponds to a `const.*` instruction.
|
||||
Const(Value),
|
||||
@ -221,24 +223,21 @@ pub enum Initializer {
|
||||
GetGlobal(ImportedGlobalIndex),
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct GlobalDescriptor {
|
||||
pub mutable: bool,
|
||||
pub ty: Type,
|
||||
}
|
||||
|
||||
/// A wasm global.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct GlobalInit {
|
||||
pub desc: GlobalDescriptor,
|
||||
pub init: Initializer,
|
||||
}
|
||||
|
||||
/// A wasm memory.
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct MemoryDescriptor {
|
||||
/// The minimum number of allowed pages.
|
||||
pub minimum: Pages,
|
||||
@ -270,8 +269,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)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FuncSig {
|
||||
params: Cow<'static, [Type]>,
|
||||
returns: Cow<'static, [Type]>,
|
||||
@ -307,6 +305,24 @@ impl FuncSig {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FuncSig {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let params = self
|
||||
.params
|
||||
.iter()
|
||||
.map(|p| p.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let returns = self
|
||||
.returns
|
||||
.iter()
|
||||
.map(|p| p.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
write!(f, "[{}] -> [{}]", params, returns)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LocalImport {
|
||||
type Local: TypedIndex;
|
||||
type Import: TypedIndex;
|
||||
@ -315,7 +331,7 @@ pub trait LocalImport {
|
||||
#[rustfmt::skip]
|
||||
macro_rules! define_map_index {
|
||||
($ty:ident) => {
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct $ty (u32);
|
||||
impl TypedIndex for $ty {
|
||||
@ -355,23 +371,23 @@ define_map_index![
|
||||
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.info.$imports.len() {
|
||||
pub fn local_or_import(self, info: &ModuleInfo) -> LocalOrImport<$ty> {
|
||||
if self.index() < info.$imports.len() {
|
||||
LocalOrImport::Import(<Self as LocalImport>::Import::new(self.index()))
|
||||
} else {
|
||||
LocalOrImport::Local(<Self as LocalImport>::Local::new(self.index() - module.info.$imports.len()))
|
||||
LocalOrImport::Local(<Self as LocalImport>::Local::new(self.index() - info.$imports.len()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $local_ty {
|
||||
pub fn convert_up(self, module: &ModuleInner) -> $ty {
|
||||
$ty ((self.index() + module.info.$imports.len()) as u32)
|
||||
pub fn convert_up(self, info: &ModuleInfo) -> $ty {
|
||||
$ty ((self.index() + info.$imports.len()) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl $imported_ty {
|
||||
pub fn convert_up(self, _module: &ModuleInner) -> $ty {
|
||||
pub fn convert_up(self, _info: &ModuleInfo) -> $ty {
|
||||
$ty (self.index() as u32)
|
||||
}
|
||||
}
|
||||
@ -391,8 +407,7 @@ define_local_or_import![
|
||||
(GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals),
|
||||
];
|
||||
|
||||
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SigIndex(u32);
|
||||
impl TypedIndex for SigIndex {
|
||||
#[doc(hidden)]
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::error::PageError;
|
||||
use std::{
|
||||
fmt,
|
||||
ops::{Add, Sub},
|
||||
@ -7,17 +8,20 @@ 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)]
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Pages(pub u32);
|
||||
|
||||
impl Pages {
|
||||
pub fn checked_add(self, rhs: Pages) -> Option<Pages> {
|
||||
pub fn checked_add(self, rhs: Pages) -> Result<Pages, PageError> {
|
||||
let added = (self.0 as usize) + (rhs.0 as usize);
|
||||
if added <= WASM_MAX_PAGES {
|
||||
Some(Pages(added as u32))
|
||||
Ok(Pages(added as u32))
|
||||
} else {
|
||||
None
|
||||
Err(PageError::ExceededMaxPages(
|
||||
self.0 as usize,
|
||||
rhs.0 as usize,
|
||||
added,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,8 +37,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)]
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Bytes(pub usize);
|
||||
|
||||
impl fmt::Debug for Bytes {
|
||||
|
@ -34,6 +34,14 @@ pub struct Ctx {
|
||||
/// A pointer to an array of imported functions, indexed by `FuncIndex`.
|
||||
pub imported_funcs: *mut ImportedFunc,
|
||||
|
||||
/// A pointer to an array of signature ids. Conceptually, this maps
|
||||
/// from a static, module-local signature id to a runtime-global
|
||||
/// signature id. This is used to allow call-indirect to other
|
||||
/// modules safely.
|
||||
pub(crate) dynamic_sigindices: *const SigId,
|
||||
|
||||
pub(crate) local_functions: *const *const Func,
|
||||
|
||||
local_backing: *mut LocalBacking,
|
||||
import_backing: *mut ImportBacking,
|
||||
module: *const ModuleInner,
|
||||
@ -59,6 +67,9 @@ impl Ctx {
|
||||
imported_globals: import_backing.vm_globals.as_mut_ptr(),
|
||||
imported_funcs: import_backing.vm_functions.as_mut_ptr(),
|
||||
|
||||
dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(),
|
||||
local_functions: local_backing.local_functions.as_ptr(),
|
||||
|
||||
local_backing,
|
||||
import_backing,
|
||||
module,
|
||||
@ -86,6 +97,9 @@ impl Ctx {
|
||||
imported_globals: import_backing.vm_globals.as_mut_ptr(),
|
||||
imported_funcs: import_backing.vm_functions.as_mut_ptr(),
|
||||
|
||||
dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(),
|
||||
local_functions: local_backing.local_functions.as_ptr(),
|
||||
|
||||
local_backing,
|
||||
import_backing,
|
||||
module,
|
||||
@ -116,7 +130,7 @@ impl Ctx {
|
||||
pub fn memory(&self, mem_index: u32) -> &Memory {
|
||||
let module = unsafe { &*self.module };
|
||||
let mem_index = MemoryIndex::new(mem_index as usize);
|
||||
match mem_index.local_or_import(module) {
|
||||
match mem_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_mem_index) => unsafe {
|
||||
let local_backing = &*self.local_backing;
|
||||
&local_backing.memories[local_mem_index]
|
||||
@ -163,12 +177,17 @@ impl Ctx {
|
||||
pub fn offset_signatures() -> u8 {
|
||||
7 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
pub fn offset_local_functions() -> u8 {
|
||||
8 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
}
|
||||
|
||||
enum InnerFunc {}
|
||||
/// Used to provide type safety (ish) for passing around function pointers.
|
||||
/// The typesystem ensures this cannot be dereferenced since an
|
||||
/// empty enum cannot actually exist.
|
||||
#[repr(C)]
|
||||
pub struct Func(InnerFunc);
|
||||
|
||||
/// An imported function, which contains the vmctx that owns this function.
|
||||
@ -354,6 +373,11 @@ mod vm_offset_tests {
|
||||
Ctx::offset_imported_funcs() as usize,
|
||||
offset_of!(Ctx => imported_funcs).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_local_functions() as usize,
|
||||
offset_of!(Ctx => local_functions).get_byte_offset(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -459,6 +483,9 @@ mod vm_ctx_tests {
|
||||
vm_memories: Map::new().into_boxed_map(),
|
||||
vm_tables: Map::new().into_boxed_map(),
|
||||
vm_globals: Map::new().into_boxed_map(),
|
||||
|
||||
dynamic_sigindices: Map::new().into_boxed_map(),
|
||||
local_functions: Map::new().into_boxed_map(),
|
||||
};
|
||||
let mut import_backing = ImportBacking {
|
||||
memories: Map::new().into_boxed_map(),
|
||||
@ -495,7 +522,10 @@ mod vm_ctx_tests {
|
||||
|
||||
fn generate_module() -> ModuleInner {
|
||||
use super::Func;
|
||||
use crate::backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper};
|
||||
use crate::backend::{
|
||||
sys::Memory, Backend, CacheGen, FuncResolver, ProtectedCaller, Token, UserTrapper,
|
||||
};
|
||||
use crate::cache::{Error as CacheError, WasmHash};
|
||||
use crate::error::RuntimeResult;
|
||||
use crate::types::{FuncIndex, LocalFuncIndex, Value};
|
||||
use hashbrown::HashMap;
|
||||
@ -526,10 +556,19 @@ mod vm_ctx_tests {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl CacheGen for Placeholder {
|
||||
fn generate_cache(
|
||||
&self,
|
||||
module: &ModuleInner,
|
||||
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), CacheError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
ModuleInner {
|
||||
func_resolver: Box::new(Placeholder),
|
||||
protected_caller: Box::new(Placeholder),
|
||||
cache_gen: Box::new(Placeholder),
|
||||
info: ModuleInfo {
|
||||
memories: Map::new(),
|
||||
globals: Map::new(),
|
||||
|
@ -13,23 +13,22 @@ use crate::{
|
||||
// +****************************+
|
||||
|
||||
pub unsafe extern "C" fn local_static_memory_grow(
|
||||
ctx: &mut vm::Ctx,
|
||||
memory_index: LocalMemoryIndex,
|
||||
delta: Pages,
|
||||
ctx: &mut vm::Ctx,
|
||||
) -> i32 {
|
||||
let local_memory = *ctx.memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut StaticMemory;
|
||||
|
||||
if let Some(old) = (*memory).grow(delta, &mut *local_memory) {
|
||||
old.0 as i32
|
||||
} else {
|
||||
-1
|
||||
match (*memory).grow(delta, &mut *local_memory) {
|
||||
Ok(old) => old.0 as i32,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn local_static_memory_size(
|
||||
memory_index: LocalMemoryIndex,
|
||||
ctx: &vm::Ctx,
|
||||
memory_index: LocalMemoryIndex,
|
||||
) -> Pages {
|
||||
let local_memory = *ctx.memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut StaticMemory;
|
||||
@ -38,23 +37,22 @@ pub unsafe extern "C" fn local_static_memory_size(
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn local_dynamic_memory_grow(
|
||||
ctx: &mut vm::Ctx,
|
||||
memory_index: LocalMemoryIndex,
|
||||
delta: Pages,
|
||||
ctx: &mut vm::Ctx,
|
||||
) -> i32 {
|
||||
let local_memory = *ctx.memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut DynamicMemory;
|
||||
|
||||
if let Some(old) = (*memory).grow(delta, &mut *local_memory) {
|
||||
old.0 as i32
|
||||
} else {
|
||||
-1
|
||||
match (*memory).grow(delta, &mut *local_memory) {
|
||||
Ok(old) => old.0 as i32,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn local_dynamic_memory_size(
|
||||
memory_index: LocalMemoryIndex,
|
||||
ctx: &vm::Ctx,
|
||||
memory_index: LocalMemoryIndex,
|
||||
) -> Pages {
|
||||
let local_memory = *ctx.memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut DynamicMemory;
|
||||
@ -67,23 +65,22 @@ pub unsafe extern "C" fn local_dynamic_memory_size(
|
||||
// +****************************+
|
||||
|
||||
pub unsafe extern "C" fn imported_static_memory_grow(
|
||||
ctx: &mut vm::Ctx,
|
||||
import_memory_index: ImportedMemoryIndex,
|
||||
delta: Pages,
|
||||
ctx: &mut vm::Ctx,
|
||||
) -> i32 {
|
||||
let local_memory = *ctx.imported_memories.add(import_memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut StaticMemory;
|
||||
|
||||
if let Some(old) = (*memory).grow(delta, &mut *local_memory) {
|
||||
old.0 as i32
|
||||
} else {
|
||||
-1
|
||||
match (*memory).grow(delta, &mut *local_memory) {
|
||||
Ok(old) => old.0 as i32,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn imported_static_memory_size(
|
||||
import_memory_index: ImportedMemoryIndex,
|
||||
ctx: &vm::Ctx,
|
||||
import_memory_index: ImportedMemoryIndex,
|
||||
) -> Pages {
|
||||
let local_memory = *ctx.imported_memories.add(import_memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut StaticMemory;
|
||||
@ -92,23 +89,22 @@ pub unsafe extern "C" fn imported_static_memory_size(
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn imported_dynamic_memory_grow(
|
||||
ctx: &mut vm::Ctx,
|
||||
memory_index: ImportedMemoryIndex,
|
||||
delta: Pages,
|
||||
ctx: &mut vm::Ctx,
|
||||
) -> i32 {
|
||||
let local_memory = *ctx.imported_memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut DynamicMemory;
|
||||
|
||||
if let Some(old) = (*memory).grow(delta, &mut *local_memory) {
|
||||
old.0 as i32
|
||||
} else {
|
||||
-1
|
||||
match (*memory).grow(delta, &mut *local_memory) {
|
||||
Ok(old) => old.0 as i32,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn imported_dynamic_memory_size(
|
||||
memory_index: ImportedMemoryIndex,
|
||||
ctx: &vm::Ctx,
|
||||
memory_index: ImportedMemoryIndex,
|
||||
) -> Pages {
|
||||
let local_memory = *ctx.imported_memories.add(memory_index.index());
|
||||
let memory = (*local_memory).memory as *mut DynamicMemory;
|
||||
@ -121,9 +117,9 @@ pub unsafe extern "C" fn imported_dynamic_memory_size(
|
||||
// +****************************+
|
||||
|
||||
pub unsafe extern "C" fn local_table_grow(
|
||||
ctx: &mut vm::Ctx,
|
||||
table_index: LocalTableIndex,
|
||||
delta: u32,
|
||||
ctx: &mut vm::Ctx,
|
||||
) -> i32 {
|
||||
let _ = table_index;
|
||||
let _ = delta;
|
||||
@ -131,7 +127,7 @@ pub unsafe extern "C" fn local_table_grow(
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn local_table_size(table_index: LocalTableIndex, ctx: &vm::Ctx) -> u32 {
|
||||
pub unsafe extern "C" fn local_table_size(ctx: &vm::Ctx, table_index: LocalTableIndex) -> u32 {
|
||||
let _ = table_index;
|
||||
let _ = ctx;
|
||||
unimplemented!()
|
||||
|
Reference in New Issue
Block a user