mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-08 12:31: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 value;
|
||||||
mod variable;
|
mod variable;
|
||||||
mod store;
|
mod store;
|
||||||
|
mod host;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -4,6 +4,7 @@ use elements::Module;
|
|||||||
use interpreter::Error;
|
use interpreter::Error;
|
||||||
use interpreter::module::{ExecutionParams};
|
use interpreter::module::{ExecutionParams};
|
||||||
use interpreter::store::{Store, ModuleId};
|
use interpreter::store::{Store, ModuleId};
|
||||||
|
use interpreter::host::HostModuleBuilder;
|
||||||
|
|
||||||
/// Program instance. Program is a set of instantiated modules.
|
/// Program instance. Program is a set of instantiated modules.
|
||||||
pub struct ProgramInstance {
|
pub struct ProgramInstance {
|
||||||
@ -42,6 +43,10 @@ impl ProgramInstance {
|
|||||||
Ok(module_id)
|
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
|
/// Get one of the modules by name
|
||||||
pub fn module(&self, name: &str) -> Option<ModuleId> {
|
pub fn module(&self, name: &str) -> Option<ModuleId> {
|
||||||
self.modules.get(name).cloned()
|
self.modules.get(name).cloned()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::{u32, usize};
|
use std::{u32, usize};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::iter::repeat;
|
use std::iter::repeat;
|
||||||
@ -31,7 +32,7 @@ pub struct FunctionContext {
|
|||||||
/// Function return type.
|
/// Function return type.
|
||||||
pub return_type: BlockType,
|
pub return_type: BlockType,
|
||||||
/// Local variables.
|
/// Local variables.
|
||||||
pub locals: Vec<VariableInstance>,
|
pub locals: Vec<RuntimeValue>,
|
||||||
/// Values stack.
|
/// Values stack.
|
||||||
pub value_stack: StackWithLimit<RuntimeValue>,
|
pub value_stack: StackWithLimit<RuntimeValue>,
|
||||||
/// Blocks frames stack.
|
/// 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 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_ref = function_context.function;
|
||||||
let function_return = {
|
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() {
|
if !function_context.is_initialized() {
|
||||||
let return_type = function_context.return_type;
|
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)?;
|
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)?
|
self.do_run_function(&mut function_context, function_body.opcodes.elements(), &function_body.labels)?
|
||||||
},
|
},
|
||||||
None => {
|
FuncInstance::Host { host_func, .. } => {
|
||||||
// move locals back to the stack
|
let args: Vec<_> = function_context.locals.drain(..).collect();
|
||||||
let locals_to_move: Vec<_> = function_context.locals.drain(..).collect();
|
let result = self.store.invoke_host(host_func, args)?;
|
||||||
for local in locals_to_move {
|
RunResult::Return(result)
|
||||||
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!()
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -977,7 +971,7 @@ impl<'store> Interpreter<'store> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FunctionContext {
|
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 func_instance = function.resolve(store);
|
||||||
let module = match *func_instance {
|
let module = match *func_instance {
|
||||||
FuncInstance::Defined { module, .. } => module,
|
FuncInstance::Defined { module, .. } => module,
|
||||||
@ -1026,16 +1020,15 @@ impl<'a> FunctionContext {
|
|||||||
self.is_initialized
|
self.is_initialized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize(&mut self, locals: &[Local]) -> Result<(), Error> {
|
pub fn initialize(&mut self, locals: &[Local]) {
|
||||||
debug_assert!(!self.is_initialized);
|
debug_assert!(!self.is_initialized);
|
||||||
self.is_initialized = true;
|
self.is_initialized = true;
|
||||||
|
|
||||||
let locals = locals.iter()
|
let locals = locals.iter()
|
||||||
.flat_map(|l| repeat(l.value_type().into()).take(l.count() as usize))
|
.flat_map(|l| repeat(l.value_type().into()).take(l.count() as usize))
|
||||||
.map(|vt| VariableInstance::new(true, vt, RuntimeValue::default(vt)))
|
.map(|vt| RuntimeValue::default(vt))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Vec<_>>();
|
||||||
self.locals.extend(locals);
|
self.locals.extend(locals);
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn module(&self) -> ModuleId {
|
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> {
|
pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result<InstructionOutcome, Error> {
|
||||||
self.locals.get_mut(index)
|
let l = self.locals.get_mut(index)
|
||||||
.ok_or(Error::Local(format!("expected to have local with index {}", index)))
|
.ok_or(Error::Local(format!("expected to have local with index {}", index)))?;
|
||||||
.and_then(|l| l.set(value))
|
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
*l = value;
|
||||||
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(&mut self, index: usize) -> Result<RuntimeValue, Error> {
|
pub fn get_local(&mut self, index: usize) -> Result<RuntimeValue, Error> {
|
||||||
self.locals.get(index)
|
self.locals.get(index)
|
||||||
|
.cloned()
|
||||||
.ok_or(Error::Local(format!("expected to have local with index {}", index)))
|
.ok_or(Error::Local(format!("expected to have local with index {}", index)))
|
||||||
.map(|l| l.get())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value_stack(&self) -> &StackWithLimit<RuntimeValue> {
|
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 mut args = function_type.params().iter().rev().map(|param_type| {
|
||||||
let param_value = caller_stack.pop()?;
|
let param_value = caller_stack.pop()?;
|
||||||
let actual_type = param_value.variable_type();
|
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)));
|
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<_>, _>>()?;
|
}).collect::<Result<Vec<_>, _>>()?;
|
||||||
args.reverse();
|
args.reverse();
|
||||||
Ok(args)
|
Ok(args)
|
||||||
|
@ -7,6 +7,7 @@ use elements::{FunctionType, GlobalEntry, GlobalType, InitExpr, MemoryType, Modu
|
|||||||
Opcode, Opcodes, Local, TableType, Type, Internal};
|
Opcode, Opcodes, Local, TableType, Type, Internal};
|
||||||
use interpreter::{Error, RuntimeValue, MemoryInstance, TableInstance, ExecutionParams, CallerContext, FunctionSignature};
|
use interpreter::{Error, RuntimeValue, MemoryInstance, TableInstance, ExecutionParams, CallerContext, FunctionSignature};
|
||||||
use interpreter::runner::{FunctionContext, prepare_function_args, Interpreter};
|
use interpreter::runner::{FunctionContext, prepare_function_args, Interpreter};
|
||||||
|
use interpreter::host::AnyFunc;
|
||||||
use validation::validate_module;
|
use validation::validate_module;
|
||||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
|
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
|
||||||
use common::stack::StackWithLimit;
|
use common::stack::StackWithLimit;
|
||||||
@ -118,6 +119,7 @@ pub enum ExternVal {
|
|||||||
Global(GlobalId),
|
Global(GlobalId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum FuncInstance {
|
pub enum FuncInstance {
|
||||||
Defined {
|
Defined {
|
||||||
func_type: TypeId,
|
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 struct FuncBody {
|
||||||
pub locals: Vec<Local>,
|
pub locals: Vec<Local>,
|
||||||
pub opcodes: Opcodes,
|
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.
|
// However, they can be referenced in several places, so it is handy to have it here.
|
||||||
modules: Vec<ModuleInstance>,
|
modules: Vec<ModuleInstance>,
|
||||||
types: Vec<FunctionType>,
|
types: Vec<FunctionType>,
|
||||||
|
host_funcs: Vec<Box<AnyFunc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Store {
|
impl Store {
|
||||||
@ -216,7 +213,7 @@ impl Store {
|
|||||||
.expect("ID should always be a valid index")
|
.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);
|
self.types.push(func_type);
|
||||||
let type_id = self.types.len() - 1;
|
let type_id = self.types.len() - 1;
|
||||||
TypeId(type_id as u32)
|
TypeId(type_id as u32)
|
||||||
@ -233,7 +230,18 @@ impl Store {
|
|||||||
FuncId(func_id as u32)
|
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> {
|
fn alloc_table(&mut self, table_type: &TableType) -> Result<TableId, Error> {
|
||||||
let table = TableInstance::new(table_type)?;
|
let table = TableInstance::new(table_type)?;
|
||||||
@ -453,6 +461,10 @@ impl Store {
|
|||||||
interpreter.run_function(inner)
|
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> {
|
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");
|
let global_instance = self.globals.get_mut(global.0 as usize).expect("ID should be always valid");
|
||||||
if !global_instance.mutable {
|
if !global_instance.mutable {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user