Merge branch 'master' into feature/dynasm-backend

This commit is contained in:
Brandon Fish
2019-03-12 20:58:22 -05:00
358 changed files with 13992 additions and 1805 deletions

View File

@ -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"]

View File

@ -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>;
}

View File

@ -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(),
});
}
}

View File

@ -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>;
}

View File

@ -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)
}
}

View File

@ -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),
}
}
}

View File

@ -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

View File

@ -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();

View File

@ -38,7 +38,7 @@ macro_rules! func {
/// },
/// };
///
/// fn foo(n: i32, _: &mut Ctx) -> i32 {
/// fn foo(_: &mut Ctx, n: i32) -> i32 {
/// n
/// }
/// ```

View File

@ -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] {

View File

@ -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));
}
}

View File

@ -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] {

View File

@ -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 {

View File

@ -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
}
}
}

View File

@ -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>

View File

@ -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

View File

@ -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,
}
}

View File

@ -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,

View File

@ -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],

View File

@ -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);
}
}

View File

@ -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
}

View File

@ -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)]

View File

@ -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 {

View File

@ -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(),

View File

@ -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!()