diff --git a/src/func.rs b/src/func.rs index 9bb97a4..ea1f136 100644 --- a/src/func.rs +++ b/src/func.rs @@ -145,8 +145,8 @@ impl FuncInstance { check_function_args(func.signature(), &args).map_err(|_| TrapKind::UnexpectedSignature)?; match *func.as_internal() { FuncInstanceInternal::Internal { .. } => { - let mut interpreter = Interpreter::new(func, args, externals)?; - interpreter.start_execution() + let mut interpreter = Interpreter::new(func, args)?; + interpreter.start_execution(externals) } FuncInstanceInternal::Host { ref host_func_index, @@ -165,15 +165,14 @@ impl FuncInstance { /// /// [`signature`]: #method.signature /// [`Trap`]: #enum.Trap.html - pub fn invoke_resumable<'args, 'externals, E: Externals + 'externals>( + pub fn invoke_resumable<'args>( func: &FuncRef, args: &'args [RuntimeValue], - externals: &'externals mut E, - ) -> Result, Trap> { + ) -> Result, Trap> { check_function_args(func.signature(), &args).map_err(|_| TrapKind::UnexpectedSignature)?; match *func.as_internal() { FuncInstanceInternal::Internal { .. } => { - let interpreter = Interpreter::new(func, args, externals)?; + let interpreter = Interpreter::new(func, args)?; Ok(FuncInvocation { kind: FuncInvocationKind::Internal(interpreter), }) @@ -184,7 +183,7 @@ impl FuncInstance { } => { Ok(FuncInvocation { kind: FuncInvocationKind::Host { - args, externals, + args, host_func_index: *host_func_index, finished: false, }, @@ -195,6 +194,7 @@ impl FuncInstance { } /// A resumable invocation error. +#[derive(Debug)] pub enum ResumableError { /// Trap happened. Trap(Trap), @@ -211,21 +211,20 @@ impl From for ResumableError { } /// A resumable invocation handle. This struct is returned by `FuncInstance::invoke_resumable`. -pub struct FuncInvocation<'args, 'externals, E: Externals + 'externals> { - kind: FuncInvocationKind<'args, 'externals, E>, +pub struct FuncInvocation<'args> { + kind: FuncInvocationKind<'args>, } -enum FuncInvocationKind<'args, 'externals, E: Externals + 'externals> { - Internal(Interpreter<'externals, E>), +enum FuncInvocationKind<'args> { + Internal(Interpreter), Host { args: &'args [RuntimeValue], - externals: &'externals mut E, host_func_index: usize, finished: bool }, } -impl<'args, 'externals, E: Externals + 'externals> FuncInvocation<'args, 'externals, E> { +impl<'args> FuncInvocation<'args> { /// Whether this invocation is currently resumable. pub fn is_resumable(&self) -> bool { match &self.kind { @@ -248,15 +247,15 @@ impl<'args, 'externals, E: Externals + 'externals> FuncInvocation<'args, 'extern } /// Start the invocation execution. - pub fn start_execution(&mut self) -> Result, ResumableError> { + pub fn start_execution<'externals, E: Externals + 'externals>(&mut self, externals: &'externals mut E) -> Result, ResumableError> { match self.kind { FuncInvocationKind::Internal(ref mut interpreter) => { if interpreter.state() != &InterpreterState::Initialized { return Err(ResumableError::AlreadyStarted); } - Ok(interpreter.start_execution()?) + Ok(interpreter.start_execution(externals)?) }, - FuncInvocationKind::Host { ref args, ref mut externals, ref mut finished, ref host_func_index } => { + FuncInvocationKind::Host { ref args, ref mut finished, ref host_func_index } => { if *finished { return Err(ResumableError::AlreadyStarted); } @@ -267,13 +266,13 @@ impl<'args, 'externals, E: Externals + 'externals> FuncInvocation<'args, 'extern } /// Resume an execution if a previous trap of Host kind happened. - pub fn resume_execution(&mut self, return_val: Option) -> Result, ResumableError> { + pub fn resume_execution<'externals, E: Externals + 'externals>(&mut self, return_val: Option, externals: &'externals mut E) -> Result, ResumableError> { match self.kind { FuncInvocationKind::Internal(ref mut interpreter) => { if !interpreter.state().is_resumable() { return Err(ResumableError::AlreadyStarted); } - Ok(interpreter.resume_execution(return_val)?) + Ok(interpreter.resume_execution(return_val, externals)?) }, FuncInvocationKind::Host { .. } => { return Err(ResumableError::NotResumable); diff --git a/src/runner.rs b/src/runner.rs index fe3955a..941cea2 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -67,16 +67,15 @@ enum RunResult { } /// Function interpreter. -pub struct Interpreter<'a, E: Externals + 'a> { - externals: &'a mut E, +pub struct Interpreter { value_stack: ValueStack, call_stack: Vec, return_type: Option, state: InterpreterState, } -impl<'a, E: Externals> Interpreter<'a, E> { - pub fn new(func: &FuncRef, args: &[RuntimeValue], externals: &'a mut E) -> Result, Trap> { +impl Interpreter { + pub fn new(func: &FuncRef, args: &[RuntimeValue]) -> Result { let mut value_stack = ValueStack::with_limit(DEFAULT_VALUE_STACK_LIMIT); for arg in args { value_stack @@ -95,7 +94,6 @@ impl<'a, E: Externals> Interpreter<'a, E> { let return_type = func.signature().return_type(); Ok(Interpreter { - externals, value_stack, call_stack, return_type, @@ -107,12 +105,12 @@ impl<'a, E: Externals> Interpreter<'a, E> { &self.state } - pub fn start_execution(&mut self) -> Result, Trap> { + pub fn start_execution<'a, E: Externals + 'a>(&mut self, externals: &'a mut E) -> Result, Trap> { // Ensure that the VM has not been executed. assert!(self.state == InterpreterState::Initialized); self.state = InterpreterState::Started; - self.run_interpreter_loop()?; + self.run_interpreter_loop(externals)?; let opt_return_value = self.return_type.map(|_vt| { self.value_stack.pop() @@ -124,7 +122,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(opt_return_value) } - pub fn resume_execution(&mut self, return_val: Option) -> Result, Trap> { + pub fn resume_execution<'a, E: Externals + 'a>(&mut self, return_val: Option, externals: &'a mut E) -> Result, Trap> { use std::mem::swap; // Ensure that the VM is resumable. @@ -146,7 +144,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { self.value_stack.push(return_val).map_err(Trap::new)?; } - self.run_interpreter_loop()?; + self.run_interpreter_loop(externals)?; let opt_return_value = self.return_type.map(|_vt| { self.value_stack.pop() @@ -158,7 +156,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(opt_return_value) } - fn run_interpreter_loop(&mut self) -> 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() @@ -205,7 +203,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { // 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, externals) { Ok(val) => val, Err(trap) => { if trap.kind().is_host() { diff --git a/src/tests/host.rs b/src/tests/host.rs index 9bbb284..e075b22 100644 --- a/src/tests/host.rs +++ b/src/tests/host.rs @@ -1,7 +1,7 @@ use { Error, Signature, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, MemoryInstance, MemoryRef, TableInstance, TableRef, ModuleImportResolver, ModuleInstance, ModuleRef, - RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind, + RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind, ResumableError, ExternVal, }; use types::ValueType; use memory_units::Pages; @@ -32,6 +32,8 @@ impl HostError for HostErrorWithCode {} struct TestHost { memory: Option, instance: Option, + + trap_sub_result: Option, } impl TestHost { @@ -39,6 +41,8 @@ impl TestHost { TestHost { memory: Some(MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap()), instance: None, + + trap_sub_result: None, } } } @@ -75,6 +79,11 @@ const GET_MEM_FUNC_INDEX: usize = 3; /// This function requires attached module instance. const RECURSE_FUNC_INDEX: usize = 4; +/// trap_sub(a: i32, b: i32) -> i32 +/// +/// This function is the same as sub(a, b), but it will send a Host trap which pauses the interpreter execution. +const TRAP_SUB_FUNC_INDEX: usize = 5; + impl Externals for TestHost { fn invoke_index( &mut self, @@ -136,6 +145,14 @@ impl Externals for TestHost { } Ok(Some(result)) } + TRAP_SUB_FUNC_INDEX => { + let a: i32 = args.nth(0); + let b: i32 = args.nth(1); + + let result: RuntimeValue = (a - b).into(); + self.trap_sub_result = Some(result); + return Err(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 301 })).into()); + } _ => panic!("env doesn't provide function at index {}", index), } } @@ -157,6 +174,7 @@ impl TestHost { ERR_FUNC_INDEX => (&[ValueType::I32], None), INC_MEM_FUNC_INDEX => (&[ValueType::I32], None), GET_MEM_FUNC_INDEX => (&[ValueType::I32], Some(ValueType::I32)), + TRAP_SUB_FUNC_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)), _ => return false, }; @@ -172,6 +190,7 @@ impl ModuleImportResolver for TestHost { "inc_mem" => INC_MEM_FUNC_INDEX, "get_mem" => GET_MEM_FUNC_INDEX, "recurse" => RECURSE_FUNC_INDEX, + "trap_sub" => TRAP_SUB_FUNC_INDEX, _ => { return Err(Error::Instantiation( format!("Export {} not found", field_name), @@ -232,6 +251,51 @@ fn call_host_func() { ); } +#[test] +fn resume_call_host_func() { + let module = parse_wat( + r#" +(module + (import "env" "trap_sub" (func $trap_sub (param i32 i32) (result i32))) + + (func (export "test") (result i32) + (call $trap_sub + (i32.const 5) + (i32.const 7) + ) + ) +) +"#, + ); + + let mut env = TestHost::new(); + + let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) + .expect("Failed to instantiate module") + .assert_no_start(); + + let func_instance = match instance.export_by_name("test").unwrap() { + ExternVal::Func(func_instance) => func_instance, + _ => panic!(), + }; + + let mut invocation = FuncInstance::invoke_resumable(&func_instance, &[]).unwrap(); + let result = invocation.start_execution(&mut env); + match result { + Err(ResumableError::Trap(_)) => {}, + _ => panic!(), + } + + assert!(invocation.is_resumable()); + let trap_sub_result = env.trap_sub_result.take(); + assert_eq!( + invocation.resume_execution(trap_sub_result, &mut env).expect( + "Failed to invoke 'test' function", + ), + Some(RuntimeValue::I32(-2)) + ); +} + #[test] fn host_err() { let module = parse_wat( @@ -325,6 +389,8 @@ fn pull_internal_mem_from_module() { let mut env = TestHost { memory: None, instance: None, + + trap_sub_result: None, }; let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))