Host module sketch

This commit is contained in:
Sergey Pepyakin 2017-12-11 16:28:05 +01:00
parent 31abb05009
commit 5920cd93d5
5 changed files with 190 additions and 39 deletions

139
src/interpreter/host.rs Normal file
View File

@ -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<String, ExternVal>,
_marker: PhantomData<St>,
}
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<Option<Ret>, Error> + 'static,
Ret: AsReturn + 'static,
P1: FromArg + 'static,
F: Into<Func1<Cl, St, Ret, P1>>,
>(
&mut self,
name: &str,
f: F,
) {
let func_type = Func1::<Cl, St, Ret, P1>::derive_func_type();
let type_id = self.store.alloc_func_type(func_type);
let anyfunc = Box::new(f.into()) as Box<AnyFunc>;
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<Option<RuntimeValue>, 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<RuntimeValue>;
fn value_type() -> Option<ValueType>;
}
impl AsReturn for i32 {
fn as_return_val(self) -> Option<RuntimeValue> {
Some(self.into())
}
fn value_type() -> Option<ValueType> {
Some(ValueType::I32)
}
}
impl AsReturn for () {
fn as_return_val(self) -> Option<RuntimeValue> {
None
}
fn value_type() -> Option<ValueType> {
None
}
}
pub struct Func1<Cl: Fn(&mut St, P1) -> Result<Option<Ret>, 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<Option<Ret>, Error>,
> AnyFunc for Func1<Cl, St, Ret, P1> {
fn call_as_any(
&self,
state: &mut Any,
args: &[RuntimeValue],
) -> Result<Option<RuntimeValue>, Error> {
let state = state.downcast_mut::<St>().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<St: 'static, Ret: AsReturn, P1: FromArg, Cl: Fn(&mut St, P1) -> Result<Option<Ret>, Error>> From<Cl>
for Func1<Cl, St, Ret, P1> {
fn from(cl: Cl) -> Self {
Func1 {
closure: cl,
_marker: PhantomData,
}
}
}
impl<
St: 'static,
Ret: AsReturn,
P1: FromArg,
Cl: Fn(&mut St, P1) -> Result<Option<Ret>, Error>,
> Func1<Cl, St, Ret, P1> {
fn derive_func_type() -> FunctionType {
FunctionType::new(vec![P1::value_type()], Ret::value_type())
}
}

View File

@ -140,6 +140,7 @@ mod table;
mod value;
mod variable;
mod store;
mod host;
#[cfg(test)]
mod tests;

View File

@ -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<St: 'static>(&mut self, name: &str) -> HostModuleBuilder<St> {
HostModuleBuilder::new(&mut self.store)
}
/// Get one of the modules by name
pub fn module(&self, name: &str) -> Option<ModuleId> {
self.modules.get(name).cloned()

View File

@ -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<VariableInstance>,
pub locals: Vec<RuntimeValue>,
/// Values stack.
pub value_stack: StackWithLimit<RuntimeValue>,
/// 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<VariableInstance>) -> Self {
pub fn new<'store>(store: &'store Store, function: FuncId, value_stack_limit: usize, frame_stack_limit: usize, function_type: &FunctionSignature, args: Vec<RuntimeValue>) -> 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::<Result<Vec<_>, _>>()?;
.map(|vt| RuntimeValue::default(vt))
.collect::<Vec<_>>();
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<InstructionOutcome, Error> {
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<RuntimeValue, Error> {
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<RuntimeValue> {
@ -1136,7 +1130,7 @@ fn effective_address(address: u32, offset: u32) -> Result<u32, Error> {
}
}
pub fn prepare_function_args(function_type: &FunctionSignature, caller_stack: &mut StackWithLimit<RuntimeValue>) -> Result<Vec<VariableInstance>, Error> {
pub fn prepare_function_args(function_type: &FunctionSignature, caller_stack: &mut StackWithLimit<RuntimeValue>) -> Result<Vec<RuntimeValue>, 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::<Result<Vec<_>, _>>()?;
args.reverse();
Ok(args)

View File

@ -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<Arc<FuncBody>> {
match *self {
FuncInstance::Defined { ref body, .. } => Some(Arc::clone(body)),
FuncInstance::Host { .. } => None,
}
}
}
#[derive(Clone, Debug)]
pub struct FuncBody {
pub locals: Vec<Local>,
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<ModuleInstance>,
types: Vec<FunctionType>,
host_funcs: Vec<Box<AnyFunc>>,
}
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<AnyFunc>) -> 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<TableId, Error> {
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<RuntimeValue>) -> Result<Option<RuntimeValue>, 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 {