Skip to content

Commit 20154c5

Browse files
Julius Rakowpepyakin
Julius Rakow
authored andcommitted
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
1 parent 2f7505d commit 20154c5

23 files changed

+452
-121
lines changed

Diff for: .travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ script:
2323
# Make sure nightly targets are not broken.
2424
- if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo check --tests --manifest-path=fuzz/Cargo.toml; fi
2525
- if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo check --benches --manifest-path=benches/Cargo.toml; fi
26+
# Make sure `no_std` version checks.
27+
- if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo +nightly check --no-default-features --features core; fi
2628
- ./test.sh
2729
- ./doc.sh
2830
after_success: |

Diff for: Cargo.toml

+13-3
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,22 @@ description = "WebAssembly interpreter"
1010
keywords = ["wasm", "webassembly", "bytecode", "interpreter"]
1111
exclude = [ "/res/*", "/tests/*", "/fuzz/*", "/benches/*" ]
1212

13+
[features]
14+
default = ["std"]
15+
# Disable for no_std support
16+
std = ["parity-wasm/std", "byteorder/std"]
17+
# Enable for no_std support
18+
# hashmap_core only works on no_std
19+
core = ["hashmap_core", "libm"]
20+
1321
[dependencies]
14-
parity-wasm = "0.31"
15-
byteorder = "1.0"
22+
parity-wasm = { version = "0.31", default-features = false }
23+
byteorder = { version = "1.0", default-features = false }
24+
hashmap_core = { version = "0.1.9", optional = true }
1625
memory_units = "0.3.0"
17-
nan-preserving-float = "0.1.0"
26+
libm = { version = "0.1.2", optional = true }
1827

1928
[dev-dependencies]
2029
assert_matches = "1.1"
30+
rand = "0.4.2"
2131
wabt = "0.6"

Diff for: README.md

