docs and tests
This commit is contained in:
parent
47273e15cf
commit
21a8744e94
|
@ -80,6 +80,7 @@ impl<T> StackWithLimit<T> {
|
||||||
/// Returns Err(StackOverflow) if stack is already full.
|
/// Returns Err(StackOverflow) if stack is already full.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn push(&mut self, value: T) -> Result<(), StackOverflow> {
|
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.stack.capacity() {
|
||||||
if self.stack.len() == self.limit {
|
if self.stack.len() == self.limit {
|
||||||
return Err(StackOverflow);
|
return Err(StackOverflow);
|
||||||
|
@ -100,6 +101,17 @@ impl<T> StackWithLimit<T> {
|
||||||
Ok(())
|
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::<i32>::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<T> {
|
pub fn pop(&mut self) -> Option<T> {
|
||||||
self.stack.pop()
|
self.stack.pop()
|
||||||
}
|
}
|
||||||
|
@ -159,42 +171,96 @@ impl<T> StackWithLimit<T> {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `a` or `b` are out of bound.
|
/// Panics if `a` or `b` are out of bound.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # extern crate wasmi;
|
||||||
|
/// # use wasmi::{StackWithLimit, StackSize};
|
||||||
|
/// let mut bstack = StackWithLimit::<i32>::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]
|
#[inline]
|
||||||
pub fn swap(&mut self, a: usize, b: usize) {
|
pub fn swap(&mut self, a: usize, b: usize) {
|
||||||
self.stack.swap(a, b)
|
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.
|
/// The removed element is replaced by the element at the top of the stack.
|
||||||
///
|
|
||||||
/// This does not preserve ordering, but is O(1).
|
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `index` is out of bounds.
|
/// Panics if `index` is out of bounds.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # extern crate wasmi;
|
||||||
|
/// # use wasmi::{StackWithLimit, StackSize};
|
||||||
|
/// let mut bstack = StackWithLimit::<i32>::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 {
|
pub fn swap_remove(&mut self, index: usize) -> T {
|
||||||
self.stack.swap_remove(index)
|
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::<i32>::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> {
|
pub fn top(&self) -> Option<&T> {
|
||||||
self.stack.last()
|
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> {
|
pub fn top_mut(&mut self) -> Option<&mut T> {
|
||||||
self.stack.last_mut()
|
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) {
|
pub fn truncate(&mut self, new_size: usize) {
|
||||||
self.stack.truncate(new_size)
|
self.stack.truncate(new_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get number of items in a stack.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # extern crate wasmi;
|
||||||
|
/// # use wasmi::{StackWithLimit, StackSize};
|
||||||
|
/// let mut bstack = StackWithLimit::<i32>::with_size(StackSize::from_element_count(2));
|
||||||
|
/// assert_eq!(bstack.len(), 0);
|
||||||
|
/// bstack.push(1);
|
||||||
|
/// bstack.push(2);
|
||||||
|
/// assert_eq!(bstack.len(), 2);
|
||||||
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.stack.len()
|
self.stack.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check whether the stack is empty.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # extern crate wasmi;
|
||||||
|
/// # use wasmi::{StackWithLimit, StackSize};
|
||||||
|
/// let mut bstack = StackWithLimit::<i32>::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 {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.stack.is_empty()
|
self.stack.is_empty()
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,6 +216,7 @@ impl Interpreter {
|
||||||
self.state = InterpreterState::Initialized;
|
self.state = InterpreterState::Initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get current state of interpreter.
|
||||||
pub fn state(&self) -> &InterpreterState {
|
pub fn state(&self) -> &InterpreterState {
|
||||||
&self.state
|
&self.state
|
||||||
}
|
}
|
||||||
|
|
|
@ -1404,13 +1404,14 @@ fn pop_value(
|
||||||
let actual_value = if stack_is_empty && is_stack_polymorphic {
|
let actual_value = if stack_is_empty && is_stack_polymorphic {
|
||||||
StackValueType::Any
|
StackValueType::Any
|
||||||
} else {
|
} 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 {
|
if value_stack.len() <= value_stack_min {
|
||||||
return Err(Error("Trying to access parent frame stack values.".into()));
|
return Err(Error("Trying to access parent frame stack values.".into()));
|
||||||
}
|
}
|
||||||
value_stack
|
value_stack.pop().ok_or_else(underflow_err)?
|
||||||
.pop()
|
|
||||||
.ok_or_else(|| Error("non-empty stack expected".into()))?
|
|
||||||
};
|
};
|
||||||
match actual_value {
|
match actual_value {
|
||||||
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(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
|
// 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
|
// and this in turn requires current frame to check whether or not we've reached
|
||||||
// unreachable.
|
// unreachable.
|
||||||
let block_type = frame_stack
|
let block_type = frame_stack.top().ok_or_else(underflow_err)?.block_type;
|
||||||
.top()
|
|
||||||
.ok_or_else(|| Error("non-empty stack expected".into()))?
|
|
||||||
.block_type;
|
|
||||||
match block_type {
|
match block_type {
|
||||||
BlockType::NoResult => (),
|
BlockType::NoResult => (),
|
||||||
BlockType::Value(required_value_type) => {
|
BlockType::Value(required_value_type) => {
|
||||||
|
@ -1467,9 +1465,7 @@ fn pop_label(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let frame = frame_stack
|
let frame = frame_stack.pop().ok_or_else(underflow_err)?;
|
||||||
.pop()
|
|
||||||
.ok_or_else(|| Error("non-empty stack expected".into()))?;
|
|
||||||
if value_stack.len() != frame.value_stack_len {
|
if value_stack.len() != frame.value_stack_len {
|
||||||
return Err(Error(format!(
|
return Err(Error(format!(
|
||||||
"Unexpected stack height {}, expected {}",
|
"Unexpected stack height {}, expected {}",
|
||||||
|
@ -1488,9 +1484,7 @@ fn top_label(frame_stack: &StackWithLimit<BlockFrame>) -> &BlockFrame {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn require_label(depth: u32, frame_stack: &StackWithLimit<BlockFrame>) -> Result<&BlockFrame, Error> {
|
fn require_label(depth: u32, frame_stack: &StackWithLimit<BlockFrame>) -> Result<&BlockFrame, Error> {
|
||||||
frame_stack
|
frame_stack.nth_from_top(depth as usize).ok_or_else(underflow_err)
|
||||||
.nth_from_top(depth as usize)
|
|
||||||
.ok_or_else(|| Error("non-empty stack expected".into()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn require_target(
|
fn require_target(
|
||||||
|
@ -1718,3 +1712,7 @@ impl Sink {
|
||||||
self.ins
|
self.ins
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn underflow_err() -> Error {
|
||||||
|
Error("non-empty stack expected".into())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue