Wasm function can only trap (#29)

* Introduce Trap struct.

* get_local can't fail.

* Add MemoryOutOfBounds trap.

* from_little_endian use slice instead of vec.

* MemoryAccessOutOfBounds for mem get and set.

* from_little_endian conversion can't fail.

* call_indirect traps.

* DivisionByZero and InvalidConversionToInt

* Use traps in value to convey an error

* select: int condition on stack top

* if: int condition on stack top

* Assert pops.

* Another protions of assert pops

* Introduce ValueStack

Also, hide FunctionContext and remove some stale code

* Traps in execution

* Make it compile.

* Check args before invoke.

* Document RuntimeArgs.

* Update host.rs

* Add rustdoc for Trap.
This commit is contained in:
Sergey Pepyakin 2018-02-01 14:59:21 +03:00 committed by Nikolay Volf
parent 1d87fb09dc
commit c96735d6d6
11 changed files with 764 additions and 567 deletions

View File

@ -8,7 +8,7 @@ use wasmi::{
Error as InterpreterError, ModuleInstance, ModuleRef,
Externals, RuntimeValue, FuncRef, ModuleImportResolver,
FuncInstance, HostError, ImportsBuilder, Signature, ValueType,
RuntimeArgs,
RuntimeArgs, Trap,
};
#[derive(Debug)]
@ -149,15 +149,15 @@ impl<'a> Externals for Runtime<'a> {
&mut self,
index: usize,
args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, InterpreterError> {
) -> Result<Option<RuntimeValue>, Trap> {
match index {
SET_FUNC_INDEX => {
let idx: i32 = args.nth(0)?;
let idx: i32 = args.nth(0);
self.game.set(idx, self.player)?;
Ok(None)
}
GET_FUNC_INDEX => {
let idx: i32 = args.nth(0)?;
let idx: i32 = args.nth(0);
let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?);
Ok(Some(val.into()))
}

View File

@ -13,13 +13,14 @@ use wasmi::{
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder,
MemoryInstance, MemoryRef, ModuleImportResolver, ModuleInstance,
ModuleRef, RuntimeValue, TableInstance, TableRef, ValueType,
Module, Signature, MemoryDescriptor,
Module, Signature, MemoryDescriptor, Trap,
TableDescriptor, GlobalDescriptor, FuncInstance, RuntimeArgs,
};
#[derive(Debug)]
enum Error {
Load(String),
Start(Trap),
Interpreter(InterpreterError),
}
@ -58,7 +59,7 @@ impl Externals for SpecModule {
&mut self,
index: usize,
args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, InterpreterError> {
) -> Result<Option<RuntimeValue>, Trap> {
match index {
PRINT_FUNC_INDEX => {
println!("print: {:?}", args);
@ -243,7 +244,9 @@ fn try_load(
) -> Result<(), Error> {
let module = try_load_module(base_dir, module_path)?;
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?;
instance.run_start(spec_driver.spec_module())?;
instance
.run_start(spec_driver.spec_module())
.map_err(|trap| Error::Start(trap))?;
Ok(())
}

View File

@ -28,13 +28,6 @@ pub struct StackWithLimit<T> where T: Clone {
}
impl<T> StackWithLimit<T> where T: Clone {
pub fn with_data<D: IntoIterator<Item=T>>(data: D, limit: usize) -> Self {
StackWithLimit {
values: data.into_iter().collect(),
limit: limit
}
}
pub fn with_limit(limit: usize) -> Self {
StackWithLimit {
values: VecDeque::new(),

View File

@ -2,13 +2,11 @@ use std::rc::{Rc, Weak};
use std::fmt;
use std::collections::HashMap;
use parity_wasm::elements::{Local, Opcodes};
use {Error, Signature};
use {Trap, Signature};
use host::Externals;
use runner::{prepare_function_args, FunctionContext, Interpreter};
use runner::{check_function_args, Interpreter};
use value::RuntimeValue;
use module::ModuleInstance;
use common::stack::StackWithLimit;
use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
/// Reference to a function (See [`FuncInstance`] for details).
///
@ -134,37 +132,16 @@ impl FuncInstance {
func: &FuncRef,
args: &[RuntimeValue],
externals: &mut E,
) -> Result<Option<RuntimeValue>, Error> {
enum InvokeKind<'a> {
Internal(FunctionContext),
Host(usize, &'a [RuntimeValue]),
}
let result = match *func.as_internal() {
FuncInstanceInternal::Internal { ref signature, .. } => {
let mut stack =
StackWithLimit::with_data(args.into_iter().cloned(), DEFAULT_VALUE_STACK_LIMIT);
let args = prepare_function_args(signature, &mut stack)?;
let context = FunctionContext::new(
func.clone(),
DEFAULT_VALUE_STACK_LIMIT,
DEFAULT_FRAME_STACK_LIMIT,
signature,
args,
);
InvokeKind::Internal(context)
) -> Result<Option<RuntimeValue>, Trap> {
debug_assert!(check_function_args(func.signature(), &args).is_ok());
match *func.as_internal() {
FuncInstanceInternal::Internal { .. } => {
let mut interpreter = Interpreter::new(externals);
interpreter.start_execution(func, args)
}
FuncInstanceInternal::Host { ref host_func_index, .. } => {
InvokeKind::Host(*host_func_index, &*args)
externals.invoke_index(*host_func_index, args.into())
}
};
match result {
InvokeKind::Internal(ctx) => {
let mut interpreter = Interpreter::new(externals);
interpreter.run_function(ctx)
}
InvokeKind::Host(host_func, args) => externals.invoke_index(host_func, args.into()),
}
}
}

View File

@ -1,6 +1,6 @@
use std::any::TypeId;
use value::{RuntimeValue, TryInto};
use Error;
use {Error, Trap};
/// Safe wrapper for list of arguments.
#[derive(Debug)]
@ -13,19 +13,37 @@ impl<'a> From<&'a [RuntimeValue]> for RuntimeArgs<'a> {
}
impl<'a> RuntimeArgs<'a> {
/// Extract argument by index `idx` returning error if cast is invalid or not enough arguments
pub fn nth<T>(&self, idx: usize) -> Result<T, Error> where RuntimeValue: TryInto<T, Error> {
Ok(self.nth_value(idx)?.try_into().map_err(|_| Error::Value("Invalid argument cast".to_owned()))?)
/// Extract argument by index `idx`.
///
/// # Errors
///
/// Returns `Err` if cast is invalid or not enough arguments.
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Error> where RuntimeValue: TryInto<T, Error> {
Ok(self.nth_value_checked(idx)?.try_into().map_err(|_| Error::Value("Invalid argument cast".to_owned()))?)
}
/// Extract argument as a runtime value by index `idx` returning error is not enough arguments
pub fn nth_value(&self, idx: usize) -> Result<RuntimeValue, Error> {
/// Extract argument as a [`RuntimeValue`] by index `idx`.
///
/// # Errors
///
/// Returns `Err` if this list has not enough arguments.
pub fn nth_value_checked(&self, idx: usize) -> Result<RuntimeValue, Error> {
if self.0.len() <= idx {
return Err(Error::Value("Invalid argument index".to_owned()));
}
Ok(self.0[idx])
}
/// Extract argument by index `idx`.
///
/// # Panics
///
/// Panics if cast is invalid or not enough arguments.
pub fn nth<T>(&self, idx: usize) -> T where RuntimeValue: TryInto<T, Error> {
let value = self.nth_value_checked(idx).expect("Invalid argument index");
value.try_into().expect("Unexpected argument type")
}
/// Total number of arguments
pub fn len(&self) -> usize {
self.0.len()
@ -104,7 +122,7 @@ impl HostError {
/// ```rust
/// use wasmi::{
/// Externals, RuntimeValue, RuntimeArgs, Error, ModuleImportResolver,
/// FuncRef, ValueType, Signature, FuncInstance
/// FuncRef, ValueType, Signature, FuncInstance, Trap,
/// };
///
/// struct HostExternals {
@ -118,11 +136,11 @@ impl HostError {
/// &mut self,
/// index: usize,
/// args: RuntimeArgs,
/// ) -> Result<Option<RuntimeValue>, Error> {
/// ) -> Result<Option<RuntimeValue>, Trap> {
/// match index {
/// ADD_FUNC_INDEX => {
/// let a: u32 = args.nth(0).unwrap();
/// let b: u32 = args.nth(1).unwrap();
/// let a: u32 = args.nth(0);
/// let b: u32 = args.nth(1);
/// let result = a + b;
///
/// Ok(Some(RuntimeValue::I32(result as i32)))
@ -173,7 +191,7 @@ pub trait Externals {
&mut self,
index: usize,
args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Error>;
) -> Result<Option<RuntimeValue>, Trap>;
}
/// Implementation of [`Externals`] that just traps on [`invoke_index`].
@ -187,8 +205,8 @@ impl Externals for NopExternals {
&mut self,
_index: usize,
_args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Error> {
Err(Error::Trap("invoke index on no-op externals".into()))
) -> Result<Option<RuntimeValue>, Trap> {
Err(Trap::Unreachable)
}
}
@ -201,14 +219,14 @@ mod tests {
#[test]
fn i32_runtime_args() {
let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into();
let val: i32 = args.nth(0).unwrap();
let val: i32 = args.nth_checked(0).unwrap();
assert_eq!(val, 0);
}
#[test]
fn i64_invalid_arg_cast() {
let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into();
assert!(args.nth::<i32>(0).is_err());
assert!(args.nth_checked::<i32>(0).is_err());
}
// Tests that `HostError` trait is object safe.

View File

@ -106,6 +106,78 @@ use std::fmt;
use std::error;
use std::collections::HashMap;
/// Error type which can happen at execution time.
///
/// Under some conditions, wasm execution may produce a `Trap`, which immediately aborts execution.
/// Traps can't be handled by WebAssembly code, but are reported to the embedder.
#[derive(Debug)]
pub enum Trap {
/// Wasm code executed `unreachable` opcode.
///
/// `unreachable` is a special opcode which always traps upon execution.
/// This opcode have a similar purpose as `ud2` in x86.
Unreachable,
/// Attempt to load or store at the address which
/// lies outside of bounds of the memory.
///
/// Since addresses are interpreted as unsigned integers, out of bounds access
/// can't happen with negative addresses (i.e. they will always wrap).
MemoryAccessOutOfBounds,
/// Attempt to access table element at index which
/// lies outside of bounds.
///
/// This typically can happen when `call_indirect` is executed
/// with index that lies out of bounds.
///
/// Since indexes are interpreted as unsinged integers, out of bounds access
/// can't happen with negative indexes (i.e. they will always wrap).
TableAccessOutOfBounds,
/// Attempt to access table element which is uninitialized (i.e. `None`).
///
/// This typically can happen when `call_indirect` is executed.
ElemUninitialized,
/// Attempt to `call_indirect` function with mismatched [signature][`Signature`].
///
/// `call_indirect` always specifies the expected signature of function.
/// If `call_indirect` is executed with index that points on function with
/// signature different that is expected by this `call_indirect`, this trap is raised.
///
/// [`Signature`]: struct.Signature.html
ElemSignatureMismatch,
/// Attempt to divide by zero.
///
/// This trap typically can happen if `div` or `rem` is executed with
/// zero as divider.
DivisionByZero,
/// Attempt to make a conversion to an int failed.
///
/// This can happen when:
///
/// - trying to do signed division (or get the remainder) -2<sup>N-1</sup> over -1. This is
/// because the result +2<sup>N-1</sup> isn't representable as a N-bit signed integer.
/// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer.
InvalidConversionToInt,
/// Stack overflow.
///
/// This is likely caused by some infinite or very deep recursion.
/// Extensive inlining might also be the cause of stack overflow.
StackOverflow,
/// Error specified by the host.
///
/// Typically returned from an implementation of [`Externals`].
///
/// [`Externals`]: trait.Externals.html
Host(Box<host::HostError>),
}
/// Internal interpreter error.
#[derive(Debug)]
pub enum Error {
@ -127,7 +199,7 @@ pub enum Error {
/// Value-level error.
Value(String),
/// Trap.
Trap(String),
Trap(Trap),
/// Custom embedder error.
Host(Box<host::HostError>),
}
@ -143,7 +215,7 @@ impl Into<String> for Error {
Error::Global(s) => s,
Error::Stack(s) => s,
Error::Value(s) => s,
Error::Trap(s) => format!("trap: {}", s),
Error::Trap(s) => format!("trap: {:?}", s),
Error::Host(e) => format!("user: {}", e),
}
}
@ -160,7 +232,7 @@ impl fmt::Display for Error {
Error::Global(ref s) => write!(f, "Global: {}", s),
Error::Stack(ref s) => write!(f, "Stack: {}", s),
Error::Value(ref s) => write!(f, "Value: {}", s),
Error::Trap(ref s) => write!(f, "Trap: {}", s),
Error::Trap(ref s) => write!(f, "Trap: {:?}", s),
Error::Host(ref e) => write!(f, "User: {}", e),
}
}
@ -179,7 +251,7 @@ impl error::Error for Error {
Error::Global(ref s) => s,
Error::Stack(ref s) => s,
Error::Value(ref s) => s,
Error::Trap(ref s) => s,
Error::Trap(_) => "Trap",
Error::Host(_) => "Host error",
}
}
@ -192,6 +264,18 @@ impl<U> From<U> for Error where U: host::HostError + Sized {
}
}
impl<U> From<U> for Trap where U: host::HostError + Sized {
fn from(e: U) -> Self {
Trap::Host(Box::new(e))
}
}
impl From<Trap> for Error {
fn from(e: Trap) -> Error {
Error::Trap(e)
}
}
impl From<validation::Error> for Error {
fn from(e: validation::Error) -> Error {
Error::Validation(e.to_string())

View File

@ -1,3 +1,5 @@
use runner::check_function_args;
use Trap;
use std::rc::Rc;
use std::cell::RefCell;
use std::fmt;
@ -575,7 +577,9 @@ impl ModuleInstance {
}
};
check_function_args(func_instance.signature(), &args)?;
FuncInstance::invoke(&func_instance, args, externals)
.map_err(|t| Error::Trap(t))
}
/// Find export by a name.
@ -608,7 +612,7 @@ impl<'a> NotStartedModuleRef<'a> {
&self.instance
}
pub fn run_start<E: Externals>(self, state: &mut E) -> Result<ModuleRef, Error> {
pub fn run_start<E: Externals>(self, state: &mut E) -> Result<ModuleRef, Trap> {
if let Some(start_fn_idx) = self.loaded_module.module().start_section() {
let start_func = self.instance.func_by_index(start_fn_idx).expect(
"Due to validation start function should exists",

File diff suppressed because it is too large Load Diff

View File

@ -107,7 +107,7 @@ impl TableInstance {
}
/// Get the specific value in the table
pub fn get(&self, offset: u32) -> Result<FuncRef, Error> {
pub fn get(&self, offset: u32) -> Result<Option<FuncRef>, Error> {
let buffer = self.buffer.borrow();
let buffer_len = buffer.len();
let table_elem = buffer.get(offset as usize).cloned().ok_or_else(||
@ -117,10 +117,7 @@ impl TableInstance {
buffer_len
)),
)?;
Ok(table_elem.ok_or_else(|| Error::Table(format!(
"trying to read uninitialized element on index {}",
offset
)))?)
Ok(table_elem)
}
/// Set the table element to the specified function.

View File

@ -1,7 +1,7 @@
use {
Error, Signature, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder,
MemoryInstance, MemoryRef, TableInstance, TableRef, ModuleImportResolver, ModuleInstance, ModuleRef,
RuntimeValue, RuntimeArgs, Module, TableDescriptor, MemoryDescriptor,
RuntimeValue, RuntimeArgs, Module, TableDescriptor, MemoryDescriptor, Trap,
};
use types::ValueType;
use wabt::wat2wasm;
@ -79,23 +79,23 @@ impl Externals for TestHost {
&mut self,
index: usize,
args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Error> {
) -> Result<Option<RuntimeValue>, Trap> {
match index {
SUB_FUNC_INDEX => {
let a: i32 = args.nth(0)?;
let b: i32 = args.nth(1)?;
let a: i32 = args.nth(0);
let b: i32 = args.nth(1);
let result: RuntimeValue = (a - b).into();
Ok(Some(result))
}
ERR_FUNC_INDEX => {
let error_code: u32 = args.nth(0)?;
let error_code: u32 = args.nth(0);
let error = HostErrorWithCode { error_code };
Err(Error::Host(Box::new(error)))
Err(Trap::Host(Box::new(error)))
}
INC_MEM_FUNC_INDEX => {
let ptr: u32 = args.nth(0)?;
let ptr: u32 = args.nth(0);
let memory = self.memory.as_ref().expect(
"Function 'inc_mem' expects attached memory",
@ -108,7 +108,7 @@ impl Externals for TestHost {
Ok(None)
}
GET_MEM_FUNC_INDEX => {
let ptr: u32 = args.nth(0)?;
let ptr: u32 = args.nth(0);
let memory = self.memory.as_ref().expect(
"Function 'get_mem' expects attached memory",
@ -119,7 +119,7 @@ impl Externals for TestHost {
Ok(Some(RuntimeValue::I32(buf[0] as i32)))
}
RECURSE_FUNC_INDEX => {
let val = args.nth_value(0)?;
let val = args.nth_value_checked(0).expect("Exactly one argument expected");
let instance = self.instance
.as_ref()
@ -131,7 +131,7 @@ impl Externals for TestHost {
.expect("expected to be Some");
if val.value_type() != result.value_type() {
return Err(Error::Host(Box::new(HostErrorWithCode { error_code: 123 })));
return Err(Trap::Host(Box::new(HostErrorWithCode { error_code: 123 })));
}
Ok(Some(result))
}
@ -263,7 +263,7 @@ fn host_err() {
);
let host_error: Box<HostError> = match error {
Error::Host(err) => err,
Error::Trap(Trap::Host(err)) => err,
err => panic!("Unexpected error {:?}", err),
};
@ -464,10 +464,10 @@ fn defer_providing_externals() {
&mut self,
index: usize,
args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Error> {
) -> Result<Option<RuntimeValue>, Trap> {
match index {
INC_FUNC_INDEX => {
let a = args.nth::<u32>(0)?;
let a = args.nth::<u32>(0);
*self.acc += a;
Ok(None)
}
@ -528,7 +528,7 @@ fn two_envs_one_externals() {
&mut self,
index: usize,
_args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Error> {
) -> Result<Option<RuntimeValue>, Trap> {
match index {
PRIVILEGED_FUNC_INDEX => {
println!("privileged!");
@ -648,7 +648,7 @@ fn dynamically_add_host_func() {
&mut self,
index: usize,
_args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Error> {
) -> Result<Option<RuntimeValue>, Trap> {
match index {
ADD_FUNC_FUNC_INDEX => {
// Allocate indicies for the new function.
@ -661,7 +661,8 @@ fn dynamically_add_host_func() {
Signature::new(&[][..], Some(ValueType::I32)),
host_func_index as usize,
);
self.table.set(table_index, Some(added_func))?;
self.table.set(table_index, Some(added_func))
.map_err(|_| Trap::TableAccessOutOfBounds)?;
Ok(Some(RuntimeValue::I32(table_index as i32)))
}

View File

@ -1,7 +1,7 @@
use std::{i32, i64, u32, u64, f32};
use std::io;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use Error;
use {Error, Trap};
use parity_wasm::elements::ValueType;
@ -53,7 +53,7 @@ pub trait LittleEndianConvert where Self: Sized {
/// Convert to little endian buffer.
fn into_little_endian(self) -> Vec<u8>;
/// Convert from little endian buffer.
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error>;
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error>;
}
/// Arithmetic operations.
@ -65,7 +65,7 @@ pub trait ArithmeticOps<T> {
/// Multiply two values.
fn mul(self, other: T) -> T;
/// Divide two values.
fn div(self, other: T) -> Result<T, Error>;
fn div(self, other: T) -> Result<T, Trap>;
}
/// Integer value.
@ -81,7 +81,7 @@ pub trait Integer<T>: ArithmeticOps<T> {
/// Get right bit rotation result.
fn rotr(self, other: T) -> T;
/// Get division remainder.
fn rem(self, other: T) -> Result<T, Error>;
fn rem(self, other: T) -> Result<T, Trap>;
}
/// Float-point value.
@ -263,19 +263,19 @@ impl_wrap_into!(f64, f32);
macro_rules! impl_try_truncate_into {
($from: ident, $into: ident) => {
impl TryTruncateInto<$into, Error> for $from {
fn try_truncate_into(self) -> Result<$into, Error> {
impl TryTruncateInto<$into, Trap> for $from {
fn try_truncate_into(self) -> Result<$into, Trap> {
// Casting from a float to an integer will round the float towards zero
// NOTE: currently this will cause Undefined Behavior if the rounded value cannot be represented by the
// target integer type. This includes Inf and NaN. This is a bug and will be fixed.
if self.is_nan() || self.is_infinite() {
return Err(Error::Value("invalid float value for this operation".into()));
return Err(Trap::InvalidConversionToInt);
}
// range check
let result = self as $into;
if result as $from != self.trunc() {
return Err(Error::Value("invalid float value for this operation".into()));
return Err(Trap::InvalidConversionToInt);
}
Ok(self as $into)
@ -376,7 +376,7 @@ impl LittleEndianConvert for i8 {
vec![self as u8]
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
buffer.get(0)
.map(|v| *v as i8)
.ok_or_else(|| Error::Value("invalid little endian buffer".into()))
@ -388,7 +388,7 @@ impl LittleEndianConvert for u8 {
vec![self]
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
buffer.get(0)
.cloned()
.ok_or_else(|| Error::Value("invalid little endian buffer".into()))
@ -403,7 +403,7 @@ impl LittleEndianConvert for i16 {
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
io::Cursor::new(buffer).read_i16::<LittleEndian>()
.map_err(|e| Error::Value(e.to_string()))
}
@ -417,7 +417,7 @@ impl LittleEndianConvert for u16 {
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
io::Cursor::new(buffer).read_u16::<LittleEndian>()
.map_err(|e| Error::Value(e.to_string()))
}
@ -431,7 +431,7 @@ impl LittleEndianConvert for i32 {
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
io::Cursor::new(buffer).read_i32::<LittleEndian>()
.map_err(|e| Error::Value(e.to_string()))
}
@ -445,7 +445,7 @@ impl LittleEndianConvert for u32 {
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
io::Cursor::new(buffer).read_u32::<LittleEndian>()
.map_err(|e| Error::Value(e.to_string()))
}
@ -459,7 +459,7 @@ impl LittleEndianConvert for i64 {
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
io::Cursor::new(buffer).read_i64::<LittleEndian>()
.map_err(|e| Error::Value(e.to_string()))
}
@ -473,7 +473,7 @@ impl LittleEndianConvert for f32 {
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
io::Cursor::new(buffer).read_u32::<LittleEndian>()
.map(f32_from_bits)
.map_err(|e| Error::Value(e.to_string()))
@ -488,7 +488,7 @@ impl LittleEndianConvert for f64 {
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
io::Cursor::new(buffer).read_u64::<LittleEndian>()
.map(f64_from_bits)
.map_err(|e| Error::Value(e.to_string()))
@ -537,12 +537,14 @@ macro_rules! impl_integer_arithmetic_ops {
fn add(self, other: $type) -> $type { self.wrapping_add(other) }
fn sub(self, other: $type) -> $type { self.wrapping_sub(other) }
fn mul(self, other: $type) -> $type { self.wrapping_mul(other) }
fn div(self, other: $type) -> Result<$type, Error> {
if other == 0 { Err(Error::Value("Division by zero".to_owned())) }
fn div(self, other: $type) -> Result<$type, Trap> {
if other == 0 {
Err(Trap::DivisionByZero)
}
else {
let (result, overflow) = self.overflowing_div(other);
if overflow {
return Err(Error::Value("Attempt to divide with overflow".to_owned()));
Err(Trap::InvalidConversionToInt)
} else {
Ok(result)
}
@ -563,7 +565,7 @@ macro_rules! impl_float_arithmetic_ops {
fn add(self, other: $type) -> $type { self + other }
fn sub(self, other: $type) -> $type { self - other }
fn mul(self, other: $type) -> $type { self * other }
fn div(self, other: $type) -> Result<$type, Error> { Ok(self / other) }
fn div(self, other: $type) -> Result<$type, Trap> { Ok(self / other) }
}
}
}
@ -579,8 +581,8 @@ macro_rules! impl_integer {
fn count_ones(self) -> $type { self.count_ones() as $type }
fn rotl(self, other: $type) -> $type { self.rotate_left(other as u32) }
fn rotr(self, other: $type) -> $type { self.rotate_right(other as u32) }
fn rem(self, other: $type) -> Result<$type, Error> {
if other == 0 { Err(Error::Value("Division by zero".to_owned())) }
fn rem(self, other: $type) -> Result<$type, Trap> {
if other == 0 { Err(Trap::DivisionByZero) }
else { Ok(self.wrapping_rem(other)) }
}
}