use StackWithLimit in interpreter

This commit is contained in:
Andrew Dirksen 2018-11-16 22:01:28 -08:00
parent 74b32aaebd
commit 34f2140cae
4 changed files with 247 additions and 314 deletions

View File

@ -116,6 +116,16 @@ impl<T> StackWithLimit<T> {
self.stack.get(index) self.stack.get(index)
} }
/// mutable version of get_relative_to_top
///
/// `bstack.get_relative_to_top(0)` gets the top of the stack
///
/// `bstack.get_relative_to_top(1)` gets the item just below the stack
pub(crate) fn get_relative_to_top_mut(&mut self, depth: usize) -> Option<&mut T> {
let index = self.stack.len().checked_sub(1)?.checked_sub(depth)?;
self.stack.get_mut(index)
}
pub(crate) fn top(&self) -> Option<&T> { pub(crate) fn top(&self) -> Option<&T> {
self.stack.last() self.stack.last()
} }

View File

@ -1,15 +1,19 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use alloc::rc::{Rc, Weak}; use alloc::rc::{Rc, Weak};
use common::stack::{StackSize, StackWithLimit};
use core::fmt; use core::fmt;
use parity_wasm::elements::Local;
use {Trap, Signature};
use host::Externals; use host::Externals;
use runner::{check_function_args, Interpreter, InterpreterState};
use value::RuntimeValue;
use types::ValueType;
use module::ModuleInstance;
use isa; use isa;
use module::ModuleInstance;
use parity_wasm::elements::Local;
use runner::{
check_function_args, FunctionContext, Interpreter, InterpreterState, RuntimeValueInternal,
DEFAULT_CALL_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT,
};
use types::ValueType;
use value::RuntimeValue;
use {Signature, Trap};
/// Reference to a function (See [`FuncInstance`] for details). /// Reference to a function (See [`FuncInstance`] for details).
/// ///
@ -58,21 +62,12 @@ pub(crate) enum FuncInstanceInternal {
impl fmt::Debug for FuncInstance { impl fmt::Debug for FuncInstance {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.as_internal() { match self.as_internal() {
&FuncInstanceInternal::Internal { &FuncInstanceInternal::Internal { ref signature, .. } => {
ref signature,
..
} => {
// We can't write description of self.module here, because it generate // We can't write description of self.module here, because it generate
// debug string for function instances and this will lead to infinite loop. // debug string for function instances and this will lead to infinite loop.
write!( write!(f, "Internal {{ signature={:?} }}", signature,)
f,
"Internal {{ signature={:?} }}",
signature,
)
}
&FuncInstanceInternal::Host { ref signature, .. } => {
write!(f, "Host {{ signature={:?} }}", signature)
} }
&FuncInstanceInternal::Host { ref signature, .. } => write!(f, "Host {{ signature={:?} }}", signature),
} }
} }
} }
@ -110,11 +105,7 @@ impl FuncInstance {
&self.0 &self.0
} }
pub(crate) fn alloc_internal( pub(crate) fn alloc_internal(module: Weak<ModuleInstance>, signature: Rc<Signature>, body: FuncBody) -> FuncRef {
module: Weak<ModuleInstance>,
signature: Rc<Signature>,
body: FuncBody,
) -> FuncRef {
let func = FuncInstanceInternal::Internal { let func = FuncInstanceInternal::Internal {
signature, signature,
module: module, module: module,
@ -130,6 +121,36 @@ impl FuncInstance {
} }
} }
/// Invoke this function with extra configuration parameters.
///
/// This is an experimental API
///
/// # Errors
///
/// Returns `Err` if `args` types is not match function [`signature`] or
/// if [`Trap`] at execution time occured.
///
/// [`signature`]: #method.signature
/// [`Trap`]: #enum.Trap.html
pub fn invoke_configurable<E: Externals>(
func: &FuncRef,
args: &[RuntimeValue],
externals: &mut E,
value_stack: StackWithLimit<RuntimeValueInternal>,
call_stack: StackWithLimit<FunctionContext>,
) -> Result<Option<RuntimeValue>, Trap> {
check_function_args(func.signature(), &args)?;
match *func.as_internal() {
FuncInstanceInternal::Internal { .. } => {
let mut interpreter = Interpreter::new(value_stack, call_stack);
interpreter.start_execution(externals, func, args, func.signature().return_type())
}
FuncInstanceInternal::Host {
ref host_func_index, ..
} => externals.invoke_index(*host_func_index, args.into()),
}
}
/// Invoke this function. /// Invoke this function.
/// ///
/// # Errors /// # Errors
@ -144,17 +165,9 @@ impl FuncInstance {
args: &[RuntimeValue], args: &[RuntimeValue],
externals: &mut E, externals: &mut E,
) -> Result<Option<RuntimeValue>, Trap> { ) -> Result<Option<RuntimeValue>, Trap> {
check_function_args(func.signature(), &args)?; let value_stack = StackWithLimit::with_size(StackSize::from_element_count(DEFAULT_VALUE_STACK_LIMIT));
match *func.as_internal() { let call_stack = StackWithLimit::with_size(StackSize::from_element_count(DEFAULT_CALL_STACK_LIMIT));
FuncInstanceInternal::Internal { .. } => { Self::invoke_configurable(func, args, externals, value_stack, call_stack)
let mut interpreter = Interpreter::new(func, args)?;
interpreter.start_execution(externals)
}
FuncInstanceInternal::Host {
ref host_func_index,
..
} => externals.invoke_index(*host_func_index, args.into()),
}
} }
/// Invoke the function, get a resumable handle. This handle can then be used to [`start_execution`]. If a /// Invoke the function, get a resumable handle. This handle can then be used to [`start_execution`]. If a
@ -171,30 +184,26 @@ impl FuncInstance {
/// [`Trap`]: #enum.Trap.html /// [`Trap`]: #enum.Trap.html
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution /// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution /// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
pub fn invoke_resumable<'args>( pub fn invoke_resumable<'args>(func: &FuncRef, args: &'args [RuntimeValue]) -> Result<FuncInvocation<'args>, Trap> {
func: &FuncRef,
args: &'args [RuntimeValue],
) -> Result<FuncInvocation<'args>, Trap> {
check_function_args(func.signature(), &args)?; check_function_args(func.signature(), &args)?;
match *func.as_internal() { match *func.as_internal() {
FuncInstanceInternal::Internal { .. } => { FuncInstanceInternal::Internal { .. } => {
let interpreter = Interpreter::new(func, args)?; let value_stack = StackWithLimit::with_size(StackSize::from_element_count(DEFAULT_VALUE_STACK_LIMIT));
let call_stack = StackWithLimit::with_size(StackSize::from_element_count(DEFAULT_CALL_STACK_LIMIT));
let interpreter = Interpreter::new(value_stack, call_stack);
Ok(FuncInvocation { Ok(FuncInvocation {
kind: FuncInvocationKind::Internal(interpreter), kind: FuncInvocationKind::Internal(interpreter),
}) })
} }
FuncInstanceInternal::Host { FuncInstanceInternal::Host {
ref host_func_index, ref host_func_index, ..
.. } => Ok(FuncInvocation {
} => { kind: FuncInvocationKind::Host {
Ok(FuncInvocation { args,
kind: FuncInvocationKind::Host { host_func_index: *host_func_index,
args, finished: false,
host_func_index: *host_func_index, },
finished: false, }),
},
})
},
} }
} }
} }
@ -239,7 +248,7 @@ enum FuncInvocationKind<'args> {
Host { Host {
args: &'args [RuntimeValue], args: &'args [RuntimeValue],
host_func_index: usize, host_func_index: usize,
finished: bool finished: bool,
}, },
} }
@ -255,32 +264,40 @@ impl<'args> FuncInvocation<'args> {
/// If the invocation is resumable, the expected return value type to be feed back in. /// If the invocation is resumable, the expected return value type to be feed back in.
pub fn resumable_value_type(&self) -> Option<ValueType> { pub fn resumable_value_type(&self) -> Option<ValueType> {
match &self.kind { match &self.kind {
&FuncInvocationKind::Internal(ref interpreter) => { &FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() {
match interpreter.state() { &InterpreterState::Resumable(ref value_type) => value_type.clone(),
&InterpreterState::Resumable(ref value_type) => value_type.clone(), _ => None,
_ => None,
}
}, },
&FuncInvocationKind::Host { .. } => None, &FuncInvocationKind::Host { .. } => None,
} }
} }
/// Start the invocation execution. /// Start the invocation execution.
pub fn start_execution<'externals, E: Externals + 'externals>(&mut self, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> { pub fn start_execution<'externals, E: Externals + 'externals>(
&mut self,
externals: &'externals mut E,
func: &FuncRef,
args: &[RuntimeValue],
return_type: Option<ValueType>,
) -> Result<Option<RuntimeValue>, ResumableError> {
match self.kind { match self.kind {
FuncInvocationKind::Internal(ref mut interpreter) => { FuncInvocationKind::Internal(ref mut interpreter) => {
if interpreter.state() != &InterpreterState::Initialized { if interpreter.state() != &InterpreterState::Initialized {
return Err(ResumableError::AlreadyStarted); return Err(ResumableError::AlreadyStarted);
} }
Ok(interpreter.start_execution(externals)?) Ok(interpreter.start_execution(externals, func, args, return_type)?)
}, }
FuncInvocationKind::Host { ref args, ref mut finished, ref host_func_index } => { FuncInvocationKind::Host {
ref args,
ref mut finished,
ref host_func_index,
} => {
if *finished { if *finished {
return Err(ResumableError::AlreadyStarted); return Err(ResumableError::AlreadyStarted);
} }
*finished = true; *finished = true;
Ok(externals.invoke_index(*host_func_index, args.clone().into())?) Ok(externals.invoke_index(*host_func_index, args.clone().into())?)
}, }
} }
} }
@ -292,17 +309,22 @@ impl<'args> FuncInvocation<'args> {
/// ///
/// [`resumable_value_type`]: #method.resumable_value_type /// [`resumable_value_type`]: #method.resumable_value_type
/// [`is_resumable`]: #method.is_resumable /// [`is_resumable`]: #method.is_resumable
pub fn resume_execution<'externals, E: Externals + 'externals>(&mut self, return_val: Option<RuntimeValue>, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> { pub fn resume_execution<'externals, E: Externals + 'externals>(
&mut self,
return_val: Option<RuntimeValue>,
externals: &'externals mut E,
return_type: Option<ValueType>,
) -> Result<Option<RuntimeValue>, ResumableError> {
match self.kind { match self.kind {
FuncInvocationKind::Internal(ref mut interpreter) => { FuncInvocationKind::Internal(ref mut interpreter) => {
if !interpreter.state().is_resumable() { if !interpreter.state().is_resumable() {
return Err(ResumableError::AlreadyStarted); return Err(ResumableError::AlreadyStarted);
} }
Ok(interpreter.resume_execution(return_val, externals)?) Ok(interpreter.resume_execution(return_val, externals, return_type)?)
}, }
FuncInvocationKind::Host { .. } => { FuncInvocationKind::Host { .. } => {
return Err(ResumableError::NotResumable); return Err(ResumableError::NotResumable);
}, }
} }
} }
} }

View File

@ -1,5 +1,6 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use common::stack::{StackOverflow, StackWithLimit};
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use core::fmt; use core::fmt;
use core::ops; use core::ops;
@ -13,8 +14,8 @@ use module::ModuleRef;
use nan_preserving_float::{F32, F64}; use nan_preserving_float::{F32, F64};
use parity_wasm::elements::Local; use parity_wasm::elements::Local;
use value::{ use value::{
ArithmeticOps, ExtendInto, Float, Integer, LittleEndianConvert, RuntimeValue, TransmuteInto, ArithmeticOps, ExtendInto, Float, Integer, LittleEndianConvert, RuntimeValue, TransmuteInto, TryTruncateInto,
TryTruncateInto, WrapInto, WrapInto,
}; };
use {Signature, Trap, TrapKind, ValueType}; use {Signature, Trap, TrapKind, ValueType};
@ -36,7 +37,7 @@ pub const DEFAULT_CALL_STACK_LIMIT: usize = 64 * 1024;
/// at these boundaries. /// at these boundaries.
#[derive(Copy, Clone, Debug, PartialEq, Default)] #[derive(Copy, Clone, Debug, PartialEq, Default)]
#[repr(transparent)] #[repr(transparent)]
struct RuntimeValueInternal(pub u64); pub struct RuntimeValueInternal(pub u64);
impl RuntimeValueInternal { impl RuntimeValueInternal {
pub fn with_type(self, ty: ValueType) -> RuntimeValue { pub fn with_type(self, ty: ValueType) -> RuntimeValue {
@ -166,54 +167,52 @@ enum RunResult {
/// Function interpreter. /// Function interpreter.
pub struct Interpreter { pub struct Interpreter {
value_stack: ValueStack, value_stack: ValueStack,
call_stack: Vec<FunctionContext>, call_stack: StackWithLimit<FunctionContext>,
return_type: Option<ValueType>,
state: InterpreterState, state: InterpreterState,
} }
impl Interpreter { impl Interpreter {
pub fn new(func: &FuncRef, args: &[RuntimeValue]) -> Result<Interpreter, Trap> { pub fn new(
let mut value_stack = ValueStack::with_limit(DEFAULT_VALUE_STACK_LIMIT); value_stack: StackWithLimit<RuntimeValueInternal>,
for &arg in args { call_stack: StackWithLimit<FunctionContext>,
let arg = arg.into(); ) -> Interpreter {
value_stack.push(arg).map_err( Interpreter {
// There is not enough space for pushing initial arguments. value_stack: ValueStack(value_stack),
// Weird, but bail out anyway.
|_| Trap::from(TrapKind::StackOverflow),
)?;
}
let mut call_stack = Vec::new();
let initial_frame = FunctionContext::new(func.clone());
call_stack.push(initial_frame);
let return_type = func.signature().return_type();
Ok(Interpreter {
value_stack,
call_stack, call_stack,
return_type,
state: InterpreterState::Initialized, state: InterpreterState::Initialized,
}) }
} }
pub fn state(&self) -> &InterpreterState { pub fn state(&self) -> &InterpreterState {
&self.state &self.state
} }
pub fn start_execution<'a, E: Externals + 'a>( pub fn start_execution<E: Externals>(
&mut self, &mut self,
externals: &'a mut E, externals: &mut E,
func: &FuncRef,
args: &[RuntimeValue],
return_type: Option<ValueType>,
) -> Result<Option<RuntimeValue>, Trap> { ) -> Result<Option<RuntimeValue>, Trap> {
debug_assert_eq!(func.signature().return_type(), return_type);
// Ensure that the VM has not been executed. This is checked in `FuncInvocation::start_execution`. // Ensure that the VM has not been executed. This is checked in `FuncInvocation::start_execution`.
assert!(self.state == InterpreterState::Initialized); assert!(self.state == InterpreterState::Initialized);
// Add initial args to value stack
for arg in args {
self.value_stack.push(RuntimeValueInternal::from(*arg))?;
}
// Add initial frame to call stack
self.call_stack
.push(FunctionContext::new(func.clone()))
.map_err(|_| TrapKind::StackOverflow)?;
self.state = InterpreterState::Started; self.state = InterpreterState::Started;
self.run_interpreter_loop(externals)?; self.run_interpreter_loop(externals)?;
let opt_return_value = self let opt_return_value = return_type.map(|vt| self.value_stack.pop().with_type(vt));
.return_type
.map(|vt| self.value_stack.pop().with_type(vt));
// Ensure that stack is empty after the execution. This is guaranteed by the validation properties. // Ensure that stack is empty after the execution. This is guaranteed by the validation properties.
assert!(self.value_stack.len() == 0); assert!(self.value_stack.len() == 0);
@ -225,6 +224,7 @@ impl Interpreter {
&mut self, &mut self,
return_val: Option<RuntimeValue>, return_val: Option<RuntimeValue>,
externals: &'a mut E, externals: &'a mut E,
return_type: Option<ValueType>,
) -> Result<Option<RuntimeValue>, Trap> { ) -> Result<Option<RuntimeValue>, Trap> {
use core::mem::swap; use core::mem::swap;
@ -235,16 +235,12 @@ impl Interpreter {
swap(&mut self.state, &mut resumable_state); swap(&mut self.state, &mut resumable_state);
if let Some(return_val) = return_val { if let Some(return_val) = return_val {
self.value_stack self.value_stack.push(return_val.into()).map_err(Trap::new)?;
.push(return_val.into())
.map_err(Trap::new)?;
} }
self.run_interpreter_loop(externals)?; self.run_interpreter_loop(externals)?;
let opt_return_value = self let opt_return_value = return_type.map(|vt| self.value_stack.pop().with_type(vt));
.return_type
.map(|vt| self.value_stack.pop().with_type(vt));
// Ensure that stack is empty after the execution. This is guaranteed by the validation properties. // Ensure that stack is empty after the execution. This is guaranteed by the validation properties.
assert!(self.value_stack.len() == 0); assert!(self.value_stack.len() == 0);
@ -252,20 +248,16 @@ impl Interpreter {
Ok(opt_return_value) Ok(opt_return_value)
} }
fn run_interpreter_loop<'a, E: Externals + 'a>( fn run_interpreter_loop<'a, E: Externals + 'a>(&mut self, externals: &'a mut E) -> Result<(), Trap> {
&mut self,
externals: &'a mut E,
) -> Result<(), Trap> {
loop { loop {
let mut function_context = self.call_stack.pop().expect( let mut function_context = self
"on loop entry - not empty; on loop continue - checking for emptiness; qed", .call_stack
); .pop()
.expect("on loop entry - not empty; on loop continue - checking for emptiness; qed");
let function_ref = function_context.function.clone(); let function_ref = function_context.function.clone();
let function_body = function_ref let function_body = function_ref
.body() .body()
.expect( .expect("Host functions checked in function_return below; Internal functions always have a body; qed");
"Host functions checked in function_return below; Internal functions always have a body; qed"
);
if !function_context.is_initialized() { if !function_context.is_initialized() {
// Initialize stack frame for the function call. // Initialize stack frame for the function call.
@ -278,40 +270,33 @@ impl Interpreter {
match function_return { match function_return {
RunResult::Return => { RunResult::Return => {
if self.call_stack.last().is_none() { if self.call_stack.top().is_none() {
// This was the last frame in the call stack. This means we // This was the last frame in the call stack. This means we
// are done executing. // are done executing.
return Ok(()); return Ok(());
} }
} }
RunResult::NestedCall(nested_func) => { RunResult::NestedCall(nested_func) => {
if self.call_stack.len() + 1 >= DEFAULT_CALL_STACK_LIMIT {
return Err(TrapKind::StackOverflow.into());
}
match *nested_func.as_internal() { match *nested_func.as_internal() {
FuncInstanceInternal::Internal { .. } => { FuncInstanceInternal::Internal { .. } => {
let nested_context = FunctionContext::new(nested_func.clone()); let nested_context = FunctionContext::new(nested_func.clone());
self.call_stack.push(function_context); self.call_stack.push(function_context)?;
self.call_stack.push(nested_context); self.call_stack.push(nested_context)?;
} }
FuncInstanceInternal::Host { ref signature, .. } => { FuncInstanceInternal::Host { ref signature, .. } => {
let args = prepare_function_args(signature, &mut self.value_stack); let args = prepare_function_args(signature, &mut self.value_stack);
// We push the function context first. If the VM is not resumable, it does no harm. If it is, we then save the context here. // We push the function context first. If the VM is not resumable, it does no harm. If it is, we then save the context here.
self.call_stack.push(function_context); self.call_stack.push(function_context)?;
let return_val = let return_val = match FuncInstance::invoke(&nested_func, &args, externals) {
match FuncInstance::invoke(&nested_func, &args, externals) { Ok(val) => val,
Ok(val) => val, Err(trap) => {
Err(trap) => { if trap.kind().is_host() {
if trap.kind().is_host() { self.state = InterpreterState::Resumable(nested_func.signature().return_type());
self.state = InterpreterState::Resumable(
nested_func.signature().return_type(),
);
}
return Err(trap);
} }
}; return Err(trap);
}
};
// Check if `return_val` matches the signature. // Check if `return_val` matches the signature.
let value_ty = return_val.as_ref().map(|val| val.value_type()); let value_ty = return_val.as_ref().map(|val| val.value_type());
@ -321,9 +306,7 @@ impl Interpreter {
} }
if let Some(return_val) = return_val { if let Some(return_val) = return_val {
self.value_stack self.value_stack.push(return_val.into()).map_err(Trap::new)?;
.push(return_val.into())
.map_err(Trap::new)?;
} }
} }
} }
@ -397,52 +380,26 @@ impl Interpreter {
&isa::Instruction::I64Load(offset) => self.run_load::<i64>(context, offset), &isa::Instruction::I64Load(offset) => self.run_load::<i64>(context, offset),
&isa::Instruction::F32Load(offset) => self.run_load::<F32>(context, offset), &isa::Instruction::F32Load(offset) => self.run_load::<F32>(context, offset),
&isa::Instruction::F64Load(offset) => self.run_load::<F64>(context, offset), &isa::Instruction::F64Load(offset) => self.run_load::<F64>(context, offset),
&isa::Instruction::I32Load8S(offset) => { &isa::Instruction::I32Load8S(offset) => self.run_load_extend::<i8, i32>(context, offset),
self.run_load_extend::<i8, i32>(context, offset) &isa::Instruction::I32Load8U(offset) => self.run_load_extend::<u8, i32>(context, offset),
} &isa::Instruction::I32Load16S(offset) => self.run_load_extend::<i16, i32>(context, offset),
&isa::Instruction::I32Load8U(offset) => { &isa::Instruction::I32Load16U(offset) => self.run_load_extend::<u16, i32>(context, offset),
self.run_load_extend::<u8, i32>(context, offset) &isa::Instruction::I64Load8S(offset) => self.run_load_extend::<i8, i64>(context, offset),
} &isa::Instruction::I64Load8U(offset) => self.run_load_extend::<u8, i64>(context, offset),
&isa::Instruction::I32Load16S(offset) => { &isa::Instruction::I64Load16S(offset) => self.run_load_extend::<i16, i64>(context, offset),
self.run_load_extend::<i16, i32>(context, offset) &isa::Instruction::I64Load16U(offset) => self.run_load_extend::<u16, i64>(context, offset),
} &isa::Instruction::I64Load32S(offset) => self.run_load_extend::<i32, i64>(context, offset),
&isa::Instruction::I32Load16U(offset) => { &isa::Instruction::I64Load32U(offset) => self.run_load_extend::<u32, i64>(context, offset),
self.run_load_extend::<u16, i32>(context, offset)
}
&isa::Instruction::I64Load8S(offset) => {
self.run_load_extend::<i8, i64>(context, offset)
}
&isa::Instruction::I64Load8U(offset) => {
self.run_load_extend::<u8, i64>(context, offset)
}
&isa::Instruction::I64Load16S(offset) => {
self.run_load_extend::<i16, i64>(context, offset)
}
&isa::Instruction::I64Load16U(offset) => {
self.run_load_extend::<u16, i64>(context, offset)
}
&isa::Instruction::I64Load32S(offset) => {
self.run_load_extend::<i32, i64>(context, offset)
}
&isa::Instruction::I64Load32U(offset) => {
self.run_load_extend::<u32, i64>(context, offset)
}
&isa::Instruction::I32Store(offset) => self.run_store::<i32>(context, offset), &isa::Instruction::I32Store(offset) => self.run_store::<i32>(context, offset),
&isa::Instruction::I64Store(offset) => self.run_store::<i64>(context, offset), &isa::Instruction::I64Store(offset) => self.run_store::<i64>(context, offset),
&isa::Instruction::F32Store(offset) => self.run_store::<F32>(context, offset), &isa::Instruction::F32Store(offset) => self.run_store::<F32>(context, offset),
&isa::Instruction::F64Store(offset) => self.run_store::<F64>(context, offset), &isa::Instruction::F64Store(offset) => self.run_store::<F64>(context, offset),
&isa::Instruction::I32Store8(offset) => self.run_store_wrap::<i32, i8>(context, offset), &isa::Instruction::I32Store8(offset) => self.run_store_wrap::<i32, i8>(context, offset),
&isa::Instruction::I32Store16(offset) => { &isa::Instruction::I32Store16(offset) => self.run_store_wrap::<i32, i16>(context, offset),
self.run_store_wrap::<i32, i16>(context, offset)
}
&isa::Instruction::I64Store8(offset) => self.run_store_wrap::<i64, i8>(context, offset), &isa::Instruction::I64Store8(offset) => self.run_store_wrap::<i64, i8>(context, offset),
&isa::Instruction::I64Store16(offset) => { &isa::Instruction::I64Store16(offset) => self.run_store_wrap::<i64, i16>(context, offset),
self.run_store_wrap::<i64, i16>(context, offset) &isa::Instruction::I64Store32(offset) => self.run_store_wrap::<i64, i32>(context, offset),
}
&isa::Instruction::I64Store32(offset) => {
self.run_store_wrap::<i64, i32>(context, offset)
}
&isa::Instruction::CurrentMemory => self.run_current_memory(context), &isa::Instruction::CurrentMemory => self.run_current_memory(context),
&isa::Instruction::GrowMemory => self.run_grow_memory(context), &isa::Instruction::GrowMemory => self.run_grow_memory(context),
@ -587,18 +544,11 @@ impl Interpreter {
} }
} }
fn run_unreachable( fn run_unreachable(&mut self, _context: &mut FunctionContext) -> Result<InstructionOutcome, TrapKind> {
&mut self,
_context: &mut FunctionContext,
) -> Result<InstructionOutcome, TrapKind> {
Err(TrapKind::Unreachable) Err(TrapKind::Unreachable)
} }
fn run_br( fn run_br(&mut self, _context: &mut FunctionContext, target: isa::Target) -> Result<InstructionOutcome, TrapKind> {
&mut self,
_context: &mut FunctionContext,
target: isa::Target,
) -> Result<InstructionOutcome, TrapKind> {
Ok(InstructionOutcome::Branch(target)) Ok(InstructionOutcome::Branch(target))
} }
@ -632,11 +582,7 @@ impl Interpreter {
Ok(InstructionOutcome::Return(drop_keep)) Ok(InstructionOutcome::Return(drop_keep))
} }
fn run_call( fn run_call(&mut self, context: &mut FunctionContext, func_idx: u32) -> Result<InstructionOutcome, TrapKind> {
&mut self,
context: &mut FunctionContext,
func_idx: u32,
) -> Result<InstructionOutcome, TrapKind> {
let func = context let func = context
.module() .module()
.func_by_index(func_idx) .func_by_index(func_idx)
@ -706,11 +652,7 @@ impl Interpreter {
Ok(InstructionOutcome::RunNextInstruction) Ok(InstructionOutcome::RunNextInstruction)
} }
fn run_get_global( fn run_get_global(&mut self, context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, TrapKind> {
&mut self,
context: &mut FunctionContext,
index: u32,
) -> Result<InstructionOutcome, TrapKind> {
let global = context let global = context
.module() .module()
.global_by_index(index) .global_by_index(index)
@ -720,11 +662,7 @@ impl Interpreter {
Ok(InstructionOutcome::RunNextInstruction) Ok(InstructionOutcome::RunNextInstruction)
} }
fn run_set_global( fn run_set_global(&mut self, context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, TrapKind> {
&mut self,
context: &mut FunctionContext,
index: u32,
) -> Result<InstructionOutcome, TrapKind> {
let val = self.value_stack.pop(); let val = self.value_stack.pop();
let global = context let global = context
.module() .module()
@ -736,23 +674,15 @@ impl Interpreter {
Ok(InstructionOutcome::RunNextInstruction) Ok(InstructionOutcome::RunNextInstruction)
} }
fn run_load<T>( fn run_load<T>(&mut self, context: &mut FunctionContext, offset: u32) -> Result<InstructionOutcome, TrapKind>
&mut self,
context: &mut FunctionContext,
offset: u32,
) -> Result<InstructionOutcome, TrapKind>
where where
RuntimeValueInternal: From<T>, RuntimeValueInternal: From<T>,
T: LittleEndianConvert, T: LittleEndianConvert,
{ {
let raw_address = self.value_stack.pop_as(); let raw_address = self.value_stack.pop_as();
let address = effective_address(offset, raw_address)?; let address = effective_address(offset, raw_address)?;
let m = context let m = context.memory().expect("Due to validation memory should exists");
.memory() let n: T = m.get_value(address).map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
.expect("Due to validation memory should exists");
let n: T = m
.get_value(address)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
self.value_stack.push(n.into())?; self.value_stack.push(n.into())?;
Ok(InstructionOutcome::RunNextInstruction) Ok(InstructionOutcome::RunNextInstruction)
} }
@ -769,12 +699,8 @@ impl Interpreter {
{ {
let raw_address = self.value_stack.pop_as(); let raw_address = self.value_stack.pop_as();
let address = effective_address(offset, raw_address)?; let address = effective_address(offset, raw_address)?;
let m = context let m = context.memory().expect("Due to validation memory should exists");
.memory() let v: T = m.get_value(address).map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
.expect("Due to validation memory should exists");
let v: T = m
.get_value(address)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
let stack_value: U = v.extend_into(); let stack_value: U = v.extend_into();
self.value_stack self.value_stack
.push(stack_value.into()) .push(stack_value.into())
@ -782,11 +708,7 @@ impl Interpreter {
.map(|_| InstructionOutcome::RunNextInstruction) .map(|_| InstructionOutcome::RunNextInstruction)
} }
fn run_store<T>( fn run_store<T>(&mut self, context: &mut FunctionContext, offset: u32) -> Result<InstructionOutcome, TrapKind>
&mut self,
context: &mut FunctionContext,
offset: u32,
) -> Result<InstructionOutcome, TrapKind>
where where
T: FromRuntimeValueInternal, T: FromRuntimeValueInternal,
T: LittleEndianConvert, T: LittleEndianConvert,
@ -795,9 +717,7 @@ impl Interpreter {
let raw_address = self.value_stack.pop_as::<u32>(); let raw_address = self.value_stack.pop_as::<u32>();
let address = effective_address(offset, raw_address)?; let address = effective_address(offset, raw_address)?;
let m = context let m = context.memory().expect("Due to validation memory should exists");
.memory()
.expect("Due to validation memory should exists");
m.set_value(address, stack_value) m.set_value(address, stack_value)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
Ok(InstructionOutcome::RunNextInstruction) Ok(InstructionOutcome::RunNextInstruction)
@ -817,34 +737,22 @@ impl Interpreter {
let stack_value = stack_value.wrap_into(); let stack_value = stack_value.wrap_into();
let raw_address = self.value_stack.pop_as::<u32>(); let raw_address = self.value_stack.pop_as::<u32>();
let address = effective_address(offset, raw_address)?; let address = effective_address(offset, raw_address)?;
let m = context let m = context.memory().expect("Due to validation memory should exists");
.memory()
.expect("Due to validation memory should exists");
m.set_value(address, stack_value) m.set_value(address, stack_value)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
Ok(InstructionOutcome::RunNextInstruction) Ok(InstructionOutcome::RunNextInstruction)
} }
fn run_current_memory( fn run_current_memory(&mut self, context: &mut FunctionContext) -> Result<InstructionOutcome, TrapKind> {
&mut self, let m = context.memory().expect("Due to validation memory should exists");
context: &mut FunctionContext,
) -> Result<InstructionOutcome, TrapKind> {
let m = context
.memory()
.expect("Due to validation memory should exists");
let s = m.current_size().0; let s = m.current_size().0;
self.value_stack.push(RuntimeValueInternal(s as _))?; self.value_stack.push(RuntimeValueInternal(s as _))?;
Ok(InstructionOutcome::RunNextInstruction) Ok(InstructionOutcome::RunNextInstruction)
} }
fn run_grow_memory( fn run_grow_memory(&mut self, context: &mut FunctionContext) -> Result<InstructionOutcome, TrapKind> {
&mut self,
context: &mut FunctionContext,
) -> Result<InstructionOutcome, TrapKind> {
let pages: u32 = self.value_stack.pop_as(); let pages: u32 = self.value_stack.pop_as();
let m = context let m = context.memory().expect("Due to validation memory should exists");
.memory()
.expect("Due to validation memory should exists");
let m = match m.grow(Pages(pages as usize)) { let m = match m.grow(Pages(pages as usize)) {
Ok(Pages(new_size)) => new_size as u32, Ok(Pages(new_size)) => new_size as u32,
Err(_) => u32::MAX, // Returns -1 (or 0xFFFFFFFF) in case of error. Err(_) => u32::MAX, // Returns -1 (or 0xFFFFFFFF) in case of error.
@ -1247,7 +1155,7 @@ impl Interpreter {
} }
/// Function execution context. /// Function execution context.
struct FunctionContext { pub struct FunctionContext {
/// Is context initialized. /// Is context initialized.
pub is_initialized: bool, pub is_initialized: bool,
/// Internal function reference. /// Internal function reference.
@ -1259,7 +1167,7 @@ struct FunctionContext {
} }
impl FunctionContext { impl FunctionContext {
pub fn new(function: FuncRef) -> Self { pub(crate) fn new(function: FuncRef) -> Self {
let module = match function.as_internal() { let module = match function.as_internal() {
FuncInstanceInternal::Internal { module, .. } => module.upgrade().expect("module deallocated"), FuncInstanceInternal::Internal { module, .. } => module.upgrade().expect("module deallocated"),
FuncInstanceInternal::Host { .. } => panic!("Host functions can't be called as internally defined functions; Thus FunctionContext can be created only with internally defined functions; qed"), FuncInstanceInternal::Host { .. } => panic!("Host functions can't be called as internally defined functions; Thus FunctionContext can be created only with internally defined functions; qed"),
@ -1278,11 +1186,7 @@ impl FunctionContext {
self.is_initialized self.is_initialized
} }
pub fn initialize( fn initialize(&mut self, locals: &[Local], value_stack: &mut ValueStack) -> Result<(), TrapKind> {
&mut self,
locals: &[Local],
value_stack: &mut ValueStack,
) -> Result<(), TrapKind> {
debug_assert!(!self.is_initialized); debug_assert!(!self.is_initialized);
let num_locals = locals.iter().map(|l| l.count() as usize).sum(); let num_locals = locals.iter().map(|l| l.count() as usize).sum();
@ -1290,9 +1194,7 @@ impl FunctionContext {
// TODO: Replace with extend. // TODO: Replace with extend.
for local in locals { for local in locals {
value_stack value_stack.push(local).map_err(|_| TrapKind::StackOverflow)?;
.push(local)
.map_err(|_| TrapKind::StackOverflow)?;
} }
self.is_initialized = true; self.is_initialized = true;
@ -1321,10 +1223,7 @@ fn effective_address(address: u32, offset: u32) -> Result<u32, TrapKind> {
} }
} }
fn prepare_function_args( fn prepare_function_args(signature: &Signature, caller_stack: &mut ValueStack) -> Vec<RuntimeValue> {
signature: &Signature,
caller_stack: &mut ValueStack,
) -> Vec<RuntimeValue> {
let mut out = signature let mut out = signature
.params() .params()
.iter() .iter()
@ -1340,14 +1239,10 @@ pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Resu
return Err(TrapKind::UnexpectedSignature.into()); return Err(TrapKind::UnexpectedSignature.into());
} }
if signature if signature.params().iter().zip(args).any(|(expected_type, param_value)| {
.params() let actual_type = param_value.value_type();
.iter() &actual_type != expected_type
.zip(args) }) {
.any(|(expected_type, param_value)| {
let actual_type = param_value.value_type();
&actual_type != expected_type
}) {
return Err(TrapKind::UnexpectedSignature.into()); return Err(TrapKind::UnexpectedSignature.into());
} }
@ -1355,32 +1250,29 @@ pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Resu
} }
#[derive(Debug)] #[derive(Debug)]
struct ValueStack { struct ValueStack(StackWithLimit<RuntimeValueInternal>);
buf: Box<[RuntimeValueInternal]>,
/// Index of the first free place in the stack.
sp: usize,
}
impl ValueStack { impl ValueStack {
fn with_limit(limit: usize) -> ValueStack {
let mut buf = Vec::new();
buf.resize(limit, RuntimeValueInternal(0));
ValueStack {
buf: buf.into_boxed_slice(),
sp: 0,
}
}
#[inline] #[inline]
fn drop_keep(&mut self, drop_keep: isa::DropKeep) { fn drop_keep(&mut self, drop_keep: isa::DropKeep) {
if drop_keep.keep == isa::Keep::Single { match drop_keep.keep {
let top = *self.top(); isa::Keep::Single => {
*self.pick_mut(drop_keep.drop as usize + 1) = top; let top = *self.top(); // takes a copy
} let pick = self
.0
.get_relative_to_top_mut(drop_keep.drop as usize)
.expect("pre-validated");
*pick = top;
}
isa::Keep::None => {}
};
self.drop_many(drop_keep.drop as usize);
}
let cur_stack_len = self.len(); fn drop_many(&mut self, count: usize) {
self.sp = cur_stack_len - drop_keep.drop as usize; debug_assert!(count <= self.len(), "Attempted to drop more items than were in stack.");
let new_len = self.0.len().checked_sub(count).unwrap_or(0);
self.0.truncate(new_len);
} }
#[inline] #[inline]
@ -1404,13 +1296,7 @@ impl ValueStack {
} }
#[inline] #[inline]
fn pop_triple( fn pop_triple(&mut self) -> (RuntimeValueInternal, RuntimeValueInternal, RuntimeValueInternal) {
&mut self,
) -> (
RuntimeValueInternal,
RuntimeValueInternal,
RuntimeValueInternal,
) {
let right = self.pop(); let right = self.pop();
let mid = self.pop(); let mid = self.pop();
let left = self.pop(); let left = self.pop();
@ -1419,37 +1305,38 @@ impl ValueStack {
#[inline] #[inline]
fn top(&self) -> &RuntimeValueInternal { fn top(&self) -> &RuntimeValueInternal {
self.pick(1) self.0.top().expect("pre-validated")
}
fn pick(&self, depth: usize) -> &RuntimeValueInternal {
&self.buf[self.sp - depth]
} }
#[inline] #[inline]
fn pick_mut(&mut self, depth: usize) -> &mut RuntimeValueInternal { fn pick_mut(&mut self, depth: usize) -> &mut RuntimeValueInternal {
&mut self.buf[self.sp - depth] self.0.get_relative_to_top_mut(depth - 1).expect("pre-validated")
} }
#[inline] #[inline]
fn pop(&mut self) -> RuntimeValueInternal { fn pop(&mut self) -> RuntimeValueInternal {
self.sp -= 1; self.0.pop().expect("pre-validated")
self.buf[self.sp]
} }
#[inline] #[inline]
fn push(&mut self, value: RuntimeValueInternal) -> Result<(), TrapKind> { fn push(&mut self, value: RuntimeValueInternal) -> Result<(), TrapKind> {
let cell = self self.0.push(value).map_err(TrapKind::from)
.buf
.get_mut(self.sp)
.ok_or_else(|| TrapKind::StackOverflow)?;
*cell = value;
self.sp += 1;
Ok(())
} }
#[inline] #[inline]
fn len(&self) -> usize { fn len(&self) -> usize {
self.sp self.0.len()
}
}
impl From<StackOverflow> for TrapKind {
fn from(_: StackOverflow) -> TrapKind {
TrapKind::StackOverflow
}
}
impl From<StackOverflow> for Trap {
fn from(_: StackOverflow) -> Trap {
Trap::new(TrapKind::StackOverflow)
} }
} }

View File

@ -10,7 +10,7 @@ use validation::context::ModuleContext;
use validation::util::Locals; use validation::util::Locals;
use validation::Error; use validation::Error;
use common::stack::StackWithLimit; use common::stack::{StackSize, StackWithLimit};
use isa; use isa;
/// Maximum number of entries in value stack per function. /// Maximum number of entries in value stack per function.
@ -1575,8 +1575,8 @@ impl<'a> FunctionValidationContext<'a> {
module: module, module: module,
position: 0, position: 0,
locals: locals, locals: locals,
value_stack: StackWithLimit::with_limit(value_stack_limit), value_stack: StackWithLimit::with_size(StackSize::from_element_count(value_stack_limit)),
frame_stack: StackWithLimit::with_limit(frame_stack_limit), frame_stack: StackWithLimit::with_size(StackSize::from_element_count(frame_stack_limit)),
return_type: return_type, return_type: return_type,
sink: Sink::with_instruction_capacity(size_estimate), sink: Sink::with_instruction_capacity(size_estimate),
} }
@ -1598,7 +1598,15 @@ fn make_top_frame_polymorphic(
let frame = frame_stack let frame = frame_stack
.top_mut() .top_mut()
.expect("make_top_frame_polymorphic is called with empty frame stack"); .expect("make_top_frame_polymorphic is called with empty frame stack");
value_stack.resize(frame.value_stack_len, StackValueType::Any);
// shrink value stack to match top frame
debug_assert!(frame.value_stack_len <= value_stack.len());
while value_stack.len() > frame.value_stack_len {
value_stack.pop().expect(
"frame.value_stack_len >= 0, value_stack.len() > frame.value_stack_len :. value_stack.len() > 0; qed",
);
}
frame.polymorphic_stack = true; frame.polymorphic_stack = true;
} }
@ -1630,7 +1638,7 @@ fn pop_value(
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.pop()? value_stack.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 => { StackValueType::Specific(stack_value_type) if stack_value_type == value_type => {
@ -1678,7 +1686,10 @@ 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.top()?.block_type; let block_type = frame_stack
.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) => {
@ -1690,7 +1701,9 @@ fn pop_label(
} }
} }
let frame = frame_stack.pop()?; let frame = frame_stack
.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 {}",
@ -1712,7 +1725,9 @@ fn require_label(
depth: u32, depth: u32,
frame_stack: &StackWithLimit<BlockFrame>, frame_stack: &StackWithLimit<BlockFrame>,
) -> Result<&BlockFrame, Error> { ) -> Result<&BlockFrame, Error> {
Ok(frame_stack.get(depth as usize)?) Ok(frame_stack
.get_relative_to_top(depth as usize)
.ok_or_else(|| Error("non-empty stack expected".into()))?)
} }
fn require_target( fn require_target(
@ -1956,4 +1971,3 @@ impl Sink {
self.ins self.ins
} }
} }