From 5920cd93d5b8be04a6bee7c9670d974c8f136e98 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Mon, 11 Dec 2017 16:28:05 +0100 Subject: [PATCH] Host module sketch --- src/interpreter/host.rs | 139 +++++++++++++++++++++++++++++++++++++ src/interpreter/mod.rs | 1 + src/interpreter/program.rs | 5 ++ src/interpreter/runner.rs | 54 +++++++------- src/interpreter/store.rs | 30 +++++--- 5 files changed, 190 insertions(+), 39 deletions(-) create mode 100644 src/interpreter/host.rs diff --git a/src/interpreter/host.rs b/src/interpreter/host.rs new file mode 100644 index 0000000..868aaff --- /dev/null +++ b/src/interpreter/host.rs @@ -0,0 +1,139 @@ +use std::any::Any; +use std::marker::PhantomData; +use std::collections::HashMap; +use elements::{FunctionType, ValueType}; +use interpreter::store::{Store, ExternVal}; +use interpreter::value::RuntimeValue; +use interpreter::Error; + +pub struct HostModuleBuilder<'a, St> { + store: &'a mut Store, + exports: HashMap, + _marker: PhantomData, +} + +impl<'a, St: 'static> HostModuleBuilder<'a, St> { + pub fn new(store: &'a mut Store) -> Self { + HostModuleBuilder { + store: store, + exports: HashMap::new(), + _marker: PhantomData, + } + } + + pub fn push_func1< + Cl: Fn(&mut St, P1) -> Result, Error> + 'static, + Ret: AsReturn + 'static, + P1: FromArg + 'static, + F: Into>, + >( + &mut self, + name: &str, + f: F, + ) { + let func_type = Func1::::derive_func_type(); + let type_id = self.store.alloc_func_type(func_type); + + let anyfunc = Box::new(f.into()) as Box; + + let func_id = self.store.alloc_host_func(type_id, anyfunc); + let extern_val = ExternVal::Func(func_id); + + self.exports.insert(name.to_owned(), extern_val); + } +} + +pub trait AnyFunc { + fn call_as_any( + &self, + state: &mut Any, + args: &[RuntimeValue], + ) -> Result, Error>; +} + +pub trait FromArg { + fn from_arg(arg: &RuntimeValue) -> Self; + fn value_type() -> ValueType; +} + +impl FromArg for i32 { + fn from_arg(arg: &RuntimeValue) -> Self { + match arg { + &RuntimeValue::I32(v) => v, + unexpected => panic!("Unexpected runtime value {:?}", unexpected) + } + } + + fn value_type() -> ValueType { + ValueType::I32 + } +} + +pub trait AsReturn { + fn as_return_val(self) -> Option; + fn value_type() -> Option; +} + +impl AsReturn for i32 { + fn as_return_val(self) -> Option { + Some(self.into()) + } + + fn value_type() -> Option { + Some(ValueType::I32) + } +} + +impl AsReturn for () { + fn as_return_val(self) -> Option { + None + } + + fn value_type() -> Option { + None + } +} + +pub struct Func1 Result, Error>, St, Ret: AsReturn, P1: FromArg> { + closure: Cl, + _marker: PhantomData<(St, Ret, P1)>, +} + +impl< + St: 'static, + Ret: AsReturn, + P1: FromArg, + Cl: Fn(&mut St, P1) -> Result, Error>, +> AnyFunc for Func1 { + fn call_as_any( + &self, + state: &mut Any, + args: &[RuntimeValue], + ) -> Result, Error> { + let state = state.downcast_mut::().unwrap(); + let p1 = P1::from_arg(&args[0]); + let result = (self.closure)(state, p1); + result.map(|r| r.and_then(|r| r.as_return_val())) + } +} + +impl Result, Error>> From + for Func1 { + fn from(cl: Cl) -> Self { + Func1 { + closure: cl, + _marker: PhantomData, + } + } +} + +impl< + St: 'static, + Ret: AsReturn, + P1: FromArg, + Cl: Fn(&mut St, P1) -> Result, Error>, +> Func1 { + fn derive_func_type() -> FunctionType { + FunctionType::new(vec![P1::value_type()], Ret::value_type()) + } +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 802c0d0..e02cdbc 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -140,6 +140,7 @@ mod table; mod value; mod variable; mod store; +mod host; #[cfg(test)] mod tests; diff --git a/src/interpreter/program.rs b/src/interpreter/program.rs index f1178b3..365a139 100644 --- a/src/interpreter/program.rs +++ b/src/interpreter/program.rs @@ -4,6 +4,7 @@ use elements::Module; use interpreter::Error; use interpreter::module::{ExecutionParams}; use interpreter::store::{Store, ModuleId}; +use interpreter::host::HostModuleBuilder; /// Program instance. Program is a set of instantiated modules. pub struct ProgramInstance { @@ -42,6 +43,10 @@ impl ProgramInstance { Ok(module_id) } + pub fn with_host_module(&mut self, name: &str) -> HostModuleBuilder { + HostModuleBuilder::new(&mut self.store) + } + /// Get one of the modules by name pub fn module(&self, name: &str) -> Option { self.modules.get(name).cloned() diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index a32f114..42a90b1 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -1,5 +1,6 @@ use std::mem; use std::ops; +use std::sync::Arc; use std::{u32, usize}; use std::fmt::{self, Display}; use std::iter::repeat; @@ -31,7 +32,7 @@ pub struct FunctionContext { /// Function return type. pub return_type: BlockType, /// Local variables. - pub locals: Vec, + pub locals: Vec, /// Values stack. pub value_stack: StackWithLimit, /// Blocks frames stack. @@ -78,31 +79,24 @@ impl<'store> Interpreter<'store> { let mut function_context = function_stack.pop_back().expect("on loop entry - not empty; on loop continue - checking for emptiness; qed"); let function_ref = function_context.function; let function_return = { - let function_body = function_ref.resolve(self.store).body(); + let func_instance = function_ref.resolve(self.store).clone(); + + match func_instance { + FuncInstance::Defined { body, .. } => { + let function_body = body; - match function_body { - Some(function_body) => { if !function_context.is_initialized() { let return_type = function_context.return_type; - function_context.initialize(&function_body.locals)?; + function_context.initialize(&function_body.locals); function_context.push_frame(&function_body.labels, BlockFrameType::Function, return_type)?; } self.do_run_function(&mut function_context, function_body.opcodes.elements(), &function_body.labels)? }, - None => { - // move locals back to the stack - let locals_to_move: Vec<_> = function_context.locals.drain(..).collect(); - for local in locals_to_move { - function_context.value_stack_mut().push(local.get())?; - } - let nested_context = CallerContext::nested(&mut function_context); - - // TODO: Call host functions - // let result = function_ref.module.call_internal_function(nested_context, function_ref.internal_index)?; - // RunResult::Return(result) - - panic!() + FuncInstance::Host { host_func, .. } => { + let args: Vec<_> = function_context.locals.drain(..).collect(); + let result = self.store.invoke_host(host_func, args)?; + RunResult::Return(result) }, } }; @@ -977,7 +971,7 @@ impl<'store> Interpreter<'store> { } impl<'a> FunctionContext { - pub fn new<'store>(store: &'store Store, function: FuncId, value_stack_limit: usize, frame_stack_limit: usize, function_type: &FunctionSignature, args: Vec) -> Self { + pub fn new<'store>(store: &'store Store, function: FuncId, value_stack_limit: usize, frame_stack_limit: usize, function_type: &FunctionSignature, args: Vec) -> Self { let func_instance = function.resolve(store); let module = match *func_instance { FuncInstance::Defined { module, .. } => module, @@ -1026,16 +1020,15 @@ impl<'a> FunctionContext { self.is_initialized } - pub fn initialize(&mut self, locals: &[Local]) -> Result<(), Error> { + pub fn initialize(&mut self, locals: &[Local]) { debug_assert!(!self.is_initialized); self.is_initialized = true; let locals = locals.iter() .flat_map(|l| repeat(l.value_type().into()).take(l.count() as usize)) - .map(|vt| VariableInstance::new(true, vt, RuntimeValue::default(vt))) - .collect::, _>>()?; + .map(|vt| RuntimeValue::default(vt)) + .collect::>(); self.locals.extend(locals); - Ok(()) } pub fn module(&self) -> ModuleId { @@ -1043,16 +1036,17 @@ impl<'a> FunctionContext { } pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result { - self.locals.get_mut(index) - .ok_or(Error::Local(format!("expected to have local with index {}", index))) - .and_then(|l| l.set(value)) - .map(|_| InstructionOutcome::RunNextInstruction) + let l = self.locals.get_mut(index) + .ok_or(Error::Local(format!("expected to have local with index {}", index)))?; + + *l = value; + Ok(InstructionOutcome::RunNextInstruction) } pub fn get_local(&mut self, index: usize) -> Result { self.locals.get(index) + .cloned() .ok_or(Error::Local(format!("expected to have local with index {}", index))) - .map(|l| l.get()) } pub fn value_stack(&self) -> &StackWithLimit { @@ -1136,7 +1130,7 @@ fn effective_address(address: u32, offset: u32) -> Result { } } -pub fn prepare_function_args(function_type: &FunctionSignature, caller_stack: &mut StackWithLimit) -> Result, Error> { +pub fn prepare_function_args(function_type: &FunctionSignature, caller_stack: &mut StackWithLimit) -> Result, Error> { let mut args = function_type.params().iter().rev().map(|param_type| { let param_value = caller_stack.pop()?; let actual_type = param_value.variable_type(); @@ -1145,7 +1139,7 @@ pub fn prepare_function_args(function_type: &FunctionSignature, caller_stack: &m return Err(Error::Function(format!("invalid parameter type {:?} when expected {:?}", actual_type, expected_type))); } - VariableInstance::new(true, expected_type, param_value) + Ok(param_value) }).collect::, _>>()?; args.reverse(); Ok(args) diff --git a/src/interpreter/store.rs b/src/interpreter/store.rs index 08061b6..b3cc024 100644 --- a/src/interpreter/store.rs +++ b/src/interpreter/store.rs @@ -7,6 +7,7 @@ use elements::{FunctionType, GlobalEntry, GlobalType, InitExpr, MemoryType, Modu Opcode, Opcodes, Local, TableType, Type, Internal}; use interpreter::{Error, RuntimeValue, MemoryInstance, TableInstance, ExecutionParams, CallerContext, FunctionSignature}; use interpreter::runner::{FunctionContext, prepare_function_args, Interpreter}; +use interpreter::host::AnyFunc; use validation::validate_module; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT}; use common::stack::StackWithLimit; @@ -118,6 +119,7 @@ pub enum ExternVal { Global(GlobalId), } +#[derive(Clone, Debug)] pub enum FuncInstance { Defined { func_type: TypeId, @@ -138,15 +140,9 @@ impl FuncInstance { } } } - - pub fn body(&self) -> Option> { - match *self { - FuncInstance::Defined { ref body, .. } => Some(Arc::clone(body)), - FuncInstance::Host { .. } => None, - } - } } +#[derive(Clone, Debug)] pub struct FuncBody { pub locals: Vec, pub opcodes: Opcodes, @@ -197,6 +193,7 @@ pub struct Store { // However, they can be referenced in several places, so it is handy to have it here. modules: Vec, types: Vec, + host_funcs: Vec>, } impl Store { @@ -216,7 +213,7 @@ impl Store { .expect("ID should always be a valid index") } - fn alloc_func_type(&mut self, func_type: FunctionType) -> TypeId { + pub fn alloc_func_type(&mut self, func_type: FunctionType) -> TypeId { self.types.push(func_type); let type_id = self.types.len() - 1; TypeId(type_id as u32) @@ -233,7 +230,18 @@ impl Store { FuncId(func_id as u32) } - // TODO: alloc_host_func + pub fn alloc_host_func(&mut self, func_type: TypeId, anyfunc: Box) -> FuncId { + self.host_funcs.push(anyfunc); + let host_func_id = self.host_funcs.len() - 1; + + let func = FuncInstance::Host { + func_type, + host_func: HostFuncId(host_func_id as u32), + }; + self.funcs.push(func); + let func_id = self.funcs.len() - 1; + FuncId(func_id as u32) + } fn alloc_table(&mut self, table_type: &TableType) -> Result { let table = TableInstance::new(table_type)?; @@ -453,6 +461,10 @@ impl Store { interpreter.run_function(inner) } + pub fn invoke_host(&mut self, host_func: HostFuncId, args: Vec) -> Result, Error> { + panic!() + } + pub fn write_global(&mut self, global: GlobalId, val: RuntimeValue) -> Result<(), Error> { let global_instance = self.globals.get_mut(global.0 as usize).expect("ID should be always valid"); if !global_instance.mutable {