Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add no_std support #122

Merged
merged 18 commits into from Oct 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
16 changes: 13 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are those comments outdated?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. They match the readme section about no_std.

# 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 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion src/common/stack.rs
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -11,6 +14,7 @@ impl fmt::Display for Error {
}
}

#[cfg(feature = "std")]
impl error::Error for Error {
fn description(&self) -> &str {
&self.0
Expand Down
8 changes: 5 additions & 3 deletions src/func.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/global.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::rc::Rc;
use std::cell::Cell;
use alloc::rc::Rc;
use core::cell::Cell;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing we do in Cranelift is to remap core to std in the top-level lib.rs here so that most files can just use std::. This obviously isn't necessary, but since we have some crates that support no_std and some that don't, it's nice to the code more consistent between the two.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would have made rebasing a lot nicer, too 🙃

use value::RuntimeValue;
use Error;
use types::ValueType;
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/host.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::any::TypeId;
use core::any::TypeId;
use value::{RuntimeValue, FromRuntimeValue};
use {TrapKind, Trap};

Expand Down Expand Up @@ -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>()
Expand Down
7 changes: 7 additions & 0 deletions src/imports.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/isa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 27 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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.
Expand Down Expand Up @@ -138,6 +159,7 @@ impl fmt::Display for Trap {
}
}

#[cfg(feature = "std")]
impl error::Error for Trap {
fn description(&self) -> &str {
"runtime trap"
Expand Down Expand Up @@ -308,6 +330,7 @@ impl fmt::Display for Error {
}
}

#[cfg(feature = "std")]
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Expand Down Expand Up @@ -367,6 +390,7 @@ mod global;
mod func;
mod types;
mod isa;
pub mod nan_preserving_float;

#[cfg(test)]
mod tests;
Expand Down
28 changes: 15 additions & 13 deletions src/memory.rs
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

core imports might be grouped

use core::fmt;
use core::cell::{Cell, RefCell};
use parity_wasm::elements::ResizableLimits;
use Error;
use memory_units::{RoundUpTo, Pages, Bytes};
Expand All @@ -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
Expand Down Expand Up @@ -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"))
}

Expand Down Expand Up @@ -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(())
}
Expand Down Expand Up @@ -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)))?;
Expand All @@ -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)))?;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
15 changes: 11 additions & 4 deletions src/module.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand Down
Loading