#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::len_without_is_empty)]
#![allow(clippy::new_ret_no_self)]
#[cfg(not(feature = "std"))]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std as alloc;
#[cfg(feature = "std")]
#[macro_use]
extern crate core;
#[cfg(test)]
extern crate assert_matches;
#[cfg(test)]
extern crate wabt;
use alloc::{
boxed::Box,
string::{String, ToString},
vec::Vec,
};
use core::fmt;
#[cfg(feature = "std")]
use std::error;
#[cfg(not(feature = "std"))]
extern crate libm;
extern crate num_rational;
extern crate num_traits;
#[derive(Debug)]
pub struct Trap {
kind: TrapKind,
wasm_trace: Vec<String>,
}
impl Trap {
pub fn new(kind: TrapKind) -> Trap {
Trap {
kind,
wasm_trace: vec![],
}
}
pub fn wasm_trace_with(mut self, info: Option<&(usize, String)>) -> Trap {
if let Some(info) = info {
self.wasm_trace.push(format!(
"{:#}[{}]",
rustc_demangle::demangle(&info.1),
info.0
));
}
self
}
pub fn wasm_trace_with_kind(kind: TrapKind, info: Option<&(usize, String)>) -> Trap {
let mut trap: Self = kind.into();
if let Some(info) = info {
trap.wasm_trace.push(format!(
"{:#}[{}]",
rustc_demangle::demangle(&info.1),
info.0
));
}
trap
}
pub fn wasm_trace(&self) -> &Vec<String> {
&self.wasm_trace
}
pub fn set_wasm_trace(mut self, trace: Vec<String>) -> Self {
self.wasm_trace = trace;
self
}
pub fn kind(&self) -> &TrapKind {
&self.kind
}
pub fn into_kind(self) -> TrapKind {
self.kind
}
}
impl fmt::Display for Trap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Trap: {:?} \n \
WASM Trace: {:?}",
self.kind, self.wasm_trace,
)
}
}
#[cfg(feature = "std")]
impl error::Error for Trap {
fn description(&self) -> &str {
"runtime trap"
}
}
#[derive(Debug)]
pub enum TrapKind {
Unreachable,
MemoryAccessOutOfBounds,
TableAccessOutOfBounds,
ElemUninitialized,
DivisionByZero,
InvalidConversionToInt,
StackOverflow,
UnexpectedSignature,
Host(Box<dyn host::HostError>),
}
impl TrapKind {
pub fn is_host(&self) -> bool {
matches!(self, TrapKind::Host(_))
}
}
#[derive(Debug)]
pub enum Error {
Validation(String),
Instantiation(String),
Function(String),
Table(String),
Memory(String),
Global(String),
Value(String),
Trap(Trap),
Host(Box<dyn host::HostError>),
}
impl Error {
pub fn as_host_error(&self) -> Option<&dyn host::HostError> {
match self {
Error::Host(host_err) => Some(&**host_err),
Error::Trap(Trap {
kind: TrapKind::Host(host_err),
..
}) => Some(&**host_err),
_ => None,
}
}
pub fn into_host_error(self) -> Option<Box<dyn host::HostError>> {
match self {
Error::Host(host_err) => Some(host_err),
Error::Trap(Trap {
kind: TrapKind::Host(host_err),
..
}) => Some(host_err),
_ => None,
}
}
pub fn try_into_host_error(self) -> Result<Box<dyn host::HostError>, Self> {
match self {
Error::Host(host_err) => Ok(host_err),
Error::Trap(Trap {
kind: TrapKind::Host(host_err),
..
}) => Ok(host_err),
other => Err(other),
}
}
}
#[allow(clippy::from_over_into)]
impl Into<String> for Error {
fn into(self) -> String {
match self {
Error::Validation(s) => s,
Error::Instantiation(s) => s,
Error::Function(s) => s,
Error::Table(s) => s,
Error::Memory(s) => s,
Error::Global(s) => s,
Error::Value(s) => s,
Error::Trap(s) => format!("trap: {:?}", s),
Error::Host(e) => format!("user: {}", e),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Validation(ref s) => write!(f, "Validation: {}", s),
Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s),
Error::Function(ref s) => write!(f, "Function: {}", s),
Error::Table(ref s) => write!(f, "Table: {}", s),
Error::Memory(ref s) => write!(f, "Memory: {}", s),
Error::Global(ref s) => write!(f, "Global: {}", s),
Error::Value(ref s) => write!(f, "Value: {}", s),
Error::Trap(ref s) => write!(f, "Trap: {:?}", s),
Error::Host(ref e) => write!(f, "User: {}", e),
}
}
}
#[cfg(feature = "std")]
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Validation(ref s) => s,
Error::Instantiation(ref s) => s,
Error::Function(ref s) => s,
Error::Table(ref s) => s,
Error::Memory(ref s) => s,
Error::Global(ref s) => s,
Error::Value(ref s) => s,
Error::Trap(_) => "Trap",
Error::Host(_) => "Host error",
}
}
}
impl<U> From<U> for Error
where
U: host::HostError + Sized,
{
fn from(e: U) -> Self {
Error::Host(Box::new(e))
}
}
impl<U> From<U> for Trap
where
U: host::HostError + Sized,
{
fn from(e: U) -> Self {
Trap::new(TrapKind::Host(Box::new(e)))
}
}
impl From<Trap> for Error {
fn from(e: Trap) -> Error {
Error::Trap(e)
}
}
impl From<TrapKind> for Trap {
fn from(e: TrapKind) -> Trap {
Trap::new(e)
}
}
impl From<validation::Error> for Error {
fn from(e: validation::Error) -> Error {
Error::Validation(e.to_string())
}
}
mod func;
mod global;
mod host;
mod imports;
mod isa;
mod memory;
mod module;
pub mod nan_preserving_float;
mod prepare;
mod runner;
mod table;
mod types;
mod value;
#[cfg(test)]
mod tests;
pub use self::func::{FuncInstance, FuncInvocation, FuncRef, ResumableError};
pub use self::global::{GlobalInstance, GlobalRef};
pub use self::host::{Externals, HostError, NopExternals, RuntimeArgs};
pub use self::imports::{ImportResolver, ImportsBuilder, ModuleImportResolver};
pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
pub use self::module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef};
pub use self::runner::{StackRecycler, DEFAULT_CALL_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
pub use self::table::{TableInstance, TableRef};
pub use self::types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor, ValueType};
pub use self::value::{Error as ValueError, FromRuntimeValue, LittleEndianConvert, RuntimeValue};
pub mod memory_units {
pub use memory_units::wasm32::*;
pub use memory_units::{size_of, ByteSize, Bytes, RoundUpTo};
}
pub struct Module {
code_map: Vec<isa::Instructions>,
module: parity_wasm::elements::Module,
}
impl Module {
pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> {
let prepare::CompiledModule { code_map, module } = prepare::compile_module(module)?;
Ok(Module { code_map, module })
}
pub fn deny_floating_point(&self) -> Result<(), Error> {
prepare::deny_floating_point(&self.module).map_err(Into::into)
}
pub fn from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<Module, Error> {
let module = parity_wasm::elements::deserialize_buffer(buffer.as_ref())
.map_err(|e: parity_wasm::elements::Error| Error::Validation(e.to_string()))?;
Module::from_parity_wasm_module(module)
}
pub fn parse_names(mut self) -> Result<Module, Error> {
self.module = self
.module
.parse_names()
.map_err(|_| Error::Instantiation("Failed to parse name sections".into()))?;
Ok(self)
}
pub fn try_parse_names(mut self) -> Module {
self.module = match self.module.parse_names() {
Ok(module) => module,
Err((_, module)) => module,
};
self
}
pub(crate) fn name_map(&self) -> Option<&parity_wasm::elements::NameMap> {
Some(self.module().names_section()?.functions()?.names())
}
pub(crate) fn module(&self) -> &parity_wasm::elements::Module {
&self.module
}
pub(crate) fn code(&self) -> &Vec<isa::Instructions> {
&self.code_map
}
}