From 21a8744e9475f41ab6325419b15b578f84630bdd Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Thu, 22 Nov 2018 13:10:00 -0800 Subject: [PATCH] docs and tests --- src/common/stack.rs | 76 +++++++++++++++++++++++++++++++++++++++--- src/runner.rs | 1 + src/validation/func.rs | 26 +++++++-------- 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/common/stack.rs b/src/common/stack.rs index b4127d8..fdd5ca4 100644 --- a/src/common/stack.rs +++ b/src/common/stack.rs @@ -80,6 +80,7 @@ impl StackWithLimit { /// Returns Err(StackOverflow) if stack is already full. #[inline] pub fn push(&mut self, value: T) -> Result<(), StackOverflow> { + debug_assert!(self.stack.capacity() <= self.limit); if self.stack.len() == self.stack.capacity() { if self.stack.len() == self.limit { return Err(StackOverflow); @@ -100,6 +101,17 @@ impl StackWithLimit { Ok(()) } + /// Remove item from the top of a stack and return it, or `None` if the stack is empty. + /// + /// ``` + /// # extern crate wasmi; + /// # use wasmi::{StackWithLimit, StackSize}; + /// let mut bstack = StackWithLimit::::with_size(StackSize::from_element_count(2)); + /// bstack.push(2); + /// assert_eq!(bstack.pop(), Some(2)); + /// assert_eq!(bstack.len(), 0); + /// assert_eq!(bstack.pop(), None); + /// ``` pub fn pop(&mut self) -> Option { self.stack.pop() } @@ -159,42 +171,96 @@ impl StackWithLimit { /// # Panics /// /// Panics if `a` or `b` are out of bound. + /// + /// ``` + /// # extern crate wasmi; + /// # use wasmi::{StackWithLimit, StackSize}; + /// let mut bstack = StackWithLimit::::with_size(StackSize::from_element_count(2)); + /// bstack.push(1); + /// bstack.push(2); + /// assert_eq!(bstack.top(), Some(&2)); + /// stack.swap(0, 1); + /// assert_eq!(bstack.top(), Some(&1)); + /// ``` #[inline] pub fn swap(&mut self, a: usize, b: usize) { self.stack.swap(a, b) } - /// Removes an element from the vector and returns it. + /// Removes an element from the stack and returns it. /// - /// The removed element is replaced by the last element of the vector. - /// - /// This does not preserve ordering, but is O(1). + /// The removed element is replaced by the element at the top of the stack. /// /// # Panics /// /// Panics if `index` is out of bounds. + /// + /// ``` + /// # extern crate wasmi; + /// # use wasmi::{StackWithLimit, StackSize}; + /// let mut bstack = StackWithLimit::::with_size(StackSize::from_element_count(2)); + /// bstack.push(1); + /// bstack.push(2); + /// assert_eq!(bstack.top(), Some(&2)); + /// assert_eq!(stack.swap_remove(0), 1); + /// assert_eq!(bstack.top(), Some(&2)); + /// ``` pub fn swap_remove(&mut self, index: usize) -> T { self.stack.swap_remove(index) } + /// Get a reference to the top of the stack, or `None` if the stack is empty. + /// + /// ``` + /// # extern crate wasmi; + /// # use wasmi::{StackWithLimit, StackSize}; + /// let mut bstack = StackWithLimit::::with_size(StackSize::from_element_count(2)); + /// assert_eq!(bstack.top(), None); + /// bstack.push(2); + /// assert_eq!(bstack.top(), Some(&2)); + /// ``` pub fn top(&self) -> Option<&T> { self.stack.last() } + /// Get a mutable reference to the top of the stack, or `None` if the stack is empty. pub fn top_mut(&mut self) -> Option<&mut T> { self.stack.last_mut() } - // Same as Vec::[truncate](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.truncate) + /// Shorten the stack, keeping the bottom len elements and dropping the rest. + /// + /// If new_size is greater then the current stack size, this has no effect. pub fn truncate(&mut self, new_size: usize) { self.stack.truncate(new_size) } + /// Get number of items in a stack. + /// + /// ``` + /// # extern crate wasmi; + /// # use wasmi::{StackWithLimit, StackSize}; + /// let mut bstack = StackWithLimit::::with_size(StackSize::from_element_count(2)); + /// assert_eq!(bstack.len(), 0); + /// bstack.push(1); + /// bstack.push(2); + /// assert_eq!(bstack.len(), 2); + /// ``` #[inline] pub fn len(&self) -> usize { self.stack.len() } + /// Check whether the stack is empty. + /// + /// ``` + /// # extern crate wasmi; + /// # use wasmi::{StackWithLimit, StackSize}; + /// let mut bstack = StackWithLimit::::with_size(StackSize::from_element_count(2)); + /// assert_eq!(bstack.is_empty(), true); + /// bstack.push(1); + /// assert_eq!(bstack.is_empty(), false); + /// ``` pub fn is_empty(&self) -> bool { self.stack.is_empty() } diff --git a/src/runner.rs b/src/runner.rs index 5cd662b..4a6bb2c 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -216,6 +216,7 @@ impl Interpreter { self.state = InterpreterState::Initialized; } + /// Get current state of interpreter. pub fn state(&self) -> &InterpreterState { &self.state } diff --git a/src/validation/func.rs b/src/validation/func.rs index fdb7c6c..53160d9 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -1404,13 +1404,14 @@ fn pop_value( let actual_value = if stack_is_empty && is_stack_polymorphic { StackValueType::Any } else { - let value_stack_min = frame_stack.top().expect("Expected a non-empty frame stack.").value_stack_len; + let value_stack_min = frame_stack + .top() + .expect("Expected a non-empty frame stack.") + .value_stack_len; if value_stack.len() <= value_stack_min { return Err(Error("Trying to access parent frame stack values.".into())); } - value_stack - .pop() - .ok_or_else(|| Error("non-empty stack expected".into()))? + value_stack.pop().ok_or_else(underflow_err)? }; match actual_value { StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(actual_value), @@ -1456,10 +1457,7 @@ fn pop_label( // Don't pop frame yet. This is essential since we still might pop values from the value stack // and this in turn requires current frame to check whether or not we've reached // unreachable. - let block_type = frame_stack - .top() - .ok_or_else(|| Error("non-empty stack expected".into()))? - .block_type; + let block_type = frame_stack.top().ok_or_else(underflow_err)?.block_type; match block_type { BlockType::NoResult => (), BlockType::Value(required_value_type) => { @@ -1467,9 +1465,7 @@ fn pop_label( } } - let frame = frame_stack - .pop() - .ok_or_else(|| Error("non-empty stack expected".into()))?; + let frame = frame_stack.pop().ok_or_else(underflow_err)?; if value_stack.len() != frame.value_stack_len { return Err(Error(format!( "Unexpected stack height {}, expected {}", @@ -1488,9 +1484,7 @@ fn top_label(frame_stack: &StackWithLimit) -> &BlockFrame { } fn require_label(depth: u32, frame_stack: &StackWithLimit) -> Result<&BlockFrame, Error> { - frame_stack - .nth_from_top(depth as usize) - .ok_or_else(|| Error("non-empty stack expected".into())) + frame_stack.nth_from_top(depth as usize).ok_or_else(underflow_err) } fn require_target( @@ -1718,3 +1712,7 @@ impl Sink { self.ins } } + +fn underflow_err() -> Error { + Error("non-empty stack expected".into()) +}