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)
}
/// 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> {
self.stack.last()
}

View File

@ -1,15 +1,19 @@
#[allow(unused_imports)]
use alloc::prelude::*;
use alloc::rc::{Rc, Weak};
use common::stack::{StackSize, StackWithLimit};
use core::fmt;
use parity_wasm::elements::Local;
use {Trap, Signature};
use host::Externals;
use runner::{check_function_args, Interpreter, InterpreterState};
use value::RuntimeValue;
use types::ValueType;
use module::ModuleInstance;
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).
///
@ -58,21 +62,12 @@ pub(crate) enum FuncInstanceInternal {
impl fmt::Debug for FuncInstance {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.as_internal() {
&FuncInstanceInternal::Internal {
ref signature,
..
} => {
&FuncInstanceInternal::Internal { ref signature, .. } => {
// We can't write description of self.module here, because it generate
// debug string for function instances and this will lead to infinite loop.
write!(
f,
"Internal {{ signature={:?} }}",
signature,
)
}
&FuncInstanceInternal::Host { ref signature, .. } => {
write!(f, "Host {{ signature={:?} }}", signature)
write!(f, "Internal {{ signature={:?} }}", signature,)
}
&FuncInstanceInternal::Host { ref signature, .. } => write!(f, "Host {{ signature={:?} }}", signature),
}
}
}
@ -110,11 +105,7 @@ impl FuncInstance {
&self.0
}
pub(crate) fn alloc_internal(
module: Weak<ModuleInstance>,
signature: Rc<Signature>,
body: FuncBody,
) -> FuncRef {
pub(crate) fn alloc_internal(module: Weak<ModuleInstance>, signature: Rc<Signature>, body: FuncBody) -> FuncRef {
let func = FuncInstanceInternal::Internal {
signature,
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.
///
/// # Errors
@ -144,17 +165,9 @@ impl FuncInstance {
args: &[RuntimeValue],
externals: &mut E,
) -> Result<Option<RuntimeValue>, Trap> {
check_function_args(func.signature(), &args)?;
match *func.as_internal() {
FuncInstanceInternal::Internal { .. } => {
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()),
}
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));
Self::invoke_configurable(func, args, externals, value_stack, call_stack)
}
/// 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
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
pub fn invoke_resumable<'args>(
func: &FuncRef,
args: &'args [RuntimeValue],
) -> Result<FuncInvocation<'args>, Trap> {
pub fn invoke_resumable<'args>(func: &FuncRef, args: &'args [RuntimeValue]) -> Result<FuncInvocation<'args>, Trap> {
check_function_args(func.signature(), &args)?;
match *func.as_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 {
kind: FuncInvocationKind::Internal(interpreter),
})
}
FuncInstanceInternal::Host {
ref host_func_index,
..
} => {
Ok(FuncInvocation {
kind: FuncInvocationKind::Host {
args,
host_func_index: *host_func_index,
finished: false,
},
})
},
ref host_func_index, ..
} => Ok(FuncInvocation {
kind: FuncInvocationKind::Host {
args,
host_func_index: *host_func_index,
finished: false,
},
}),
}
}
}
@ -239,7 +248,7 @@ enum FuncInvocationKind<'args> {
Host {
args: &'args [RuntimeValue],
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.
pub fn resumable_value_type(&self) -> Option<ValueType> {
match &self.kind {
&FuncInvocationKind::Internal(ref interpreter) => {
match interpreter.state() {
&InterpreterState::Resumable(ref value_type) => value_type.clone(),
_ => None,
}
&FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() {
&InterpreterState::Resumable(ref value_type) => value_type.clone(),
_ => None,
},
&FuncInvocationKind::Host { .. } => None,
}
}
/// 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 {
FuncInvocationKind::Internal(ref mut interpreter) => {
if interpreter.state() != &InterpreterState::Initialized {
return Err(ResumableError::AlreadyStarted);
}
Ok(interpreter.start_execution(externals)?)
},
FuncInvocationKind::Host { ref args, ref mut finished, ref host_func_index } => {
Ok(interpreter.start_execution(externals, func, args, return_type)?)
}
FuncInvocationKind::Host {
ref args,
ref mut finished,
ref host_func_index,
} => {
if *finished {
return Err(ResumableError::AlreadyStarted);
}
*finished = true;
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
/// [`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 {
FuncInvocationKind::Internal(ref mut interpreter) => {
if !interpreter.state().is_resumable() {
return Err(ResumableError::AlreadyStarted);
}
Ok(interpreter.resume_execution(return_val, externals)?)
},
Ok(interpreter.resume_execution(return_val, externals, return_type)?)
}
FuncInvocationKind::Host { .. } => {
return Err(ResumableError::NotResumable);
},
}
}
}
}

