use StackWithLimit in interpreter
This commit is contained in:
parent
74b32aaebd
commit
34f2140cae
|
@ -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()
|
||||
}
|
||||
|
|
152
src/func.rs
152
src/func.rs
|
@ -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);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
367
src/runner.rs
367
src/runner.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue