diff --git a/src/builder/module.rs b/src/builder/module.rs index 6bd375e..b69f59d 100644 --- a/src/builder/module.rs +++ b/src/builder/module.rs @@ -262,6 +262,12 @@ impl ModuleBuilder where F: Invoke { self.module.import.entries_mut().len() as u32 - 1 } + /// Push export entry to module. + pub fn push_export(&mut self, export: elements::ExportEntry) -> u32 { + self.module.export.entries_mut().push(export); + self.module.export.entries_mut().len() as u32 - 1 + } + /// Add new function using dedicated builder pub fn function(self) -> FunctionBuilder { FunctionBuilder::with_callback(self) diff --git a/src/interpreter/env.rs b/src/interpreter/env.rs index bf4bc73..3414355 100644 --- a/src/interpreter/env.rs +++ b/src/interpreter/env.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Weak}; use std::collections::HashMap; -use builder::module; +use builder::{module, function, export}; use elements::{Module, FunctionType, ExportEntry, Internal, GlobalEntry, GlobalType, ValueType, InitExpr, Opcode, Opcodes}; use interpreter::Error; @@ -73,13 +73,27 @@ const INDEX_FUNC_MAX: u32 = 10000; pub type UserFunctions = HashMap; /// User function closure -pub type UserFunctionClosure = Box Result, Error>>; +pub type UserFunctionClosure = Box; + +/// User-defined function execution interface +pub trait UserFunctionInterface { + fn call(&mut self, 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 pub struct UserFunction { - params: Vec, - result: Option, - closure: UserFunctionClosure, + /// User function parameters (for signature matching) + pub params: Vec, + /// User function return type (for signature matching) + pub result: Option, + /// Executor of the function + pub closure: UserFunctionClosure, } /// Environment parameters. @@ -92,7 +106,7 @@ pub struct EnvParams { pub allow_memory_growth: bool, } -type UserFunctionsInternals = HashMap; +type UserFunctionsInternals = Vec<::std::cell::RefCell>; pub struct EnvModuleInstance { _params: EnvParams, @@ -168,20 +182,26 @@ impl ModuleInstanceInterface for EnvModuleInstance { .map(|g| g.get()) .map(Some), INDEX_FUNC_MIN_NONUSED ... INDEX_FUNC_MAX => Err(Error::Trap("unimplemented".into())), - idx @ _ if idx == INDEX_FUNC_MAX + 1 => outer.value_stack.pop().map(|_| None), // TODO: `gas(i32) -> None` function - idx @ _ if idx == INDEX_FUNC_MAX + 2 => Ok(Some(RuntimeValue::I32(0))), // TODO: `_storage_size() -> i32` function - idx @ _ if idx == INDEX_FUNC_MAX + 3 => outer.value_stack.pop_triple().map(|_| Some(RuntimeValue::I32(0))), // TODO: `_storage_size(i32,i32,i32) -> i32` function - _ => Err(Error::Trap(format!("trying to call function with index {} in emv module", index))), + idx if idx > INDEX_FUNC_MAX && idx <= INDEX_FUNC_MAX + self.user_functions.len() as u32 => { + // user-defined function + let user_index = idx - (INDEX_FUNC_MAX+1); + let func = self.user_functions.get(user_index as usize).ok_or(Error::Trap(format!("Trying to invoke user-defined function {}", user_index)))?; + func.borrow_mut().call(outer) + }, + // idx @ _ if idx == INDEX_FUNC_MAX + 1 => outer.value_stack.pop().map(|_| None), // TODO: `gas(i32) -> None` function + // idx @ _ if idx == INDEX_FUNC_MAX + 2 => Ok(Some(RuntimeValue::I32(0))), // TODO: `_storage_size() -> i32` function + // idx @ _ if idx == INDEX_FUNC_MAX + 3 => outer.value_stack.pop_triple().map(|_| Some(RuntimeValue::I32(0))), // TODO: `_storage_size(i32,i32,i32) -> i32` function + _ => Err(Error::Trap(format!("trying to call function with index {} in env module", index))), } } } pub fn env_module(user_functions: UserFunctions) -> Result { - let mut env_params = EnvParams::default(); + let env_params = EnvParams::default(); debug_assert!(env_params.total_stack < env_params.total_memory); debug_assert!((env_params.total_stack % LINEAR_MEMORY_PAGE_SIZE) == 0); debug_assert!((env_params.total_memory % LINEAR_MEMORY_PAGE_SIZE) == 0); - let mut module = module() + let mut builder = module() // memory regions .memory() .with_min(env_params.total_memory / LINEAR_MEMORY_PAGE_SIZE) @@ -232,33 +252,31 @@ pub fn env_module(user_functions: UserFunctions) -> Result { + /// Value stack limit pub value_stack_limit: usize, + /// Frame stack limit pub frame_stack_limit: usize, + /// Stack of the input parameters pub value_stack: &'a mut StackWithLimit, } @@ -315,6 +318,7 @@ impl ModuleInstanceInterface for ModuleInstance { } impl<'a> CallerContext<'a> { + /// Top most args pub fn topmost(args: &'a mut StackWithLimit) -> Self { CallerContext { value_stack_limit: 1024, @@ -323,6 +327,7 @@ impl<'a> CallerContext<'a> { } } + /// Nested context pub fn nested(outer: &'a mut FunctionContext) -> Self { CallerContext { value_stack_limit: outer.value_stack().limit() - outer.value_stack().len(), diff --git a/src/interpreter/program.rs b/src/interpreter/program.rs index 02331e2..58bd524 100644 --- a/src/interpreter/program.rs +++ b/src/interpreter/program.rs @@ -27,6 +27,7 @@ impl ProgramInstance { }) } + /// Create new program instance with predefined user-defined functions pub fn with_functions(funcs: env::UserFunctions) -> Result { Ok(ProgramInstance { essence: Arc::new(ProgramInstanceEssence::with_functions(funcs)?), @@ -58,6 +59,7 @@ impl ProgramInstanceEssence { ProgramInstanceEssence::with_functions(HashMap::with_capacity(0)) } + /// Create new program essence with provided user-defined functions pub fn with_functions(funcs: env::UserFunctions) -> Result { let mut modules = HashMap::new(); let env_module: Arc = Arc::new(env_module(funcs)?); diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index 835c514..fba651c 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -4,7 +4,7 @@ use builder::module; use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType, InitExpr, ValueType, Opcodes, Opcode}; use interpreter::Error; -use interpreter::module::ModuleInstanceInterface; +use interpreter::module::{ModuleInstanceInterface, CallerContext}; use interpreter::program::ProgramInstance; use interpreter::value::RuntimeValue; @@ -119,3 +119,42 @@ fn global_get_set() { assert_eq!(module.execute_index(1, vec![]).unwrap_err(), Error::Variable("trying to update immutable variable".into())); assert_eq!(module.execute_index(2, vec![]).unwrap_err(), Error::Variable("trying to update variable of type I32 with value of type Some(I64)".into())); } + +#[test] +fn with_user_functions() { + use interpreter::{UserFunction, UserFunctions}; + + let module = module() + .with_import(ImportEntry::new("env".into(), "custom_alloc".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 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 program = ProgramInstance::with_functions(user_functions).unwrap(); + let module_instance = program.add_module("main", module).unwrap(); + + assert_eq!(module_instance.execute_index(1, vec![]).unwrap().unwrap(), RuntimeValue::I32(0)); + assert_eq!(module_instance.execute_index(1, vec![]).unwrap().unwrap(), RuntimeValue::I32(32)); + assert_eq!(module_instance.execute_index(1, vec![]).unwrap().unwrap(), RuntimeValue::I32(64)); +} \ No newline at end of file