UserFunctionExecutor

This commit is contained in:
Svyatoslav Nikolsky
2017-05-19 09:36:50 +03:00
parent 977df55323
commit a0bfa322c6
6 changed files with 150 additions and 160 deletions

View File

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use std::collections::HashMap; use std::collections::HashMap;
use parking_lot::RwLock;
use elements::{FunctionType, Internal, ValueType}; use elements::{FunctionType, Internal, ValueType};
use interpreter::Error; use interpreter::Error;
use interpreter::module::{ModuleInstanceInterface, ExecutionParams, ItemIndex, use interpreter::module::{ModuleInstanceInterface, ExecutionParams, ItemIndex,
@ -13,54 +13,58 @@ use interpreter::variable::VariableInstance;
/// Min index of native function. /// Min index of native function.
pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001; pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001;
/// Set of user-defined functions /// User function closure type.
pub type UserFunctions = HashMap<String, UserFunction>; // pub type UserFunctionClosure<'a> = &'a mut FnMut(context: CallerContext) -> Result<Option<RuntimeValue>, Error>;
/// User function closure /// User functions executor.
pub type UserFunctionClosure = Box<UserFunctionInterface>; pub trait UserFunctionExecutor {
/// Execute function with given name.
/// User-defined function execution interface fn execute(&mut self, name: &str, context: CallerContext) -> Result<Option<RuntimeValue>, Error>;
pub trait UserFunctionInterface {
/// Handles the user function invocation
fn call(&mut self, context: CallerContext) -> Result<Option<RuntimeValue>, Error>;
} }
impl<T> UserFunctionInterface for T where T: FnMut(CallerContext) -> Result<Option<RuntimeValue>, Error> { /// User function type.
fn call(&mut self, context: CallerContext) -> Result<Option<RuntimeValue>, Error> {
(&mut *self)(context)
}
}
/// Signature of user-defined env function
pub struct UserFunction { pub struct UserFunction {
/// User function parameters (for signature matching) /// User function name.
pub name: String,
/// User function parameters (for signature matching).
pub params: Vec<ValueType>, pub params: Vec<ValueType>,
/// User function return type (for signature matching) /// User function return type (for signature matching).
pub result: Option<ValueType>, pub result: Option<ValueType>,
/// Executor of the function
pub closure: UserFunctionClosure,
} }
type UserFunctionsInternals = Vec<::std::cell::RefCell<UserFunctionClosure>>; /// Set of user-defined functions
pub struct UserFunctions<'a> {
/// Functions list.
pub functions: Vec<UserFunction>,
/// Functions executor.
pub executor: &'a mut UserFunctionExecutor,
}
/// Native module instance. /// Native module instance.
pub struct NativeModuleInstance { pub struct NativeModuleInstance<'a> {
/// Underllying module reference.
env: Arc<ModuleInstanceInterface>, env: Arc<ModuleInstanceInterface>,
user_functions_names: HashMap<String, u32>, /// User function executor.
user_functions: UserFunctionsInternals, executor: RwLock<&'a mut UserFunctionExecutor>,
/// By-name functions index.
by_name: HashMap<String, u32>,
/// User functions list.
functions: Vec<UserFunction>,
} }
impl NativeModuleInstance { impl<'a> NativeModuleInstance<'a> {
pub fn new(env: Arc<ModuleInstanceInterface>, user_functions_names: HashMap<String, u32>, user_functions: UserFunctionsInternals) -> Result<Self, Error> { /// Create new native module
pub fn new(env: Arc<ModuleInstanceInterface>, functions: UserFunctions<'a>) -> Result<Self, Error> {
Ok(NativeModuleInstance { Ok(NativeModuleInstance {
env: env, env: env,
user_functions_names: user_functions_names, executor: RwLock::new(functions.executor),
user_functions: user_functions, 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<Option<RuntimeValue>, Error> { fn execute_main(&self, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
self.env.execute_main(params) self.env.execute_main(params)
} }
@ -74,8 +78,8 @@ impl ModuleInstanceInterface for NativeModuleInstance {
} }
fn export_entry(&self, name: &str) -> Result<Internal, Error> { fn export_entry(&self, name: &str) -> Result<Internal, Error> {
if let Some(index) = self.user_functions_names.get(name) { if let Some(index) = self.by_name.get(name) {
return Ok(Internal::Function(*index)); return Ok(Internal::Function(NATIVE_INDEX_FUNC_MIN + *index));
} }
self.env.export_entry(name) self.env.export_entry(name)
@ -107,24 +111,14 @@ impl ModuleInstanceInterface for NativeModuleInstance {
} }
// TODO: check type // TODO: check type
self.user_functions self.functions
.get((index - NATIVE_INDEX_FUNC_MIN) as usize) .get((index - NATIVE_INDEX_FUNC_MIN) as usize)
.ok_or(Error::Native(format!("trying to call native function with index {}", index))) .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. /// Create wrapper for env module with given native user functions.
pub fn env_native_module(env: Arc<ModuleInstanceInterface>, user_functions: UserFunctions) -> Result<NativeModuleInstance, Error> { pub fn env_native_module(env: Arc<ModuleInstanceInterface>, user_functions: UserFunctions) -> Result<NativeModuleInstance, Error> {
let mut funcs = user_functions; NativeModuleInstance::new(env, 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)
} }

View File

@ -95,7 +95,7 @@ impl ModuleImports {
} }
/// Get module reference. /// Get module reference.
pub fn module(&self, externals: Option<&HashMap<String, Arc<ModuleInstanceInterface>>>, name: &str) -> Result<Arc<ModuleInstanceInterface>, Error> { pub fn module<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, name: &str) -> Result<Arc<ModuleInstanceInterface + 'a>, Error> {
if let Some(externals) = externals { if let Some(externals) = externals {
if let Some(module) = externals.get(name).cloned() { if let Some(module) = externals.get(name).cloned() {
return Ok(module); return Ok(module);
@ -109,7 +109,7 @@ impl ModuleImports {
} }
/// Get function index. /// Get function index.
pub fn function(&self, externals: Option<&HashMap<String, Arc<ModuleInstanceInterface>>>, import: &ImportEntry) -> Result<u32, Error> { pub fn function<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<u32, Error> {
let (_, export) = self.external_export(externals, import)?; let (_, export) = self.external_export(externals, import)?;
if let Internal::Function(external_index) = export { if let Internal::Function(external_index) = export {
return Ok(external_index); return Ok(external_index);
@ -119,7 +119,7 @@ impl ModuleImports {
} }
/// Get table reference. /// Get table reference.
pub fn table(&self, externals: Option<&HashMap<String, Arc<ModuleInstanceInterface>>>, import: &ImportEntry) -> Result<Arc<TableInstance>, Error> { pub fn table<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<Arc<TableInstance>, Error> {
let (module, export) = self.external_export(externals, import)?; let (module, export) = self.external_export(externals, import)?;
if let Internal::Table(external_index) = export { if let Internal::Table(external_index) = export {
return module.table(ItemIndex::Internal(external_index)); return module.table(ItemIndex::Internal(external_index));
@ -129,7 +129,7 @@ impl ModuleImports {
} }
/// Get memory reference. /// Get memory reference.
pub fn memory(&self, externals: Option<&HashMap<String, Arc<ModuleInstanceInterface>>>, import: &ImportEntry) -> Result<Arc<MemoryInstance>, Error> { pub fn memory<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<Arc<MemoryInstance>, Error> {
let (module, export) = self.external_export(externals, import)?; let (module, export) = self.external_export(externals, import)?;
if let Internal::Memory(external_index) = export { if let Internal::Memory(external_index) = export {
return module.memory(ItemIndex::Internal(external_index)); return module.memory(ItemIndex::Internal(external_index));
@ -139,7 +139,7 @@ impl ModuleImports {
} }
/// Get global reference. /// Get global reference.
pub fn global(&self, externals: Option<&HashMap<String, Arc<ModuleInstanceInterface>>>, import: &ImportEntry) -> Result<Arc<VariableInstance>, Error> { pub fn global<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<Arc<VariableInstance>, Error> {
let (module, export) = self.external_export(externals, import)?; let (module, export) = self.external_export(externals, import)?;
if let Internal::Global(external_index) = export { if let Internal::Global(external_index) = export {
return module.global(ItemIndex::Internal(external_index)); 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()))) Err(Error::Program(format!("wrong import {} from module {} (expecting global)", import.field(), import.module())))
} }
fn external_export(&self, externals: Option<&HashMap<String, Arc<ModuleInstanceInterface>>>, import: &ImportEntry) -> Result<(Arc<ModuleInstanceInterface>, Internal), Error> { fn external_export<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<(Arc<ModuleInstanceInterface + 'a>, Internal), Error> {
self.module(externals, import.module()) self.module(externals, import.module())
.and_then(|m| .and_then(|m|
m.export_entry(import.field()) m.export_entry(import.field())

View File

@ -69,7 +69,10 @@ mod variable;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use self::memory::MemoryInstance;
pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, CallerContext, ExecutionParams}; pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, CallerContext, ExecutionParams};
pub use self::table::TableInstance;
pub use self::program::ProgramInstance; pub use self::program::ProgramInstance;
pub use self::value::RuntimeValue; 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};

View File

@ -12,13 +12,13 @@ use interpreter::table::TableInstance;
use interpreter::value::{RuntimeValue, TryInto, TransmuteInto}; use interpreter::value::{RuntimeValue, TryInto, TransmuteInto};
use interpreter::variable::{VariableInstance, VariableType}; use interpreter::variable::{VariableInstance, VariableType};
#[derive(Clone)] #[derive(Default, Clone)]
/// Execution context. /// Execution context.
pub struct ExecutionParams { pub struct ExecutionParams<'a> {
/// Arguments. /// Arguments.
pub args: Vec<RuntimeValue>, pub args: Vec<RuntimeValue>,
/// Execution-local external modules. /// Execution-local external modules.
pub externals: HashMap<String, Arc<ModuleInstanceInterface>>, pub externals: HashMap<String, Arc<ModuleInstanceInterface + 'a>>,
} }
/// Module instance API. /// Module instance API.
@ -79,12 +79,12 @@ pub struct CallerContext<'a> {
/// Stack of the input parameters /// Stack of the input parameters
pub value_stack: &'a mut StackWithLimit<RuntimeValue>, pub value_stack: &'a mut StackWithLimit<RuntimeValue>,
/// Execution-local external modules. /// Execution-local external modules.
pub externals: &'a HashMap<String, Arc<ModuleInstanceInterface>>, pub externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>,
} }
impl ExecutionParams { impl<'a> ExecutionParams<'a> {
/// Create new execution params with given externa; module override. /// Create new execution params with given externa; module override.
pub fn with_external(name: String, module: Arc<ModuleInstanceInterface>) -> Self { pub fn with_external(name: String, module: Arc<ModuleInstanceInterface + 'a>) -> Self {
let mut externals = HashMap::new(); let mut externals = HashMap::new();
externals.insert(name, module); externals.insert(name, module);
ExecutionParams { ExecutionParams {
@ -92,10 +92,16 @@ impl ExecutionParams {
externals: externals, externals: externals,
} }
} }
/// Add argument.
pub fn add_argument(mut self, arg: RuntimeValue) -> Self {
self.args.push(arg);
self
}
} }
impl From<Vec<RuntimeValue>> for ExecutionParams { impl<'a> From<Vec<RuntimeValue>> for ExecutionParams<'a> {
fn from(args: Vec<RuntimeValue>) -> ExecutionParams { fn from(args: Vec<RuntimeValue>) -> ExecutionParams<'a> {
ExecutionParams { ExecutionParams {
args: args, args: args,
externals: HashMap::new(), externals: HashMap::new(),
@ -357,7 +363,7 @@ impl ModuleInstanceInterface for ModuleInstance {
impl<'a> CallerContext<'a> { impl<'a> CallerContext<'a> {
/// Top most args /// Top most args
pub fn topmost(args: &'a mut StackWithLimit<RuntimeValue>, externals: &'a HashMap<String, Arc<ModuleInstanceInterface>>) -> Self { pub fn topmost(args: &'a mut StackWithLimit<RuntimeValue>, externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>) -> Self {
CallerContext { CallerContext {
value_stack_limit: 1024, value_stack_limit: 1024,
frame_stack_limit: 1024, frame_stack_limit: 1024,

View File

@ -22,7 +22,7 @@ pub struct FunctionContext<'a> {
/// Module instance. /// Module instance.
pub module: &'a ModuleInstance, pub module: &'a ModuleInstance,
/// Execution-local external modules. /// Execution-local external modules.
pub externals: &'a HashMap<String, Arc<ModuleInstanceInterface>>, pub externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>,
/// Function return type. /// Function return type.
pub return_type: BlockType, pub return_type: BlockType,
/// Local variables. /// Local variables.
@ -872,7 +872,7 @@ impl Interpreter {
} }
impl<'a> FunctionContext<'a> { impl<'a> FunctionContext<'a> {
pub fn new(module: &'a ModuleInstance, externals: &'a HashMap<String, Arc<ModuleInstanceInterface>>, value_stack_limit: usize, frame_stack_limit: usize, function: &FunctionType, body: &[Opcode], args: Vec<VariableInstance>) -> Result<Self, Error> { pub fn new(module: &'a ModuleInstance, externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>, value_stack_limit: usize, frame_stack_limit: usize, function: &FunctionType, body: &[Opcode], args: Vec<VariableInstance>) -> Result<Self, Error> {
let mut context = FunctionContext { let mut context = FunctionContext {
module: module, module: module,
externals: externals, externals: externals,
@ -893,7 +893,7 @@ impl<'a> FunctionContext<'a> {
self.module self.module
} }
pub fn externals(&self) -> &HashMap<String, Arc<ModuleInstanceInterface>> { pub fn externals(&self) -> &HashMap<String, Arc<ModuleInstanceInterface + 'a>> {
&self.externals &self.externals
} }

View File

@ -5,7 +5,9 @@ use builder::module;
use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType, use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType,
InitExpr, ValueType, Opcodes, Opcode}; InitExpr, ValueType, Opcodes, Opcode};
use interpreter::Error; 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::program::ProgramInstance;
use interpreter::value::RuntimeValue; use interpreter::value::RuntimeValue;
@ -122,111 +124,96 @@ fn global_get_set() {
} }
#[test] #[test]
fn with_user_functions() { fn single_program_different_modules() {
use interpreter::{UserFunction, UserFunctions, ExecutionParams, env_native_module}; // user function executor
struct FunctionExecutor {
pub memory: Arc<MemoryInstance>,
pub values: Vec<i32>,
}
impl UserFunctionExecutor for FunctionExecutor {
fn execute(&mut self, name: &str, context: CallerContext) -> Result<Option<RuntimeValue>, Error> {
match name {
"add" => {
let memory_value = self.memory.get(0, 1).unwrap()[0];
let fn_argument = context.value_stack.pop_as::<u32>().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::<u32>().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() let module = module()
.with_import(ImportEntry::new("env".into(), "custom_alloc".into(), External::Function(0))) .with_import(ImportEntry::new("env".into(), "add".into(), External::Function(0)))
.with_import(ImportEntry::new("env".into(), "custom_increment".into(), External::Function(0))) .with_import(ImportEntry::new("env".into(), "sub".into(), External::Function(0)))
.function() .function()
.signature().return_type().i32().build() .signature().param().i32().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![ .body().with_opcodes(Opcodes::new(vec![
Opcode::I32Const(32), Opcode::GetLocal(0),
Opcode::Call(0), Opcode::Call(0),
Opcode::End, Opcode::End,
])).build() ])).build()
.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(); .build();
let mut top = 0i32; // load module
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::<i32>()?;
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();
let module_instance = program.add_module("main", module).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 let mut executor = FunctionExecutor {
assert_eq!(module_instance.execute_index(2, params.clone()).unwrap().unwrap(), RuntimeValue::I32(0)); memory: env_memory.clone(),
assert_eq!(module_instance.execute_index(2, params.clone()).unwrap().unwrap(), RuntimeValue::I32(32)); values: Vec::new(),
assert_eq!(module_instance.execute_index(2, params.clone()).unwrap().unwrap(), RuntimeValue::I32(64)); };
{
// second import // create native env module with native add && sub implementations
assert_eq!(module_instance.execute_index(1, params.clone()).unwrap().unwrap(), RuntimeValue::I32(10000)); let functions: UserFunctions = UserFunctions {
assert_eq!(module_instance.execute_index(1, params).unwrap().unwrap(), RuntimeValue::I32(10001)); 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<Option<RuntimeValue>, Error> {
let prev = self.top;
self.top += context.value_stack.pop_as::<i32>()?;
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));
}