Add functionality to resume execution in Interpreter level
This commit is contained in:
parent
fa556a2af1
commit
dcf926fd15
|
@ -37,16 +37,9 @@ pub enum InstructionOutcome {
|
||||||
Return(isa::DropKeep),
|
Return(isa::DropKeep),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function run result.
|
#[derive(PartialEq, Eq)]
|
||||||
enum RunResult {
|
|
||||||
/// Function has returned.
|
|
||||||
Return,
|
|
||||||
/// Function is calling other function.
|
|
||||||
NestedCall(FuncRef),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Function execution state, related to pause and resume.
|
/// Function execution state, related to pause and resume.
|
||||||
enum RunState {
|
pub enum InterpreterState {
|
||||||
/// The interpreter has been created, but has not been executed.
|
/// The interpreter has been created, but has not been executed.
|
||||||
Initialized,
|
Initialized,
|
||||||
/// The interpreter has started execution, and cannot be called again if it exits normally, or no Host traps happened.
|
/// The interpreter has started execution, and cannot be called again if it exits normally, or no Host traps happened.
|
||||||
|
@ -56,13 +49,30 @@ enum RunState {
|
||||||
Resumable(Option<ValueType>),
|
Resumable(Option<ValueType>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl InterpreterState {
|
||||||
|
pub fn is_resumable(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
&InterpreterState::Resumable(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Function run result.
|
||||||
|
enum RunResult {
|
||||||
|
/// Function has returned.
|
||||||
|
Return,
|
||||||
|
/// Function is calling other function.
|
||||||
|
NestedCall(FuncRef),
|
||||||
|
}
|
||||||
|
|
||||||
/// Function interpreter.
|
/// Function interpreter.
|
||||||
pub struct Interpreter<'a, E: Externals + 'a> {
|
pub struct Interpreter<'a, E: Externals + 'a> {
|
||||||
externals: &'a mut E,
|
externals: &'a mut E,
|
||||||
value_stack: ValueStack,
|
value_stack: ValueStack,
|
||||||
call_stack: Vec<FunctionContext>,
|
call_stack: Vec<FunctionContext>,
|
||||||
return_type: Option<ValueType>,
|
return_type: Option<ValueType>,
|
||||||
state: RunState,
|
state: InterpreterState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, E: Externals> Interpreter<'a, E> {
|
impl<'a, E: Externals> Interpreter<'a, E> {
|
||||||
|
@ -89,12 +99,49 @@ impl<'a, E: Externals> Interpreter<'a, E> {
|
||||||
value_stack,
|
value_stack,
|
||||||
call_stack,
|
call_stack,
|
||||||
return_type,
|
return_type,
|
||||||
state: RunState::Initialized,
|
state: InterpreterState::Initialized,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_execution(&mut self) -> Result<Option<RuntimeValue>, Trap> {
|
pub fn start_execution(&mut self) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
self.state = RunState::Started;
|
// Ensure that the VM has not been executed.
|
||||||
|
assert!(self.state == InterpreterState::Initialized);
|
||||||
|
|
||||||
|
self.state = InterpreterState::Started;
|
||||||
|
self.run_interpreter_loop()?;
|
||||||
|
|
||||||
|
let opt_return_value = self.return_type.map(|_vt| {
|
||||||
|
self.value_stack.pop()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure that stack is empty after the execution.
|
||||||
|
assert!(self.value_stack.len() == 0);
|
||||||
|
|
||||||
|
Ok(opt_return_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resume_execution(&mut self, return_val: Option<RuntimeValue>) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
|
use std::mem::swap;
|
||||||
|
|
||||||
|
// Ensure that the VM is resumable.
|
||||||
|
assert!(self.state.is_resumable());
|
||||||
|
|
||||||
|
let mut resumable_state = InterpreterState::Started;
|
||||||
|
swap(&mut self.state, &mut resumable_state);
|
||||||
|
let expected_ty = match resumable_state {
|
||||||
|
InterpreterState::Resumable(ty) => ty,
|
||||||
|
_ => unreachable!("Resumable arm is checked above is_resumable; qed"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let value_ty = return_val.as_ref().map(|val| val.value_type());
|
||||||
|
if value_ty != expected_ty {
|
||||||
|
return Err(TrapKind::UnexpectedSignature.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(return_val) = return_val {
|
||||||
|
self.value_stack.push(return_val).map_err(Trap::new)?;
|
||||||
|
}
|
||||||
|
|
||||||
self.run_interpreter_loop()?;
|
self.run_interpreter_loop()?;
|
||||||
|
|
||||||
let opt_return_value = self.return_type.map(|_vt| {
|
let opt_return_value = self.return_type.map(|_vt| {
|
||||||
|
@ -151,11 +198,14 @@ impl<'a, E: Externals> Interpreter<'a, E> {
|
||||||
},
|
},
|
||||||
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.
|
||||||
|
self.call_stack.push(function_context);
|
||||||
|
|
||||||
let return_val = match FuncInstance::invoke(&nested_func, &args, self.externals) {
|
let return_val = match FuncInstance::invoke(&nested_func, &args, self.externals) {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(trap) => {
|
Err(trap) => {
|
||||||
if trap.kind().is_host() {
|
if trap.kind().is_host() {
|
||||||
self.state = RunState::Resumable(nested_func.signature().return_type());
|
self.state = InterpreterState::Resumable(nested_func.signature().return_type());
|
||||||
}
|
}
|
||||||
return Err(trap);
|
return Err(trap);
|
||||||
},
|
},
|
||||||
|
@ -171,7 +221,6 @@ impl<'a, E: Externals> Interpreter<'a, E> {
|
||||||
if let Some(return_val) = return_val {
|
if let Some(return_val) = return_val {
|
||||||
self.value_stack.push(return_val).map_err(Trap::new)?;
|
self.value_stack.push(return_val).map_err(Trap::new)?;
|
||||||
}
|
}
|
||||||
self.call_stack.push(function_context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue