Merge upstream/master into feature/llvm-feature

This commit is contained in:
Lachlan Sneff
2019-02-22 14:14:18 -08:00
273 changed files with 4556 additions and 834 deletions

View File

@ -6,9 +6,9 @@ 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,
};
@ -19,8 +19,7 @@ pub mod 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,
LLVM,
@ -44,15 +43,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
@ -100,3 +91,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

@ -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 sha2::{Digest, Sha256};
use std::{io, mem, slice};
#[derive(Debug)]
pub enum InvalidFileType {
@ -26,23 +21,80 @@ 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]);
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 array = [0u8; 32];
array.copy_from_slice(Sha256::digest(wasm).as_slice());
WasmHash(array)
}
/// Create the hexadecimal representation of the
/// stored hash.
pub fn encode(self) -> String {
hex::encode(self.0)
}
pub(crate) fn into_array(self) -> [u8; 32] {
self.0
}
}
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 +110,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 +164,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;
type StoreError;
fn load(&self, key: WasmHash) -> Result<Module, Self::LoadError>;
fn store(&mut self, key: WasmHash, module: Module) -> Result<(), Self::StoreError>;
}

View File

@ -1,6 +1,8 @@
use crate::types::{
FuncSig, GlobalDescriptor, MemoryDescriptor, MemoryIndex, TableDescriptor, TableIndex, Type,
};
use core::borrow::Borrow;
use std::sync::Arc;
pub type Result<T> = std::result::Result<T, Error>;
pub type CompileResult<T> = std::result::Result<T, CompileError>;
@ -26,6 +28,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.
///
@ -74,6 +89,31 @@ 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.
///
@ -110,6 +150,39 @@ impl PartialEq for RuntimeError {
}
}
impl std::fmt::Display for RuntimeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
RuntimeError::IndirectCallSignature { table } => write!(
f,
"Indirect call signature error with Table Index \"{:?}\"",
table
),
RuntimeError::IndirectCallToNull { table } => {
write!(f, "Indirect call to null with table index \"{:?}\"", table)
}
RuntimeError::IllegalArithmeticOperation => write!(f, "Illegal arithmetic operation"),
RuntimeError::OutOfBoundsAccess { memory, addr } => match addr {
Some(addr) => write!(
f,
"Out-of-bounds access with memory index {:?} and address {}",
memory, addr
),
None => write!(f, "Out-of-bounds access with memory index {:?}", memory),
},
RuntimeError::TableOutOfBounds { table } => {
write!(f, "Table out of bounds with table index \"{:?}\"", table)
}
RuntimeError::Unknown { msg } => {
write!(f, "Unknown runtime error with message: \"{}\"", msg)
}
RuntimeError::User { msg } => write!(f, "User runtime error with message: \"{}\"", msg),
}
}
}
impl std::error::Error for RuntimeError {}
/// This error type is produced by resolving a wasm function
/// given its name.
///
@ -127,6 +200,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.
///
@ -146,12 +244,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 {
@ -160,6 +270,22 @@ 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.
@ -228,3 +354,11 @@ 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 {
write!(f, "{}", self)
}
}
impl std::error::Error for Error {}

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

@ -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 {
@ -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,6 +1,7 @@
use crate::{
backend::{Backend, FuncResolver, ProtectedCaller},
error::Result,
cache::{Artifact, Error as CacheError, WasmHash},
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>,
@ -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

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

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

@ -205,8 +205,28 @@ 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,

View File

@ -156,8 +156,28 @@ 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,

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

@ -50,6 +50,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,
@ -140,3 +148,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

@ -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 ),* ) {
@ -234,18 +234,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

View File

@ -7,8 +7,7 @@ use crate::{
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,
@ -20,12 +19,17 @@ pub enum Type {
F64,
}
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),
@ -170,15 +174,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,
@ -202,8 +204,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),
@ -211,24 +212,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,
@ -260,8 +258,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]>,
@ -297,6 +294,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;
@ -305,7 +320,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 {
@ -361,7 +376,7 @@ macro_rules! define_local_or_import {
}
impl $imported_ty {
pub fn convert_up(self, _module: &ModuleInfo) -> $ty {
pub fn convert_up(self, _info: &ModuleInfo) -> $ty {
$ty (self.index() as u32)
}
}
@ -381,8 +396,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

@ -7,8 +7,7 @@ const WASM_PAGE_SIZE: usize = 65_536;
const WASM_MAX_PAGES: usize = 65_536;
/// Units of WebAssembly pages (as specified to be 65,536 bytes).
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Pages(pub u32);
impl Pages {
@ -33,8 +32,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

@ -494,7 +494,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;
@ -525,10 +528,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(),