diff --git a/src/interpreter/env_native.rs b/src/interpreter/env_native.rs index d18bd3b..09e5227 100644 --- a/src/interpreter/env_native.rs +++ b/src/interpreter/env_native.rs @@ -1,6 +1,6 @@ use std::sync::Arc; use std::collections::HashMap; - +use parking_lot::RwLock; use elements::{FunctionType, Internal, ValueType}; use interpreter::Error; use interpreter::module::{ModuleInstanceInterface, ExecutionParams, ItemIndex, @@ -13,54 +13,58 @@ use interpreter::variable::VariableInstance; /// Min index of native function. pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001; -/// Set of user-defined functions -pub type UserFunctions = HashMap; +/// User function closure type. +// pub type UserFunctionClosure<'a> = &'a mut FnMut(context: CallerContext) -> Result, Error>; -/// User function closure -pub type UserFunctionClosure = Box; - -/// User-defined function execution interface -pub trait UserFunctionInterface { - /// Handles the user function invocation - fn call(&mut self, context: CallerContext) -> Result, Error>; +/// User functions executor. +pub trait UserFunctionExecutor { + /// Execute function with given name. + fn execute(&mut self, name: &str, context: CallerContext) -> Result, Error>; } -impl UserFunctionInterface for T where T: FnMut(CallerContext) -> Result, Error> { - fn call(&mut self, context: CallerContext) -> Result, Error> { - (&mut *self)(context) - } -} - -/// Signature of user-defined env function +/// User function type. pub struct UserFunction { - /// User function parameters (for signature matching) + /// User function name. + pub name: String, + /// User function parameters (for signature matching). pub params: Vec, - /// User function return type (for signature matching) + /// User function return type (for signature matching). pub result: Option, - /// Executor of the function - pub closure: UserFunctionClosure, } -type UserFunctionsInternals = Vec<::std::cell::RefCell>; +/// Set of user-defined functions +pub struct UserFunctions<'a> { + /// Functions list. + pub functions: Vec, + /// Functions executor. + pub executor: &'a mut UserFunctionExecutor, +} /// Native module instance. -pub struct NativeModuleInstance { +pub struct NativeModuleInstance<'a> { + /// Underllying module reference. env: Arc, - user_functions_names: HashMap, - user_functions: UserFunctionsInternals, + /// User function executor. + executor: RwLock<&'a mut UserFunctionExecutor>, + /// By-name functions index. + by_name: HashMap, + /// User functions list. + functions: Vec, } -impl NativeModuleInstance { - pub fn new(env: Arc, user_functions_names: HashMap, user_functions: UserFunctionsInternals) -> Result { +impl<'a> NativeModuleInstance<'a> { + /// Create new native module + pub fn new(env: Arc, functions: UserFunctions<'a>) -> Result { Ok(NativeModuleInstance { env: env, - user_functions_names: user_functions_names, - user_functions: user_functions, + executor: RwLock::new(functions.executor), + by_name: functions.functions.iter().enumerate().map(|(i, f)| (f.name.clone(), i as u32)).collect(), + functions: functions.functions, }) } } -impl ModuleInstanceInterface for NativeModuleInstance { +impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { fn execute_main(&self, params: ExecutionParams) -> Result, Error> { self.env.execute_main(params) } @@ -74,8 +78,8 @@ impl ModuleInstanceInterface for NativeModuleInstance { } fn export_entry(&self, name: &str) -> Result { - if let Some(index) = self.user_functions_names.get(name) { - return Ok(Internal::Function(*index)); + if let Some(index) = self.by_name.get(name) { + return Ok(Internal::Function(NATIVE_INDEX_FUNC_MIN + *index)); } self.env.export_entry(name) @@ -107,24 +111,14 @@ impl ModuleInstanceInterface for NativeModuleInstance { } // TODO: check type - self.user_functions + self.functions .get((index - NATIVE_INDEX_FUNC_MIN) as usize) .ok_or(Error::Native(format!("trying to call native function with index {}", index))) - .and_then(|f| f.borrow_mut().call(outer)) + .and_then(|f| self.executor.write().execute(&f.name, outer)) } } /// Create wrapper for env module with given native user functions. pub fn env_native_module(env: Arc, user_functions: UserFunctions) -> Result { - let mut funcs = user_functions; - let mut names = HashMap::new(); - let mut internals = UserFunctionsInternals::new(); - let mut index = NATIVE_INDEX_FUNC_MIN; - for (func_name, func) in funcs.drain() { - internals.push(::std::cell::RefCell::new(func.closure)); - names.insert(func_name, index); - index += 1; - } - - NativeModuleInstance::new(env, names, internals) + NativeModuleInstance::new(env, user_functions) } diff --git a/src/interpreter/imports.rs b/src/interpreter/imports.rs index 8346944..6588a6b 100644 --- a/src/interpreter/imports.rs +++ b/src/interpreter/imports.rs @@ -95,7 +95,7 @@ impl ModuleImports { } /// Get module reference. - pub fn module(&self, externals: Option<&HashMap>>, name: &str) -> Result, Error> { + pub fn module<'a>(&self, externals: Option<&'a HashMap>>, name: &str) -> Result, Error> { if let Some(externals) = externals { if let Some(module) = externals.get(name).cloned() { return Ok(module); @@ -109,7 +109,7 @@ impl ModuleImports { } /// Get function index. - pub fn function(&self, externals: Option<&HashMap>>, import: &ImportEntry) -> Result { + pub fn function<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result { let (_, export) = self.external_export(externals, import)?; if let Internal::Function(external_index) = export { return Ok(external_index); @@ -119,7 +119,7 @@ impl ModuleImports { } /// Get table reference. - pub fn table(&self, externals: Option<&HashMap>>, import: &ImportEntry) -> Result, Error> { + pub fn table<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result, Error> { let (module, export) = self.external_export(externals, import)?; if let Internal::Table(external_index) = export { return module.table(ItemIndex::Internal(external_index)); @@ -129,7 +129,7 @@ impl ModuleImports { } /// Get memory reference. - pub fn memory(&self, externals: Option<&HashMap>>, import: &ImportEntry) -> Result, Error> { + pub fn memory<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result, Error> { let (module, export) = self.external_export(externals, import)?; if let Internal::Memory(external_index) = export { return module.memory(ItemIndex::Internal(external_index)); @@ -139,7 +139,7 @@ impl ModuleImports { } /// Get global reference. - pub fn global(&self, externals: Option<&HashMap>>, import: &ImportEntry) -> Result, Error> { + pub fn global<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result, Error> { let (module, export) = self.external_export(externals, import)?; if let Internal::Global(external_index) = export { return module.global(ItemIndex::Internal(external_index)); @@ -148,7 +148,7 @@ impl ModuleImports { Err(Error::Program(format!("wrong import {} from module {} (expecting global)", import.field(), import.module()))) } - fn external_export(&self, externals: Option<&HashMap>>, import: &ImportEntry) -> Result<(Arc, Internal), Error> { + fn external_export<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result<(Arc, Internal), Error> { self.module(externals, import.module()) .and_then(|m| m.export_entry(import.field()) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index d1daa3e..d436a3e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -69,7 +69,10 @@ mod variable; #[cfg(test)] mod tests; +pub use self::memory::MemoryInstance; pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, CallerContext, ExecutionParams}; +pub use self::table::TableInstance; pub use self::program::ProgramInstance; pub use self::value::RuntimeValue; -pub use self::env_native::{env_native_module, UserFunctions, UserFunction, UserFunctionInterface}; +pub use self::variable::VariableInstance; +pub use self::env_native::{env_native_module, UserFunctions, UserFunction, UserFunctionExecutor}; diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 8ec85bb..7e987be 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -12,13 +12,13 @@ use interpreter::table::TableInstance; use interpreter::value::{RuntimeValue, TryInto, TransmuteInto}; use interpreter::variable::{VariableInstance, VariableType}; -#[derive(Clone)] +#[derive(Default, Clone)] /// Execution context. -pub struct ExecutionParams { +pub struct ExecutionParams<'a> { /// Arguments. pub args: Vec, /// Execution-local external modules. - pub externals: HashMap>, + pub externals: HashMap>, } /// Module instance API. @@ -79,12 +79,12 @@ pub struct CallerContext<'a> { /// Stack of the input parameters pub value_stack: &'a mut StackWithLimit, /// Execution-local external modules. - pub externals: &'a HashMap>, + pub externals: &'a HashMap>, } -impl ExecutionParams { +impl<'a> ExecutionParams<'a> { /// Create new execution params with given externa; module override. - pub fn with_external(name: String, module: Arc) -> Self { + pub fn with_external(name: String, module: Arc) -> Self { let mut externals = HashMap::new(); externals.insert(name, module); ExecutionParams { @@ -92,10 +92,16 @@ impl ExecutionParams { externals: externals, } } + + /// Add argument. + pub fn add_argument(mut self, arg: RuntimeValue) -> Self { + self.args.push(arg); + self + } } -impl From> for ExecutionParams { - fn from(args: Vec) -> ExecutionParams { +impl<'a> From> for ExecutionParams<'a> { + fn from(args: Vec) -> ExecutionParams<'a> { ExecutionParams { args: args, externals: HashMap::new(), @@ -357,7 +363,7 @@ impl ModuleInstanceInterface for ModuleInstance { impl<'a> CallerContext<'a> { /// Top most args - pub fn topmost(args: &'a mut StackWithLimit, externals: &'a HashMap>) -> Self { + pub fn topmost(args: &'a mut StackWithLimit, externals: &'a HashMap>) -> Self { CallerContext { value_stack_limit: 1024, frame_stack_limit: 1024, diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index 0f2ebed..e25a17b 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -22,7 +22,7 @@ pub struct FunctionContext<'a> { /// Module instance. pub module: &'a ModuleInstance, /// Execution-local external modules. - pub externals: &'a HashMap>, + pub externals: &'a HashMap>, /// Function return type. pub return_type: BlockType, /// Local variables. @@ -872,7 +872,7 @@ impl Interpreter { } impl<'a> FunctionContext<'a> { - pub fn new(module: &'a ModuleInstance, externals: &'a HashMap>, value_stack_limit: usize, frame_stack_limit: usize, function: &FunctionType, body: &[Opcode], args: Vec) -> Result { + pub fn new(module: &'a ModuleInstance, externals: &'a HashMap>, value_stack_limit: usize, frame_stack_limit: usize, function: &FunctionType, body: &[Opcode], args: Vec) -> Result { let mut context = FunctionContext { module: module, externals: externals, @@ -893,7 +893,7 @@ impl<'a> FunctionContext<'a> { self.module } - pub fn externals(&self) -> &HashMap> { + pub fn externals(&self) -> &HashMap> { &self.externals } diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index 5c03b4a..fee1201 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -5,7 +5,9 @@ use builder::module; use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType, InitExpr, ValueType, Opcodes, Opcode}; use interpreter::Error; -use interpreter::module::{ModuleInstanceInterface, CallerContext}; +use interpreter::env_native::{env_native_module, UserFunction, UserFunctions, UserFunctionExecutor}; +use interpreter::memory::MemoryInstance; +use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams}; use interpreter::program::ProgramInstance; use interpreter::value::RuntimeValue; @@ -122,111 +124,96 @@ fn global_get_set() { } #[test] -fn with_user_functions() { - use interpreter::{UserFunction, UserFunctions, ExecutionParams, env_native_module}; +fn single_program_different_modules() { + // user function executor + struct FunctionExecutor { + pub memory: Arc, + pub values: Vec, + } + + impl UserFunctionExecutor for FunctionExecutor { + fn execute(&mut self, name: &str, context: CallerContext) -> Result, Error> { + match name { + "add" => { + let memory_value = self.memory.get(0, 1).unwrap()[0]; + let fn_argument = context.value_stack.pop_as::().unwrap() as u8; + let sum = memory_value + fn_argument; + self.memory.set(0, &vec![sum]).unwrap(); + self.values.push(sum as i32); + Ok(Some(RuntimeValue::I32(sum as i32))) + }, + "sub" => { + let memory_value = self.memory.get(0, 1).unwrap()[0]; + let fn_argument = context.value_stack.pop_as::().unwrap() as u8; + let diff = memory_value - fn_argument; + self.memory.set(0, &vec![diff]).unwrap(); + self.values.push(diff as i32); + Ok(Some(RuntimeValue::I32(diff as i32))) + }, + _ => Err(Error::Trap("not implemented".into())), + } + } + } + + // create new program + let program = ProgramInstance::new().unwrap(); + // => env module is created + let env_instance = program.module("env").unwrap(); + // => linear memory is created + let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); let module = module() - .with_import(ImportEntry::new("env".into(), "custom_alloc".into(), External::Function(0))) - .with_import(ImportEntry::new("env".into(), "custom_increment".into(), External::Function(0))) + .with_import(ImportEntry::new("env".into(), "add".into(), External::Function(0))) + .with_import(ImportEntry::new("env".into(), "sub".into(), External::Function(0))) .function() - .signature().return_type().i32().build() + .signature().param().i32().return_type().i32().build() .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(32), + Opcode::GetLocal(0), Opcode::Call(0), Opcode::End, ])).build() .build() + .function() + .signature().param().i32().return_type().i32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::GetLocal(0), + Opcode::Call(1), + Opcode::End, + ])).build() + .build() .build(); - let mut top = 0i32; - let mut user_functions = UserFunctions::new(); - user_functions.insert( - "custom_alloc".to_owned(), - UserFunction { - params: vec![ValueType::I32], - result: Some(ValueType::I32), - closure: Box::new(move |context: CallerContext| { - let prev = top; - top = top + context.value_stack.pop_as::()?; - Ok(Some(prev.into())) - }), - } - ); - - let mut rolling = 9999i32; - user_functions.insert( - "custom_increment".to_owned(), - UserFunction { - params: vec![ValueType::I32], - result: Some(ValueType::I32), - closure: Box::new(move |_context: CallerContext| { - rolling = rolling + 1; - Ok(Some(rolling.into())) - }), - } - ); - - let program = ProgramInstance::new().unwrap(); + // load module let module_instance = program.add_module("main", module).unwrap(); - let native_env = Arc::new(env_native_module(program.module("env").unwrap(), user_functions).unwrap()); - let params = ExecutionParams::with_external("env".into(), native_env); - // internal function using first import - assert_eq!(module_instance.execute_index(2, params.clone()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module_instance.execute_index(2, params.clone()).unwrap().unwrap(), RuntimeValue::I32(32)); - assert_eq!(module_instance.execute_index(2, params.clone()).unwrap().unwrap(), RuntimeValue::I32(64)); - - // second import - assert_eq!(module_instance.execute_index(1, params.clone()).unwrap().unwrap(), RuntimeValue::I32(10000)); - assert_eq!(module_instance.execute_index(1, params).unwrap().unwrap(), RuntimeValue::I32(10001)); + let mut executor = FunctionExecutor { + memory: env_memory.clone(), + values: Vec::new(), + }; + { + // create native env module with native add && sub implementations + let functions: UserFunctions = UserFunctions { + executor: &mut executor, + functions: vec![UserFunction { + name: "add".into(), + params: vec![ValueType::I32], + result: Some(ValueType::I32), + }, UserFunction { + name: "sub".into(), + params: vec![ValueType::I32], + result: Some(ValueType::I32), + }], + }; + let native_env_instance = Arc::new(env_native_module(env_instance, functions).unwrap()); + + // execute functions + let params = ExecutionParams::with_external("env".into(), native_env_instance); + + assert_eq!(module_instance.execute_index(2, params.clone().add_argument(RuntimeValue::I32(7))).unwrap().unwrap(), RuntimeValue::I32(7)); + assert_eq!(module_instance.execute_index(2, params.clone().add_argument(RuntimeValue::I32(50))).unwrap().unwrap(), RuntimeValue::I32(57)); + assert_eq!(module_instance.execute_index(3, params.clone().add_argument(RuntimeValue::I32(15))).unwrap().unwrap(), RuntimeValue::I32(42)); + } + + assert_eq!(executor.memory.get(0, 1).unwrap()[0], 42); + assert_eq!(executor.values, vec![7, 57, 42]); } - -#[test] -fn with_user_functions_extended() { - use interpreter::{UserFunction, UserFunctions, UserFunctionInterface, - ExecutionParams, env_native_module}; - - struct UserMAlloc { - top: i32, - } - - impl UserFunctionInterface for UserMAlloc { - fn call(&mut self, context: CallerContext) -> Result, Error> { - let prev = self.top; - self.top += context.value_stack.pop_as::()?; - Ok(Some(prev.into())) - } - } - - let module = module() - .with_import(ImportEntry::new("env".into(), "_malloc".into(), External::Function(0))) - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(32), - Opcode::Call(0), - Opcode::End, - ])).build() - .build() - .build(); - - let mut user_functions = UserFunctions::new(); - user_functions.insert( - "_malloc".to_owned(), - UserFunction { - params: vec![ValueType::I32], - result: Some(ValueType::I32), - closure: Box::new(UserMAlloc { top: 0 }), - } - ); - - let program = ProgramInstance::new().unwrap(); - let module_instance = program.add_module("main", module).unwrap(); - let native_env = Arc::new(env_native_module(program.module("env").unwrap(), user_functions).unwrap()); - let params = ExecutionParams::with_external("env".into(), native_env); - - // internal function using first import - assert_eq!(module_instance.execute_index(1, params.clone()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module_instance.execute_index(1, params.clone()).unwrap().unwrap(), RuntimeValue::I32(32)); - assert_eq!(module_instance.execute_index(1, params).unwrap().unwrap(), RuntimeValue::I32(64)); -} \ No newline at end of file