+18
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,24 @@ cargo build
2727
cargo test
2828
```
2929

30+
# `no_std` support
31+
This crate supports `no_std` environments.
32+
Enable the `core` feature and disable default features:
33+
```toml
34+
[dependencies]
35+
parity-wasm = {
36+
version = "0.31",
37+
default-features = false,
38+
features = "core"
39+
}
40+
```
41+
42+
The `core` feature requires the `core` and `alloc` libraries and a nightly compiler.
43+
Also, code related to `std::error` is disabled.
44+
45+
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).
46+
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).
47+
3048
## Contribution
3149

3250
Unless you explicitly state otherwise, any contribution intentionally submitted

Diff for: src/common/stack.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
#[allow(unused_imports)]
2+
use alloc::prelude::*;
13

4+
#[cfg(feature = "std")]
25
use std::error;
3-
use std::fmt;
6+
use core::fmt;
47

58
#[derive(Debug)]
69
pub struct Error(String);
@@ -11,6 +14,7 @@ impl fmt::Display for Error {
1114
}
1215
}
1316

17+
#[cfg(feature = "std")]
1418
impl error::Error for Error {
1519
fn description(&self) -> &str {
1620
&self.0

Diff for: src/func.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use std::rc::{Rc, Weak};
2-
use std::fmt;
1+
#[allow(unused_imports)]
2+
use alloc::prelude::*;
3+
use alloc::rc::{Rc, Weak};
4+
use core::fmt;
35
use parity_wasm::elements::Local;
46
use {Trap, TrapKind, Signature};
57
use host::Externals;
@@ -17,7 +19,7 @@ use isa;
1719
#[derive(Clone, Debug)]
1820
pub struct FuncRef(Rc<FuncInstance>);
1921

20-
impl ::std::ops::Deref for FuncRef {
22+
impl ::core::ops::Deref for FuncRef {
2123
type Target = FuncInstance;
2224
fn deref(&self) -> &FuncInstance {
2325
&self.0

Diff for: src/global.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use std::rc::Rc;
2-
use std::cell::Cell;
1+
use alloc::rc::Rc;
2+
use core::cell::Cell;
33
use value::RuntimeValue;
44
use Error;
55
use types::ValueType;
@@ -13,7 +13,7 @@ use parity_wasm::elements::{ValueType as EValueType};
1313
#[derive(Clone, Debug)]
1414
pub struct GlobalRef(Rc<GlobalInstance>);
1515

16-
impl ::std::ops::Deref for GlobalRef {
16+
impl ::core::ops::Deref for GlobalRef {
1717
type Target = GlobalInstance;
1818
fn deref(&self) -> &GlobalInstance {
1919
&self.0

Diff for: src/host.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::any::TypeId;
1+
use core::any::TypeId;
22
use value::{RuntimeValue, FromRuntimeValue};
33
use {TrapKind, Trap};
44

@@ -98,7 +98,7 @@ impl<'a> RuntimeArgs<'a> {
9898
/// _ => panic!(),
9999
/// }
100100
/// ```
101-
pub trait HostError: 'static + ::std::fmt::Display + ::std::fmt::Debug + Send + Sync {
101+
pub trait HostError: 'static + ::core::fmt::Display + ::core::fmt::Debug + Send + Sync {
102102
#[doc(hidden)]
103103
fn __private_get_type_id__(&self) -> TypeId {
104104
TypeId::of::<Self>()

Diff for: src/imports.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1+
#[allow(unused_imports)]
2+
use alloc::prelude::*;
3+
4+
#[cfg(feature = "std")]
15
use std::collections::HashMap;
6+
#[cfg(not(feature = "std"))]
7+
use hashmap_core::HashMap;
8+
29
use global::GlobalRef;
310
use memory::MemoryRef;
411
use func::FuncRef;

Diff for: src/isa.rs

+3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
//! - Reserved immediates are ignored for `call_indirect`, `current_memory`, `grow_memory`.
6868
//!
6969
70+
#[allow(unused_imports)]
71+
use alloc::prelude::*;
72+
7073
/// Should we keep a value before "discarding" a stack frame?
7174
///
7275
/// Note that this is a `enum` since Wasm doesn't support multiple return

Diff for: src/lib.rs

+27-3
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,21 @@
9696
9797
#![warn(missing_docs)]
9898

99+
#![cfg_attr(not(feature = "std"), no_std)]
100+
101+
//// alloc is required in no_std
102+
#![cfg_attr(not(feature = "std"), feature(alloc))]
103+
104+
#[cfg(not(feature = "std"))]
105+
#[macro_use]
106+
extern crate alloc;
107+
#[cfg(feature = "std")]
108+
extern crate std as alloc;
109+
110+
#[cfg(feature = "std")]
111+
#[macro_use]
112+
extern crate core;
113+
99114
#[cfg(test)]
100115
extern crate wabt;
101116
#[cfg(test)]
@@ -104,13 +119,19 @@ extern crate assert_matches;
104119

105120
extern crate parity_wasm;
106121
extern crate byteorder;
122+
#[cfg(not(feature = "std"))]
123+
extern crate hashmap_core;
107124
extern crate memory_units as memory_units_crate;
108125

109-
pub extern crate nan_preserving_float;
110-
111-
use std::fmt;
126+
#[allow(unused_imports)]
127+
use alloc::prelude::*;
128+
use core::fmt;
129+
#[cfg(feature = "std")]
112130
use std::error;
113131

132+
#[cfg(not(feature = "std"))]
133+
extern crate libm;
134+
114135
/// Error type which can be thrown by wasm code or by host environment.
115136
///
116137
/// Under some conditions, wasm execution may produce a `Trap`, which immediately aborts execution.
@@ -138,6 +159,7 @@ impl fmt::Display for Trap {
138159
}
139160
}
140161