View File

@ -1,5 +1,6 @@
#[allow(unused_imports)]
use alloc::prelude::*;
use common::stack::{StackOverflow, StackWithLimit};
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use core::fmt;
use core::ops;
@ -13,8 +14,8 @@ use module::ModuleRef;
use nan_preserving_float::{F32, F64};
use parity_wasm::elements::Local;
use value::{
ArithmeticOps, ExtendInto, Float, Integer, LittleEndianConvert, RuntimeValue, TransmuteInto,
TryTruncateInto, WrapInto,
ArithmeticOps, ExtendInto, Float, Integer, LittleEndianConvert, RuntimeValue, TransmuteInto, TryTruncateInto,
WrapInto,
};
use {Signature, Trap, TrapKind, ValueType};
@ -36,7 +37,7 @@ pub const DEFAULT_CALL_STACK_LIMIT: usize = 64 * 1024;
/// at these boundaries.
#[derive(Copy, Clone, Debug, PartialEq, Default)]
#[repr(transparent)]
struct RuntimeValueInternal(pub u64);
pub struct RuntimeValueInternal(pub u64);
impl RuntimeValueInternal {
pub fn with_type(self, ty: ValueType) -> RuntimeValue {
@ -166,54 +167,52 @@ enum RunResult {
/// Function interpreter.
pub struct Interpreter {
value_stack: ValueStack,
call_stack: Vec<FunctionContext>,
return_type: Option<ValueType>,
call_stack: StackWithLimit<FunctionContext>,
state: InterpreterState,
}
impl Interpreter {
pub fn new(func: &FuncRef, args: &[RuntimeValue]) -> Result<Interpreter, Trap> {
let mut value_stack = ValueStack::with_limit(DEFAULT_VALUE_STACK_LIMIT);
for &arg in args {
let arg = arg.into();
value_stack.push(arg).map_err(
// There is not enough space for pushing initial arguments.
// 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,
pub fn new(
value_stack: StackWithLimit<RuntimeValueInternal>,
call_stack: StackWithLimit<FunctionContext>,
) -> Interpreter {
Interpreter {
value_stack: ValueStack(value_stack),
call_stack,
return_type,
state: InterpreterState::Initialized,
})
}
}
pub fn state(&self) -> &InterpreterState {
&self.state
}
pub fn start_execution<'a, E: Externals + 'a>(
pub fn start_execution<E: Externals>(
&mut self,
externals: &'a mut E,
externals: &mut E,
func: &FuncRef,
args: &[RuntimeValue],
return_type: Option<ValueType>,
) -> 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`.
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.run_interpreter_loop(externals)?;
let opt_return_value = self
.return_type
.map(|vt| self.value_stack.pop().with_type(vt));
let opt_return_value = 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.
assert!(self.value_stack.len() == 0);
@ -225,6 +224,7 @@ impl Interpreter {
&mut self,
return_val: Option<RuntimeValue>,
externals: &'a mut E,
return_type: Option<ValueType>,
) -> Result<Option<RuntimeValue>, Trap> {
use core::mem::swap;
@ -235,16 +235,12 @@ impl Interpreter {
swap(&mut self.state, &mut resumable_state);
if let Some(return_val) = return_val {
self.value_stack
.push(return_val.into())
.map_err(Trap::new)?;
self.value_stack.push(return_val.into()).map_err(Trap::new)?;
}
self.run_interpreter_loop(externals)?;
let opt_return_value = self
.return_type
.map(|vt| self.value_stack.pop().with_type(vt));
let opt_return_value = 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.
assert!(self.value_stack.len() == 0);
@ -252,20 +248,16 @@ impl Interpreter {
Ok(opt_return_value)
}
fn run_interpreter_loop<'a, E: Externals + 'a>(
&mut self,
externals: &'a mut E,
) -> Result<(), Trap> {
fn run_interpreter_loop<'a, E: Externals + 'a>(&mut self, externals: &'a mut E) -> Result<(), Trap> {
loop {
let mut function_context = self.call_stack.pop().expect(
"on loop entry - not empty; on loop continue - checking for emptiness; qed",
);
let mut function_context = self
.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_body = function_ref
.body()
.expect(
"Host functions checked in function_return below; Internal functions always have a body; qed"
);
.expect("Host functions checked in function_return below; Internal functions always have a body; qed");
if !function_context.is_initialized() {
// Initialize stack frame for the function call.
@ -278,40 +270,33 @@ impl Interpreter {
match function_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
// are done executing.
return Ok(());
}
}
RunResult::NestedCall(nested_func) => {
if self.call_stack.len() + 1 >= DEFAULT_CALL_STACK_LIMIT {
return Err(TrapKind::StackOverflow.into());
}
match *nested_func.as_internal() {
FuncInstanceInternal::Internal { .. } => {
let nested_context = FunctionContext::new(nested_func.clone());
self.call_stack.push(function_context);
self.call_stack.push(nested_context);
self.call_stack.push(function_context)?;
self.call_stack.push(nested_context)?;
}
FuncInstanceInternal::Host { ref signature, .. } => {
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.
self.call_stack.push(function_context);
self.call_stack.push(function_context)?;
let return_val =
match FuncInstance::invoke(&nested_func, &args, externals) {
Ok(val) => val,
Err(trap) => {
if trap.kind().is_host() {
self.state = InterpreterState::Resumable(
nested_func.signature().return_type(),
);
}
return Err(trap);
let return_val = match FuncInstance::invoke(&nested_func, &args, externals) {
Ok(val) => val,
Err(trap) => {
if trap.kind().is_host() {
self.state = InterpreterState::Resumable(nested_func.signature().return_type());
}
};
return Err(trap);
}
};
// Check if `return_val` matches the signature.
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 {
self.value_stack
.push(return_val.into())
.map_err(Trap::new)?;
self.value_stack.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::F32Load(offset) => self.run_load::<F32>(context, offset),
&isa::Instruction::F64Load(offset) => self.run_load::<F64>(context, offset),
&isa::Instruction::I32Load8S(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::I32Load16U(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::I32Load8S(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::I32Load16U(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::I64Store(offset) => self.run_store::<i64>(context, offset),
&isa::Instruction::F32Store(offset) => self.run_store::<F32>(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::I32Store16(offset) => {
self.run_store_wrap::<i32, i16>(context, offset)
}
&isa::Instruction::I32Store16(offset) => self.run_store_wrap::<i32, i16>(context, offset),
&isa::Instruction::I64Store8(offset) => self.run_store_wrap::<i64, i8>(context, offset),
&isa::Instruction::I64Store16(offset) => {
self.run_store_wrap::<i64, i16>(context, offset)
}
&isa::Instruction::I64Store32(offset) => {
self.run_store_wrap::<i64, i32>(context, offset)
}
&isa::Instruction::I64Store16(offset) => self.run_store_wrap::<i64, i16>(context, offset),
&isa::Instruction::I64Store32(offset) => self.run_store_wrap::<i64, i32>(context, offset),
&isa::Instruction::CurrentMemory => self.run_current_memory(context),
&isa::Instruction::GrowMemory => self.run_grow_memory(context),
@ -587,18 +544,11 @@ impl Interpreter {
}
}
fn run_unreachable(
&mut self,
_context: &mut FunctionContext,
) -> Result<InstructionOutcome, TrapKind> {
fn run_unreachable(&mut self, _context: &mut FunctionContext) -> Result<InstructionOutcome, TrapKind> {
Err(TrapKind::Unreachable)
}
fn run_br(
&mut self,
_context: &mut FunctionContext,
target: isa::Target,
) -> Result<InstructionOutcome, TrapKind> {
fn run_br(&mut self, _context: &mut FunctionContext, target: isa::Target) -> Result<InstructionOutcome, TrapKind> {
Ok(InstructionOutcome::Branch(target))
}
@ -632,11 +582,7 @@ impl Interpreter {
Ok(InstructionOutcome::Return(drop_keep))
}
fn run_call(
&mut self,
context: &mut FunctionContext,
func_idx: u32,
) -> Result<InstructionOutcome, TrapKind> {
fn run_call(&mut self, context: &mut FunctionContext, func_idx: u32) -> Result<InstructionOutcome, TrapKind> {
let func = context
.module()
.func_by_index(func_idx)
@ -706,11 +652,7 @@ impl Interpreter {
Ok(InstructionOutcome::RunNextInstruction)
}
fn run_get_global(
&mut self,
context: &mut FunctionContext,
index: u32,
) -> Result<InstructionOutcome, TrapKind> {
fn run_get_global(&mut self, context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, TrapKind> {
let global = context
.module()
.global_by_index(index)
@ -720,11 +662,7 @@ impl Interpreter {
Ok(InstructionOutcome::RunNextInstruction)
}
fn run_set_global(
&mut self,
context: &mut FunctionContext,
index: u32,
) -> Result<InstructionOutcome, TrapKind> {
fn run_set_global(&mut self, context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, TrapKind> {
let val = self.value_stack.pop();
let global = context
.module()
@ -736,23 +674,15 @@ impl Interpreter {
Ok(InstructionOutcome::RunNextInstruction)
}
fn run_load<T>(
&mut self,
context: &mut FunctionContext,
offset: u32,
) -> Result<InstructionOutcome, TrapKind>
fn run_load<T>(&mut self, context: &mut FunctionContext, offset: u32) -> Result<InstructionOutcome, TrapKind>
where
RuntimeValueInternal: From<T>,
T: LittleEndianConvert,
{
let raw_address = self.value_stack.pop_as();
let address = effective_address(offset, raw_address)?;
let m = context
.memory()
.expect("Due to validation memory should exists");
let n: T = m
.get_value(address)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
let m = context.memory().expect("Due to validation memory should exists");
let n: T = m.get_value(address).map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
self.value_stack.push(n.into())?;
Ok(InstructionOutcome::RunNextInstruction)
}
@ -769,12 +699,8 @@ impl Interpreter {
{
let raw_address = self.value_stack.pop_as();
let address = effective_address(offset, raw_address)?;
let m = context
.memory()
.expect("Due to validation memory should exists");
let v: T = m
.get_value(address)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
let m = context.memory().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();
self.value_stack
.push(stack_value.into())
@ -782,11 +708,7 @@ impl Interpreter {
.map(|_| InstructionOutcome::RunNextInstruction)
}
fn run_store<T>(
&mut self,
context: &mut FunctionContext,
offset: u32,
) -> Result<InstructionOutcome, TrapKind>
fn run_store<T>(&mut self, context: &mut FunctionContext, offset: u32) -> Result<InstructionOutcome, TrapKind>
where
T: FromRuntimeValueInternal,
T: LittleEndianConvert,
@ -795,9 +717,7 @@ impl Interpreter {
let raw_address = self.value_stack.pop_as::<u32>();
let address = effective_address(offset, raw_address)?;
let m = context
.memory()
.expect("Due to validation memory should exists");
let m = context.memory().expect("Due to validation memory should exists");
m.set_value(address, stack_value)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
Ok(InstructionOutcome::RunNextInstruction)
@ -817,34 +737,22 @@ impl Interpreter {
let stack_value = stack_value.wrap_into();
let raw_address = self.value_stack.pop_as::<u32>();
let address = effective_address(offset, raw_address)?;
let m = context
.memory()
.expect("Due to validation memory should exists");
let m = context.memory().expect("Due to validation memory should exists");
m.set_value(address, stack_value)
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
Ok(InstructionOutcome::RunNextInstruction)
}
fn run_current_memory(
&mut self,
context: &mut FunctionContext,
) -> Result<InstructionOutcome, TrapKind> {
let m = context
.memory()
.expect("Due to validation memory should exists");
fn run_current_memory(&mut self, context: &mut FunctionContext) -> Result<InstructionOutcome, TrapKind> {
let m = context.memory().expect("Due to validation memory should exists");
let s = m.current_size().0;
self.value_stack.push(RuntimeValueInternal(s as _))?;
Ok(InstructionOutcome::RunNextInstruction)
}
fn run_grow_memory(
&mut self,
context: &mut FunctionContext,
) -> Result<InstructionOutcome, TrapKind> {
fn run_grow_memory(&mut self, context: &mut FunctionContext) -> Result<InstructionOutcome, TrapKind> {
let pages: u32 = self.value_stack.pop_as();
let m = context
.memory()
.expect("Due to validation memory should exists");
let m = context.memory().expect("Due to validation memory should exists");
let m = match m.grow(Pages(pages as usize)) {
Ok(Pages(new_size)) => new_size as u32,
Err(_) => u32::MAX, // Returns -1 (or 0xFFFFFFFF) in case of error.
@ -1247,7 +1155,7 @@ impl Interpreter {
}
/// Function execution context.
struct FunctionContext {
pub struct FunctionContext {
/// Is context initialized.
pub is_initialized: bool,
/// Internal function reference.
@ -1259,7 +1167,7 @@ struct FunctionContext {
}
impl FunctionContext {
pub fn new(function: FuncRef) -> Self {
pub(crate) fn new(function: FuncRef) -> Self {
let module = match function.as_internal() {
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"),
@ -1278,11 +1186,7 @@ impl FunctionContext {
self.is_initialized
}
pub fn initialize(
&mut self,
locals: &[Local],
value_stack: &mut ValueStack,
) -> Result<(), TrapKind> {
fn initialize(&mut self, locals: &[Local], value_stack: &mut ValueStack) -> Result<(), TrapKind> {
debug_assert!(!self.is_initialized);
let num_locals = locals.iter().map(|l| l.count() as usize).sum();
@ -1290,9 +1194,7 @@ impl FunctionContext {
// TODO: Replace with extend.
for local in locals {
value_stack
.push(local)
.map_err(|_| TrapKind::StackOverflow)?;
value_stack.push(local).map_err(|_| TrapKind::StackOverflow)?;
}
self.is_initialized = true;
@ -1321,10 +1223,7 @@ fn effective_address(address: u32, offset: u32) -> Result<u32, TrapKind> {
}
}
fn prepare_function_args(
signature: &Signature,
caller_stack: &mut ValueStack,
) -> Vec<RuntimeValue> {
fn prepare_function_args(signature: &Signature, caller_stack: &mut ValueStack) -> Vec<RuntimeValue> {
let mut out = signature
.params()
.iter()
@ -1340,14 +1239,10 @@ pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Resu
return Err(TrapKind::UnexpectedSignature.into());
}
if signature
.params()
.iter()
.zip(args)
.any(|(expected_type, param_value)| {
let actual_type = param_value.value_type();
&actual_type != expected_type
}) {
if signature.params().iter().zip(args).any(|(expected_type, param_value)| {
let actual_type = param_value.value_type();
&actual_type != expected_type
}) {
return Err(TrapKind::UnexpectedSignature.into());
}
@ -1355,32 +1250,29 @@ pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Resu
}
#[derive(Debug)]
struct ValueStack {
buf: Box<[RuntimeValueInternal]>,
/// Index of the first free place in the stack.
sp: usize,
}
struct ValueStack(StackWithLimit<RuntimeValueInternal>);
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]
fn drop_keep(&mut self, drop_keep: isa::DropKeep) {
if drop_keep.keep == isa::Keep::Single {
let top = *self.top();
*self.pick_mut(drop_keep.drop as usize + 1) = top;
}
match drop_keep.keep {
isa::Keep::Single => {
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();
self.sp = cur_stack_len - drop_keep.drop as usize;
fn drop_many(&mut self, count: 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]
@ -1404,13 +1296,7 @@ impl ValueStack {
}
#[inline]
fn pop_triple(
&mut self,
) -> (
RuntimeValueInternal,
RuntimeValueInternal,
RuntimeValueInternal,
) {
fn pop_triple(&mut self) -> (RuntimeValueInternal, RuntimeValueInternal, RuntimeValueInternal) {
let right = self.pop();
let mid = self.pop();
let left = self.pop();
@ -1419,37 +1305,38 @@ impl ValueStack {
#[inline]
fn top(&self) -> &RuntimeValueInternal {
self.pick(1)
}
fn pick(&self, depth: usize) -> &RuntimeValueInternal {
&self.buf[self.sp - depth]
self.0.top().expect("pre-validated")
}
#[inline]
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]
fn pop(&mut self) -> RuntimeValueInternal {
self.sp -= 1;
self.buf[self.sp]
self.0.pop().expect("pre-validated")
}
#[inline]
fn push(&mut self, value: RuntimeValueInternal) -> Result<(), TrapKind> {
let cell = self
.buf
.get_mut(self.sp)
.ok_or_else(|| TrapKind::StackOverflow)?;
*cell = value;
self.sp += 1;
Ok(())
self.0.push(value).map_err(TrapKind::from)
}
#[inline]
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::Error;
use common::stack::StackWithLimit;
use common::stack::{StackSize, StackWithLimit};
use isa;
/// Maximum number of entries in value stack per function.
@ -1575,8 +1575,8 @@ impl<'a> FunctionValidationContext<'a> {
module: module,
position: 0,
locals: locals,
value_stack: StackWithLimit::with_limit(value_stack_limit),
frame_stack: StackWithLimit::with_limit(frame_stack_limit),
value_stack: StackWithLimit::with_size(StackSize::from_element_count(value_stack_limit)),
frame_stack: StackWithLimit::with_size(StackSize::from_element_count(frame_stack_limit)),
return_type: return_type,
sink: Sink::with_instruction_capacity(size_estimate),
}
@ -1598,7 +1598,15 @@ fn make_top_frame_polymorphic(
let frame = frame_stack
.top_mut()
.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;
}
@ -1630,7 +1638,7 @@ fn pop_value(
if value_stack.len() <= value_stack_min {
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 {
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
// and this in turn requires current frame to check whether or not we've reached
// 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 {
BlockType::NoResult => (),
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 {
return Err(Error(format!(
"Unexpected stack height {}, expected {}",
@ -1712,7 +1725,9 @@ fn require_label(
depth: u32,
frame_stack: &StackWithLimit<BlockFrame>,
) -> 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(
@ -1956,4 +1971,3 @@ impl Sink {
self.ins
}
}