Add no_std support (#122)
* add default-enabled std feature * use parity-wasm/std feature only if std is enabled * drop dependency on std::io * use hashmap_core instead of std::collections::HashMap * disable std::error in no_std * core and alloc all the things * mention no_std in readme * add no_std feature and use hashmap_core only on no_std * rename the no_std feature to core * drop dependency on byteorder/std * simplify float impl macro * remove some trailing whitespace * use libm for float math in no_std * add note about no_std panics of libm to readme * Embed nan-preserving-float crate. * Add no_std check to the Travis CI config * add missing dev-dependency
This commit is contained in:
parent
2f7505d120
commit
20154c5e24
|
@ -23,6 +23,8 @@ script:
|
|||
# Make sure nightly targets are not broken.
|
||||
- if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo check --tests --manifest-path=fuzz/Cargo.toml; fi
|
||||
- if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo check --benches --manifest-path=benches/Cargo.toml; fi
|
||||
# Make sure `no_std` version checks.
|
||||
- if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo +nightly check --no-default-features --features core; fi
|
||||
- ./test.sh
|
||||
- ./doc.sh
|
||||
after_success: |
|
||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -10,12 +10,22 @@ description = "WebAssembly interpreter"
|
|||
keywords = ["wasm", "webassembly", "bytecode", "interpreter"]
|
||||
exclude = [ "/res/*", "/tests/*", "/fuzz/*", "/benches/*" ]
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
# Disable for no_std support
|
||||
std = ["parity-wasm/std", "byteorder/std"]
|
||||
# Enable for no_std support
|
||||
# hashmap_core only works on no_std
|
||||
core = ["hashmap_core", "libm"]
|
||||
|
||||
[dependencies]
|
||||
parity-wasm = "0.31"
|
||||
byteorder = "1.0"
|
||||
parity-wasm = { version = "0.31", default-features = false }
|
||||
byteorder = { version = "1.0", default-features = false }
|
||||
hashmap_core = { version = "0.1.9", optional = true }
|
||||
memory_units = "0.3.0"
|
||||
nan-preserving-float = "0.1.0"
|
||||
libm = { version = "0.1.2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.1"
|
||||
rand = "0.4.2"
|
||||
wabt = "0.6"
|
||||
|
|
18
README.md
18
README.md
|
@ -27,6 +27,24 @@ cargo build
|
|||
cargo test
|
||||
```
|
||||
|
||||
# `no_std` support
|
||||
This crate supports `no_std` environments.
|
||||
Enable the `core` feature and disable default features:
|
||||
```toml
|
||||
[dependencies]
|
||||
parity-wasm = {
|
||||
version = "0.31",
|
||||
default-features = false,
|
||||
features = "core"
|
||||
}
|
||||
```
|
||||
|
||||
The `core` feature requires the `core` and `alloc` libraries and a nightly compiler.
|
||||
Also, code related to `std::error` is disabled.
|
||||
|
||||
Floating point operations in `no_std` use [`libm`](https://crates.io/crates/libm), which sometimes panics in debug mode (https://github.com/japaric/libm/issues/4).
|
||||
So make sure to either use release builds or avoid WASM with floating point operations, for example by using [`deny_floating_point`](https://docs.rs/wasmi/0.4.0/wasmi/struct.Module.html#method.deny_floating_point).
|
||||
|
||||
## Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error(String);
|
||||
|
@ -11,6 +14,7 @@ impl fmt::Display for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
&self.0
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::rc::{Rc, Weak};
|
||||
use std::fmt;
|
||||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use alloc::rc::{Rc, Weak};
|
||||
use core::fmt;
|
||||
use parity_wasm::elements::Local;
|
||||
use {Trap, TrapKind, Signature};
|
||||
use host::Externals;
|
||||
|
@ -17,7 +19,7 @@ use isa;
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct FuncRef(Rc<FuncInstance>);
|
||||
|
||||
impl ::std::ops::Deref for FuncRef {
|
||||
impl ::core::ops::Deref for FuncRef {
|
||||
type Target = FuncInstance;
|
||||
fn deref(&self) -> &FuncInstance {
|
||||
&self.0
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::rc::Rc;
|
||||
use std::cell::Cell;
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::Cell;
|
||||
use value::RuntimeValue;
|
||||
use Error;
|
||||
use types::ValueType;
|
||||
|
@ -13,7 +13,7 @@ use parity_wasm::elements::{ValueType as EValueType};
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct GlobalRef(Rc<GlobalInstance>);
|
||||
|
||||
impl ::std::ops::Deref for GlobalRef {
|
||||
impl ::core::ops::Deref for GlobalRef {
|
||||
type Target = GlobalInstance;
|
||||
fn deref(&self) -> &GlobalInstance {
|
||||
&self.0
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::any::TypeId;
|
||||
use core::any::TypeId;
|
||||
use value::{RuntimeValue, FromRuntimeValue};
|
||||
use {TrapKind, Trap};
|
||||
|
||||
|
@ -98,7 +98,7 @@ impl<'a> RuntimeArgs<'a> {
|
|||
/// _ => panic!(),
|
||||
/// }
|
||||
/// ```
|
||||
pub trait HostError: 'static + ::std::fmt::Display + ::std::fmt::Debug + Send + Sync {
|
||||
pub trait HostError: 'static + ::core::fmt::Display + ::core::fmt::Debug + Send + Sync {
|
||||
#[doc(hidden)]
|
||||
fn __private_get_type_id__(&self) -> TypeId {
|
||||
TypeId::of::<Self>()
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashmap_core::HashMap;
|
||||
|
||||
use global::GlobalRef;
|
||||
use memory::MemoryRef;
|
||||
use func::FuncRef;
|
||||
|
|
|
@ -67,6 +67,9 @@
|
|||
//! - Reserved immediates are ignored for `call_indirect`, `current_memory`, `grow_memory`.
|
||||
//!
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
||||
/// Should we keep a value before "discarding" a stack frame?
|
||||
///
|
||||
/// Note that this is a `enum` since Wasm doesn't support multiple return
|
||||
|
|
30
src/lib.rs
30
src/lib.rs
|
@ -96,6 +96,21 @@
|
|||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
//// alloc is required in no_std
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
#[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 wabt;
|
||||
#[cfg(test)]
|
||||
|
@ -104,13 +119,19 @@ extern crate assert_matches;
|
|||
|
||||
extern crate parity_wasm;
|
||||
extern crate byteorder;
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate hashmap_core;
|
||||
extern crate memory_units as memory_units_crate;
|
||||
|
||||
pub extern crate nan_preserving_float;
|
||||
|
||||
use std::fmt;
|
||||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use core::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate libm;
|
||||
|
||||
/// Error type which can be thrown by wasm code or by host environment.
|
||||
///
|
||||
/// Under some conditions, wasm execution may produce a `Trap`, which immediately aborts execution.
|
||||
|
@ -138,6 +159,7 @@ impl fmt::Display for Trap {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl error::Error for Trap {
|
||||
fn description(&self) -> &str {
|
||||
"runtime trap"
|
||||
|
@ -308,6 +330,7 @@ impl fmt::Display for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
|
@ -367,6 +390,7 @@ mod global;
|
|||
mod func;
|
||||
mod types;
|
||||
mod isa;
|
||||
pub mod nan_preserving_float;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use std::u32;
|
||||
use std::ops::Range;
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use std::cell::{Cell, RefCell};
|
||||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use alloc::rc::Rc;
|
||||
use core::u32;
|
||||
use core::ops::Range;
|
||||
use core::cmp;
|
||||
use core::fmt;
|
||||
use core::cell::{Cell, RefCell};
|
||||
use parity_wasm::elements::ResizableLimits;
|
||||
use Error;
|
||||
use memory_units::{RoundUpTo, Pages, Bytes};
|
||||
|
@ -28,7 +30,7 @@ const LINEAR_MEMORY_MAX_PAGES: Pages = Pages(65536);
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct MemoryRef(Rc<MemoryInstance>);
|
||||
|
||||
impl ::std::ops::Deref for MemoryRef {
|
||||
impl ::core::ops::Deref for MemoryRef {
|
||||
type Target = MemoryInstance;
|
||||
fn deref(&self) -> &MemoryInstance {
|
||||
&self.0
|
||||
|
@ -172,7 +174,7 @@ impl MemoryInstance {
|
|||
/// Get value from memory at given offset.
|
||||
pub fn get_value<T: LittleEndianConvert>(&self, offset: u32) -> Result<T, Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let region = self.checked_region(&mut buffer, offset as usize, ::std::mem::size_of::<T>())?;
|
||||
let region = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?;
|
||||
Ok(T::from_little_endian(&buffer[region.range()]).expect("Slice size is checked"))
|
||||
}
|
||||
|
||||
|
@ -216,7 +218,7 @@ impl MemoryInstance {
|
|||
/// Copy value in the memory at given offset.
|
||||
pub fn set_value<T: LittleEndianConvert>(&self, offset: u32, value: T) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let range = self.checked_region(&mut buffer, offset as usize, ::std::mem::size_of::<T>())?.range();
|
||||
let range = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?.range();
|
||||
value.into_little_endian(&mut buffer[range]);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -254,7 +256,7 @@ impl MemoryInstance {
|
|||
}
|
||||
|
||||
fn checked_region<B>(&self, buffer: &mut B, offset: usize, size: usize) -> Result<CheckedRegion, Error>
|
||||
where B: ::std::ops::DerefMut<Target=Vec<u8>>
|
||||
where B: ::core::ops::DerefMut<Target=Vec<u8>>
|
||||
{
|
||||
let end = offset.checked_add(size)
|
||||
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?;
|
||||
|
@ -275,7 +277,7 @@ impl MemoryInstance {
|
|||
|
||||
fn checked_region_pair<B>(&self, buffer: &mut B, offset1: usize, size1: usize, offset2: usize, size2: usize)
|
||||
-> Result<(CheckedRegion, CheckedRegion), Error>
|
||||
where B: ::std::ops::DerefMut<Target=Vec<u8>>
|
||||
where B: ::core::ops::DerefMut<Target=Vec<u8>>
|
||||
{
|
||||
let end1 = offset1.checked_add(size1)
|
||||
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size1, offset1)))?;
|
||||
|
@ -314,7 +316,7 @@ impl MemoryInstance {
|
|||
|
||||
let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||
|
||||
unsafe { ::std::ptr::copy(
|
||||
unsafe { ::core::ptr::copy(
|
||||
buffer[read_region.range()].as_ptr(),
|
||||
buffer[write_region.range()].as_mut_ptr(),
|
||||
len,
|
||||
|
@ -343,7 +345,7 @@ impl MemoryInstance {
|
|||
return Err(Error::Memory(format!("non-overlapping copy is used for overlapping regions")))
|
||||
}
|
||||
|
||||
unsafe { ::std::ptr::copy_nonoverlapping(
|
||||
unsafe { ::core::ptr::copy_nonoverlapping(
|
||||
buffer[read_region.range()].as_ptr(),
|
||||
buffer[write_region.range()].as_mut_ptr(),
|
||||
len,
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use alloc::rc::Rc;
|
||||
use runner::check_function_args;
|
||||
use Trap;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use core::cell::RefCell;
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashmap_core::HashMap;
|
||||
|
||||
use parity_wasm::elements::{External, InitExpr, Internal, Instruction, ResizableLimits, Type};
|
||||
use {Module, Error, Signature, MemoryInstance, RuntimeValue, TableInstance};
|
||||
use imports::ImportResolver;
|
||||
|
@ -32,7 +39,7 @@ use memory_units::Pages;
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct ModuleRef(pub(crate) Rc<ModuleInstance>);
|
||||
|
||||
impl ::std::ops::Deref for ModuleRef {
|
||||
impl ::core::ops::Deref for ModuleRef {
|
||||
type Target = ModuleInstance;
|
||||
fn deref(&self) -> &ModuleInstance {
|
||||
&self.0
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use libm::{F32Ext, F64Ext};
|
||||
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub, Rem};
|
||||
use core::cmp::{Ordering, PartialEq, PartialOrd};
|
||||
|
||||
macro_rules! impl_binop {
|
||||
($for:ident, $is:ident, $op:ident, $func_name:ident) => {
|
||||
impl<T: Into<$for>> $op<T> for $for {
|
||||
type Output = Self;
|
||||
|
||||
fn $func_name(self, other: T) -> Self {
|
||||
$for(
|
||||
$op::$func_name(
|
||||
$is::from_bits(self.0),
|
||||
$is::from_bits(other.into().0)
|
||||
).to_bits()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! float {
|
||||
($for:ident, $rep:ident, $is:ident) => {
|
||||
float!($for, $rep, $is, 1 << (::core::mem::size_of::<$is>() * 8 - 1));
|
||||
};
|
||||
($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => {
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct $for($rep);
|
||||
|
||||
impl_binop!($for, $is, Add, add);
|
||||
impl_binop!($for, $is, Sub, sub);
|
||||
impl_binop!($for, $is, Mul, mul);
|
||||
impl_binop!($for, $is, Div, div);
|
||||
impl_binop!($for, $is, Rem, rem);
|
||||
|
||||
impl $for {
|
||||
pub fn from_bits(other: $rep) -> Self {
|
||||
$for(other)
|
||||
}
|
||||
|
||||
pub fn to_bits(self) -> $rep {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn from_float(fl: $is) -> Self {
|
||||
fl.into()
|
||||
}
|
||||
|
||||
pub fn to_float(self) -> $is {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn is_nan(self) -> bool {
|
||||
self.to_float().is_nan()
|
||||
}
|
||||
|
||||
pub fn abs(self) -> Self {
|
||||
$for(self.0 & !$sign_bit)
|
||||
}
|
||||
|
||||
pub fn fract(self) -> Self {
|
||||
self.to_float().fract().into()
|
||||
}
|
||||
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
Self::from(self.to_float().min(other.to_float()))
|
||||
}
|
||||
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
Self::from(self.to_float().max(other.to_float()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$is> for $for {
|
||||
fn from(other: $is) -> $for {
|
||||
$for(other.to_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$for> for $is {
|
||||
fn from(other: $for) -> $is {
|
||||
<$is>::from_bits(other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for $for {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self {
|
||||
$for(self.0 ^ $sign_bit)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<$for> + Copy> PartialEq<T> for $for {
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
$is::from(*self) == $is::from((*other).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<$for> + Copy> PartialOrd<T> for $for {
|
||||
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
|
||||
$is::from(*self).partial_cmp(&$is::from((*other).into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::fmt::Debug for $for {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
$is::from(*self).fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float!(F32, u32, f32);
|
||||
float!(F64, u64, f64);
|
||||
|
||||
impl From<u32> for F32 {
|
||||
fn from(other: u32) -> Self {
|
||||
Self::from_bits(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<F32> for u32 {
|
||||
fn from(other: F32) -> Self {
|
||||
other.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for F64 {
|
||||
fn from(other: u64) -> Self {
|
||||
Self::from_bits(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<F64> for u64 {
|
||||
fn from(other: F64) -> Self {
|
||||
other.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate rand;
|
||||
|
||||
use self::rand::Rng;
|
||||
|
||||
use super::{F32, F64};
|
||||
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::fmt::Debug;
|
||||
use core::iter;
|
||||
|
||||
fn test_ops<T, F, I>(iter: I)
|
||||
where
|
||||
T: Add<Output = T>
|
||||
+ Div<Output = T>
|
||||
+ Mul<Output = T>
|
||||
+ Sub<Output = T>
|
||||
+ Neg<Output = T>
|
||||
+ Copy
|
||||
+ Debug
|
||||
+ PartialEq,
|
||||
F: Into<T>
|
||||
+ Add<Output = F>
|
||||
+ Div<Output = F>
|
||||
+ Mul<Output = F>
|
||||
+ Sub<Output = F>
|
||||
+ Neg<Output = F>
|
||||
+ Copy
|
||||
+ Debug,
|
||||
I: IntoIterator<Item = (F, F)>,
|
||||
{
|
||||
for (a, b) in iter {
|
||||
assert_eq!((a + b).into(), a.into() + b.into());
|
||||
assert_eq!((a - b).into(), a.into() - b.into());
|
||||
assert_eq!((a * b).into(), a.into() * b.into());
|
||||
assert_eq!((a / b).into(), a.into() / b.into());
|
||||
assert_eq!((-a).into(), -a.into());
|
||||
assert_eq!((-b).into(), -b.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_f32() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let iter = iter::repeat(()).map(|_| rng.gen());
|
||||
|
||||
test_ops::<F32, f32, _>(iter.take(1000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_f64() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let iter = iter::repeat(()).map(|_| rng.gen());
|
||||
|
||||
test_ops::<F64, f64, _>(iter.take(1000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neg_nan_f32() {
|
||||
assert_eq!((-F32(0xff80_3210)).0, 0x7f80_3210);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neg_nan_f64() {
|
||||
assert_eq!((-F64(0xff80_3210_0000_0000)).0, 0x7f80_3210_0000_0000);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
use std::ops;
|
||||
use std::{u32, usize};
|
||||
use std::fmt;
|
||||
use std::iter::repeat;
|
||||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use core::ops;
|
||||
use core::{u32, usize};
|
||||
use core::fmt;
|
||||
use core::iter::repeat;
|
||||
use parity_wasm::elements::Local;
|
||||
use {Error, Trap, TrapKind, Signature};
|
||||
use module::ModuleRef;
|
||||
|
@ -19,7 +21,7 @@ use nan_preserving_float::{F32, F64};
|
|||
use isa;
|
||||
|
||||
/// Maximum number of entries in value stack.
|
||||
pub const DEFAULT_VALUE_STACK_LIMIT: usize = (1024 * 1024) / ::std::mem::size_of::<RuntimeValue>();
|
||||
pub const DEFAULT_VALUE_STACK_LIMIT: usize = (1024 * 1024) / ::core::mem::size_of::<RuntimeValue>();
|
||||
|
||||
// TODO: Make these parameters changeble.
|
||||
pub const DEFAULT_CALL_STACK_LIMIT: usize = 64 * 1024;
|
||||
|
@ -122,7 +124,7 @@ impl Interpreter {
|
|||
}
|
||||
|
||||
pub fn resume_execution<'a, E: Externals + 'a>(&mut self, return_val: Option<RuntimeValue>, externals: &'a mut E) -> Result<Option<RuntimeValue>, Trap> {
|
||||
use std::mem::swap;
|
||||
use core::mem::swap;
|
||||
|
||||
// Ensure that the VM is resumable. This is checked in `FuncInvocation::resume_execution`.
|
||||
assert!(self.state.is_resumable());
|
||||
|
|
12
src/table.rs
12
src/table.rs
|
@ -1,7 +1,9 @@
|
|||
use std::u32;
|
||||
use std::fmt;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use alloc::rc::Rc;
|
||||
use core::u32;
|
||||
use core::fmt;
|
||||
use core::cell::RefCell;
|
||||
use parity_wasm::elements::ResizableLimits;
|
||||
use Error;
|
||||
use func::FuncRef;
|
||||
|
@ -16,7 +18,7 @@ use module::check_limits;
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct TableRef(Rc<TableInstance>);
|
||||
|
||||
impl ::std::ops::Deref for TableRef {
|
||||
impl ::core::ops::Deref for TableRef {
|
||||
type Target = TableInstance;
|
||||
fn deref(&self) -> &TableInstance {
|
||||
&self.0
|
||||
|
|
|
@ -12,8 +12,8 @@ struct HostErrorWithCode {
|
|||
error_code: u32,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for HostErrorWithCode {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
||||
impl ::core::fmt::Display for HostErrorWithCode {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> Result<(), ::core::fmt::Error> {
|
||||
write!(f, "{}", self.error_code)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,10 @@ fn assert_error_properties() {
|
|||
fn unsigned_to_runtime_value() {
|
||||
use super::RuntimeValue;
|
||||
|
||||
let overflow_i32: u32 = ::std::i32::MAX as u32 + 1;
|
||||
let overflow_i32: u32 = ::core::i32::MAX as u32 + 1;
|
||||
assert_eq!(RuntimeValue::from(overflow_i32).try_into::<u32>().unwrap(), overflow_i32);
|
||||
|
||||
let overflow_i64: u64 = ::std::i64::MAX as u64 + 1;
|
||||
let overflow_i64: u64 = ::core::i64::MAX as u64 + 1;
|
||||
assert_eq!(RuntimeValue::from(overflow_i64).try_into::<u64>().unwrap(), overflow_i64);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use alloc::borrow::Cow;
|
||||
|
||||
use parity_wasm::elements::{
|
||||
FunctionType, ValueType as EValueType, GlobalType, TableType, MemoryType};
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use parity_wasm::elements::{MemoryType, TableType, GlobalType, BlockType, ValueType, FunctionType};
|
||||
use validation::Error;
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use std::u32;
|
||||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use core::u32;
|
||||
use parity_wasm::elements::{Instruction, BlockType, ValueType, TableElementType, Func, FuncBody};
|
||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||
use validation::context::ModuleContext;
|
||||
|
@ -1708,7 +1710,7 @@ impl Sink {
|
|||
}
|
||||
|
||||
fn emit_br_table(&mut self, targets: &[Target], default: Target) {
|
||||
use std::iter;
|
||||
use core::iter;
|
||||
|
||||
let pc = self.cur_pc();
|
||||
let mut isa_targets = Vec::new();
|
||||
|
@ -1739,7 +1741,7 @@ impl Sink {
|
|||
///
|
||||
/// Panics if the label is already resolved.
|
||||
fn resolve_label(&mut self, label: LabelId) {
|
||||
use std::mem;
|
||||
use core::mem;
|
||||
|
||||
if let (Label::Resolved(_), _) = self.labels[label.0] {
|
||||
panic!("Trying to resolve already resolved label");
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashSet;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashmap_core::HashSet;
|
||||
|
||||
use parity_wasm::elements::{
|
||||
BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, Module, Instruction,
|
||||
ResizableLimits, TableType, ValueType, InitExpr, Type,
|
||||
|
@ -27,6 +35,7 @@ impl fmt::Display for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
&self.0
|
||||
|
@ -45,7 +54,7 @@ pub struct ValidatedModule {
|
|||
pub module: Module,
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for ValidatedModule {
|
||||
impl ::core::ops::Deref for ValidatedModule {
|
||||
type Target = Module;
|
||||
fn deref(&self) -> &Module {
|
||||
&self.module
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use parity_wasm::elements::{Local, ValueType};
|
||||
use validation::Error;
|
||||
|
||||
|
|
157
src/value.rs
157
src/value.rs
|
@ -1,7 +1,6 @@
|
|||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use nan_preserving_float::{F32, F64};
|
||||
use std::io;
|
||||
use std::{f32, i32, i64, u32, u64};
|
||||
use core::{f32, i32, i64, u32, u64};
|
||||
use TrapKind;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -261,7 +260,7 @@ impl FromRuntimeValue for bool {
|
|||
}
|
||||
|
||||
/// This conversion assumes that `i8` is represented as an [`I32`].
|
||||
///
|
||||
///
|
||||
/// [`I32`]: enum.RuntimeValue.html#variant.I32
|
||||
impl FromRuntimeValue for i8 {
|
||||
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
|
||||
|
@ -275,7 +274,7 @@ impl FromRuntimeValue for i8 {
|
|||
}
|
||||
|
||||
/// This conversion assumes that `i16` is represented as an [`I32`].
|
||||
///
|
||||
///
|
||||
/// [`I32`]: enum.RuntimeValue.html#variant.I32
|
||||
impl FromRuntimeValue for i16 {
|
||||
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
|
||||
|
@ -289,7 +288,7 @@ impl FromRuntimeValue for i16 {
|
|||
}
|
||||
|
||||
/// This conversion assumes that `u8` is represented as an [`I32`].
|
||||
///
|
||||
///
|
||||
/// [`I32`]: enum.RuntimeValue.html#variant.I32
|
||||
impl FromRuntimeValue for u8 {
|
||||
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
|
||||
|
@ -303,7 +302,7 @@ impl FromRuntimeValue for u8 {
|
|||
}
|
||||
|
||||
/// This conversion assumes that `u16` is represented as an [`I32`].
|
||||
///
|
||||
///
|
||||
/// [`I32`]: enum.RuntimeValue.html#variant.I32
|
||||
impl FromRuntimeValue for u16 {
|
||||
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
|
||||
|
@ -578,88 +577,86 @@ impl LittleEndianConvert for u8 {
|
|||
}
|
||||
|
||||
impl LittleEndianConvert for i16 {
|
||||
fn into_little_endian(self, mut buffer: &mut[u8]) {
|
||||
buffer.write_i16::<LittleEndian>(self)
|
||||
.expect("i16 is written without any errors");
|
||||
fn into_little_endian(self, buffer: &mut[u8]) {
|
||||
LittleEndian::write_i16(buffer, self);
|
||||
}
|
||||
|
||||
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
||||
io::Cursor::new(buffer).read_i16::<LittleEndian>()
|
||||
.map_err(|_| Error::InvalidLittleEndianBuffer)
|
||||
buffer.get(0..2)
|
||||
.map(LittleEndian::read_i16)
|
||||
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl LittleEndianConvert for u16 {
|
||||
fn into_little_endian(self, mut buffer: &mut[u8]) {
|
||||
buffer.write_u16::<LittleEndian>(self)
|
||||
.expect("u16 is written without any errors");
|
||||
fn into_little_endian(self, buffer: &mut[u8]) {
|
||||
LittleEndian::write_u16(buffer, self);
|
||||
}
|
||||
|
||||
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
||||
io::Cursor::new(buffer).read_u16::<LittleEndian>()
|
||||
.map_err(|_| Error::InvalidLittleEndianBuffer)
|
||||
buffer.get(0..2)
|
||||
.map(LittleEndian::read_u16)
|
||||
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl LittleEndianConvert for i32 {
|
||||
fn into_little_endian(self, mut buffer: &mut[u8]) {
|
||||
buffer.write_i32::<LittleEndian>(self)
|
||||
.expect("i32 is written without any errors");
|
||||
fn into_little_endian(self, buffer: &mut[u8]) {
|
||||
LittleEndian::write_i32(buffer, self);
|
||||
}
|
||||
|
||||
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
||||
io::Cursor::new(buffer).read_i32::<LittleEndian>()
|
||||
.map_err(|_| Error::InvalidLittleEndianBuffer)
|
||||
buffer.get(0..4)
|
||||
.map(LittleEndian::read_i32)
|
||||
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl LittleEndianConvert for u32 {
|
||||
fn into_little_endian(self, mut buffer: &mut[u8]) {
|
||||
buffer.write_u32::<LittleEndian>(self)
|
||||
.expect("u32 is written without any errors");
|
||||
fn into_little_endian(self, buffer: &mut[u8]) {
|
||||
LittleEndian::write_u32(buffer, self);
|
||||
}
|
||||
|
||||
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
||||
io::Cursor::new(buffer).read_u32::<LittleEndian>()
|
||||
.map_err(|_| Error::InvalidLittleEndianBuffer)
|
||||
buffer.get(0..4)
|
||||
.map(LittleEndian::read_u32)
|
||||
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl LittleEndianConvert for i64 {
|
||||
fn into_little_endian(self, mut buffer: &mut[u8]) {
|
||||
buffer.write_i64::<LittleEndian>(self)
|
||||
.expect("i64 is written without any errors");
|
||||
fn into_little_endian(self, buffer: &mut[u8]) {
|
||||
LittleEndian::write_i64(buffer, self);
|
||||
}
|
||||
|
||||
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
||||
io::Cursor::new(buffer).read_i64::<LittleEndian>()
|
||||
.map_err(|_| Error::InvalidLittleEndianBuffer)
|
||||
buffer.get(0..8)
|
||||
.map(LittleEndian::read_i64)
|
||||
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl LittleEndianConvert for f32 {
|
||||
fn into_little_endian(self, mut buffer: &mut[u8]) {
|
||||
buffer.write_f32::<LittleEndian>(self)
|
||||
.expect("f32 is written without any errors");
|
||||
fn into_little_endian(self, buffer: &mut[u8]) {
|
||||
LittleEndian::write_f32(buffer, self);
|
||||
}
|
||||
|
||||
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
||||
io::Cursor::new(buffer).read_u32::<LittleEndian>()
|
||||
.map(f32::from_bits)
|
||||
.map_err(|_| Error::InvalidLittleEndianBuffer)
|
||||
buffer.get(0..4)
|
||||
.map(LittleEndian::read_f32)
|
||||
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl LittleEndianConvert for f64 {
|
||||
fn into_little_endian(self, mut buffer: &mut[u8]) {
|
||||
buffer.write_f64::<LittleEndian>(self)
|
||||
.expect("i64 is written without any errors");
|
||||
fn into_little_endian(self, buffer: &mut[u8]) {
|
||||
LittleEndian::write_f64(buffer, self);
|
||||
}
|
||||
|
||||
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
||||
io::Cursor::new(buffer).read_u64::<LittleEndian>()
|
||||
.map(f64::from_bits)
|
||||
.map_err(|_| Error::InvalidLittleEndianBuffer)
|
||||
buffer.get(0..8)
|
||||
.map(LittleEndian::read_f64)
|
||||
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -748,34 +745,52 @@ impl_integer!(u32);
|
|||
impl_integer!(i64);
|
||||
impl_integer!(u64);
|
||||
|
||||
macro_rules! impl_float {
|
||||
($type:ident, $int_type:ident) => {
|
||||
impl_float!($type, $type, $int_type);
|
||||
// Use std float functions in std environment.
|
||||
// And libm's implementation in no_std
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! call_math {
|
||||
($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => {
|
||||
$fXX::$op($e)
|
||||
};
|
||||
($type:ident, $intermediate:ident, $int_type:ident) => {
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
macro_rules! call_math {
|
||||
($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => {
|
||||
::libm::$FXXExt::$op($e)
|
||||
};
|
||||
}
|
||||
|
||||
// We cannot call the math functions directly, because there are multiple available implementaitons in no_std.
|
||||
// In std, there are only `Value::$op` and `std::$fXX:$op`.
|
||||
// The `std` ones are preferred, because they are not from a trait.
|
||||
// For `no_std`, the implementations are `Value::$op` and `libm::FXXExt::$op`,
|
||||
// both of which are trait implementations and hence ambiguous.
|
||||
// So we have to use a full path, which is what `call_math!` does.
|
||||
macro_rules! impl_float {
|
||||
($type:ident, $fXX:ident, $FXXExt:ident, $iXX:ident) => {
|
||||
impl Float<$type> for $type {
|
||||
fn abs(self) -> $type {
|
||||
$intermediate::abs(self.into()).into()
|
||||
call_math!(abs, $fXX::from(self), $fXX, $FXXExt).into()
|
||||
}
|
||||
fn floor(self) -> $type {
|
||||
$intermediate::floor(self.into()).into()
|
||||
call_math!(floor, $fXX::from(self), $fXX, $FXXExt).into()
|
||||
}
|
||||
fn ceil(self) -> $type {
|
||||
$intermediate::ceil(self.into()).into()
|
||||
call_math!(ceil, $fXX::from(self), $fXX, $FXXExt).into()
|
||||
}
|
||||
fn trunc(self) -> $type {
|
||||
$intermediate::trunc(self.into()).into()
|
||||
call_math!(trunc, $fXX::from(self), $fXX, $FXXExt).into()
|
||||
}
|
||||
fn round(self) -> $type {
|
||||
$intermediate::round(self.into()).into()
|
||||
call_math!(round, $fXX::from(self), $fXX, $FXXExt).into()
|
||||
}
|
||||
fn nearest(self) -> $type {
|
||||
let round = self.round();
|
||||
if self.fract().abs() != 0.5 {
|
||||
if call_math!(fract, $fXX::from(self), $fXX, $FXXExt).abs() != 0.5 {
|
||||
return round;
|
||||
}
|
||||
|
||||
use std::ops::Rem;
|
||||
use core::ops::Rem;
|
||||
if round.rem(2.0) == 1.0 {
|
||||
self.floor()
|
||||
} else if round.rem(2.0) == -1.0 {
|
||||
|
@ -785,34 +800,40 @@ macro_rules! impl_float {
|
|||
}
|
||||
}
|
||||
fn sqrt(self) -> $type {
|
||||
$intermediate::sqrt(self.into()).into()
|
||||
call_math!(sqrt, $fXX::from(self), $fXX, $FXXExt).into()
|
||||
}
|
||||
// This instruction corresponds to what is sometimes called "minNaN" in other languages.
|
||||
fn min(self, other: $type) -> $type {
|
||||
if self.is_nan() || other.is_nan() {
|
||||
return ::std::$intermediate::NAN.into();
|
||||
if self.is_nan() {
|
||||
return self;
|
||||
}
|
||||
if other.is_nan() {
|
||||
return other;
|
||||
}
|
||||
|
||||
self.min(other)
|
||||
}
|
||||
// This instruction corresponds to what is sometimes called "maxNaN" in other languages.
|
||||
fn max(self, other: $type) -> $type {
|
||||
if self.is_nan() || other.is_nan() {
|
||||
return ::std::$intermediate::NAN.into();
|
||||
if self.is_nan() {
|
||||
return self;
|
||||
}
|
||||
if other.is_nan() {
|
||||
return other;
|
||||
}
|
||||
|
||||
self.max(other)
|
||||
}
|
||||
fn copysign(self, other: $type) -> $type {
|
||||
use std::mem::size_of;
|
||||
use core::mem::size_of;
|
||||
|
||||
if self.is_nan() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let sign_mask: $int_type = 1 << ((size_of::<$int_type>() << 3) - 1);
|
||||
let self_int: $int_type = self.transmute_into();
|
||||
let other_int: $int_type = other.transmute_into();
|
||||
let sign_mask: $iXX = 1 << ((size_of::<$iXX>() << 3) - 1);
|
||||
let self_int: $iXX = self.transmute_into();
|
||||
let other_int: $iXX = other.transmute_into();
|
||||
let is_self_sign_set = (self_int & sign_mask) != 0;
|
||||
let is_other_sign_set = (other_int & sign_mask) != 0;
|
||||
if is_self_sign_set == is_other_sign_set {
|
||||
|
@ -827,7 +848,7 @@ macro_rules! impl_float {
|
|||
};
|
||||
}
|
||||
|
||||
impl_float!(f32, i32);
|
||||
impl_float!(f64, i64);
|
||||
impl_float!(F32, f32, i32);
|
||||
impl_float!(F64, f64, i64);
|
||||
impl_float!(f32, f32, F32Ext, i32);
|
||||
impl_float!(f64, f64, F64Ext, i64);
|
||||
impl_float!(F32, f32, F32Ext, i32);
|
||||
impl_float!(F64, f64, F64Ext, i64);
|
||||
|
|
Loading…
Reference in New Issue