162+
#[cfg(feature = "std")]
141163
impl error::Error for Trap {
142164
fn description(&self) -> &str {
143165
"runtime trap"
@@ -308,6 +330,7 @@ impl fmt::Display for Error {
308330
}
309331
}
310332

333+
#[cfg(feature = "std")]
311334
impl error::Error for Error {
312335
fn description(&self) -> &str {
313336
match *self {
@@ -367,6 +390,7 @@ mod global;
367390
mod func;
368391
mod types;
369392
mod isa;
393+
pub mod nan_preserving_float;
370394

371395
#[cfg(test)]
372396
mod tests;

Diff for: src/memory.rs

+15-13
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use std::u32;
2-
use std::ops::Range;
3-
use std::cmp;
4-
use std::fmt;
5-
use std::rc::Rc;
6-
use std::cell::{Cell, RefCell};
1+
#[allow(unused_imports)]
2+
use alloc::prelude::*;
3+
use alloc::rc::Rc;
4+
use core::u32;
5+
use core::ops::Range;
6+
use core::cmp;
7+
use core::fmt;
8+
use core::cell::{Cell, RefCell};
79
use parity_wasm::elements::ResizableLimits;
810
use Error;
911
use memory_units::{RoundUpTo, Pages, Bytes};
@@ -28,7 +30,7 @@ const LINEAR_MEMORY_MAX_PAGES: Pages = Pages(65536);
2830
#[derive(Clone, Debug)]
2931
pub struct MemoryRef(Rc<MemoryInstance>);
3032

31-
impl ::std::ops::Deref for MemoryRef {
33+
impl ::core::ops::Deref for MemoryRef {
3234
type Target = MemoryInstance;
3335
fn deref(&self) -> &MemoryInstance {
3436
&self.0
@@ -172,7 +174,7 @@ impl MemoryInstance {
172174
/// Get value from memory at given offset.
173175
pub fn get_value<T: LittleEndianConvert>(&self, offset: u32) -> Result<T, Error> {
174176
let mut buffer = self.buffer.borrow_mut();
175-
let region = self.checked_region(&mut buffer, offset as usize, ::std::mem::size_of::<T>())?;
177+
let region = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?;
176178
Ok(T::from_little_endian(&buffer[region.range()]).expect("Slice size is checked"))
177179
}
178180

@@ -216,7 +218,7 @@ impl MemoryInstance {
216218
/// Copy value in the memory at given offset.
217219
pub fn set_value<T: LittleEndianConvert>(&self, offset: u32, value: T) -> Result<(), Error> {
218220
let mut buffer = self.buffer.borrow_mut();
219-
let range = self.checked_region(&mut buffer, offset as usize, ::std::mem::size_of::<T>())?.range();
221+
let range = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?.range();
220222
value.into_little_endian(&mut buffer[range]);
221223
Ok(())
222224
}
@@ -254,7 +256,7 @@ impl MemoryInstance {
254256
}
255257

256258
fn checked_region<B>(&self, buffer: &mut B, offset: usize, size: usize) -> Result<CheckedRegion, Error>
257-
where B: ::std::ops::DerefMut<Target=Vec<u8>>
259+
where B: ::core::ops::DerefMut<Target=Vec<u8>>
258260
{
259261
let end = offset.checked_add(size)
260262
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?;
@@ -275,7 +277,7 @@ impl MemoryInstance {
275277

276278
fn checked_region_pair<B>(&self, buffer: &mut B, offset1: usize, size1: usize, offset2: usize, size2: usize)
277279
-> Result<(CheckedRegion, CheckedRegion), Error>
278-
where B: ::std::ops::DerefMut<Target=Vec<u8>>
280+
where B: ::core::ops::DerefMut<Target=Vec<u8>>
279281
{
280282
let end1 = offset1.checked_add(size1)
281283
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size1, offset1)))?;
@@ -314,7 +316,7 @@ impl MemoryInstance {
314316

315317
let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
316318

317-
unsafe { ::std::ptr::copy(
319+
unsafe { ::core::ptr::copy(
318320
buffer[read_region.range()].as_ptr(),
319321
buffer[write_region.range()].as_mut_ptr(),
320322
len,
@@ -343,7 +345,7 @@ impl MemoryInstance {
343345
return Err(Error::Memory(format!("non-overlapping copy is used for overlapping regions")))
344346
}
345347

346-
unsafe { ::std::ptr::copy_nonoverlapping(
348+
unsafe { ::core::ptr::copy_nonoverlapping(
347349
buffer[read_region.range()].as_ptr(),
348350
buffer[write_region.range()].as_mut_ptr(),
349351
len,

Diff for: src/module.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
#[allow(unused_imports)]
2+
use alloc::prelude::*;
3+
use alloc::rc::Rc;
14
use runner::check_function_args;
25
use Trap;
3-
use std::rc::Rc;
4-
use std::cell::RefCell;
5-
use std::fmt;
6+
use core::cell::RefCell;
7+
use core::fmt;
8+
9+
#[cfg(feature = "std")]
610
use std::collections::HashMap;
11+
#[cfg(not(feature = "std"))]
12+
use hashmap_core::HashMap;
13+
714
use parity_wasm::elements::{External, InitExpr, Internal, Instruction, ResizableLimits, Type};
815
use {Module, Error, Signature, MemoryInstance, RuntimeValue, TableInstance};
916
use imports::ImportResolver;
@@ -32,7 +39,7 @@ use memory_units::Pages;
3239
#[derive(Clone, Debug)]
3340
pub struct ModuleRef(pub(crate) Rc<ModuleInstance>);
3441

35-
impl ::std::ops::Deref for ModuleRef {
42+
impl ::core::ops::Deref for ModuleRef {
3643
type Target = ModuleInstance;
3744
fn deref(&self) -> &ModuleInstance {
3845
&self.0

0 commit comments

Comments
 (0)