mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-05 19:11:35 +00:00
Host module sketch
This commit is contained in:
parent
31abb05009
commit
5920cd93d5
139
src/interpreter/host.rs
Normal file
139
src/interpreter/host.rs
Normal 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())
|
||||
}
|
||||
}
|
@ -140,6 +140,7 @@ mod table;
|
||||
mod value;
|
||||
mod variable;
|
||||
mod store;
|
||||
mod host;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user