Use Externals mechanism instead of HostState

This commit is contained in:
Sergey Pepyakin
2018-01-05 17:52:29 +03:00
parent 8c7dc1b529
commit 26ab9f9e81
10 changed files with 211 additions and 645 deletions

View File

@ -3,7 +3,7 @@
extern crate parity_wasm;
use std::env::args;
use parity_wasm::interpreter::{ModuleInstance, HostState};
use parity_wasm::interpreter::{ModuleInstance, EmptyExternals};
fn main() {
let args: Vec<_> = args().collect();
@ -23,12 +23,12 @@ fn main() {
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
let main = ModuleInstance::new(&module)
.run_start(&mut HostState::default())
.run_start(&mut EmptyExternals)
.expect("Failed to initialize module");
// The argument should be parsable as a valid integer
let argument: i32 = args[2].parse().expect("Integer argument required");
// "_call" export of function to be executed with an i32 argument and prints the result of execution
println!("Result: {:?}", main.invoke_export("_call", &[parity_wasm::RuntimeValue::I32(argument)], &mut HostState::default()));
println!("Result: {:?}", main.invoke_export("_call", &[parity_wasm::RuntimeValue::I32(argument)], &mut EmptyExternals));
}

View File

@ -4,7 +4,7 @@ use std::env::args;
use parity_wasm::RuntimeValue;
use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType};
use parity_wasm::interpreter::{ModuleInstance, HostState};
use parity_wasm::interpreter::{ModuleInstance, EmptyExternals};
fn main() {
@ -75,8 +75,8 @@ fn main() {
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
let main = ModuleInstance::new(&module)
.run_start(&mut HostState::default())
.run_start(&mut EmptyExternals)
.expect("Failed to initialize module");
println!("Result: {:?}", main.invoke_export(func_name, &args, &mut HostState::default()).expect(""));
println!("Result: {:?}", main.invoke_export(func_name, &args, &mut EmptyExternals).expect(""));
}

View File

@ -3,10 +3,11 @@ extern crate parity_wasm;
use std::env;
use std::fmt;
use std::rc::Rc;
use parity_wasm::elements::Module;
use parity_wasm::elements::{Module, FunctionType, ValueType, TableType, GlobalType, MemoryType};
use parity_wasm::interpreter::{
Error as InterpreterError, HostModule, HostModuleBuilder,
ModuleInstance, UserError, HostState, StateKey
Error as InterpreterError, ModuleInstance, UserError,
HostFuncIndex, Externals, RuntimeValue, GlobalInstance, TableInstance, MemoryInstance,
TableRef, MemoryRef, GlobalRef, FuncRef, TryInto, ImportResolver, FuncInstance,
};
#[derive(Debug)]
@ -139,54 +140,123 @@ struct Runtime<'a> {
game: &'a mut tictactoe::Game,
}
unsafe impl<'a> StateKey for Runtime<'a> {
type Static = Runtime<'static>;
const SET_FUNC_INDEX: HostFuncIndex = 0;
const GET_FUNC_INDEX: HostFuncIndex = 1;
impl<'a> Externals for Runtime<'a> {
fn invoke_index(
&mut self,
index: HostFuncIndex,
args: &[RuntimeValue],
) -> Result<Option<RuntimeValue>, InterpreterError> {
match index {
SET_FUNC_INDEX => {
let idx: i32 = args[0].try_into().unwrap();
self.game.set(idx, self.player)?;
Ok(None)
}
GET_FUNC_INDEX => {
let idx: i32 = args[0].try_into().unwrap();
let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?);
Ok(Some(val.into()))
}
_ => panic!("unknown function index")
}
}
fn check_signature(&self, index: HostFuncIndex, sig: &FunctionType) -> bool {
match index {
SET_FUNC_INDEX => {
sig.params() == &[ValueType::I32] && sig.return_type() == None
}
GET_FUNC_INDEX => {
sig.params() == &[ValueType::I32] && sig.return_type() == Some(ValueType::I32)
}
_ => panic!("unknown function index")
}
}
fn memory_by_index(&self, _index: usize) -> &MemoryInstance {
panic!("host module doesn't export any memories")
}
fn table_by_index(&self, _index: usize) -> &TableInstance {
panic!("host module doesn't export any tables")
}
fn global_by_index(&self, _index: usize) -> &GlobalInstance {
panic!("host module doesn't export any globals")
}
}
struct RuntimeImportResolver;
impl<'a> ImportResolver for RuntimeImportResolver {
fn resolve_func(
&self,
field_name: &str,
_func_type: &FunctionType,
) -> Result<FuncRef, InterpreterError> {
let func_ref = match field_name {
"set" => {
FuncInstance::alloc_host(FunctionType::new(vec![ValueType::I32], None), SET_FUNC_INDEX)
},
"get" => FuncInstance::alloc_host(FunctionType::new(vec![ValueType::I32], Some(ValueType::I32)), GET_FUNC_INDEX),
_ => return Err(
InterpreterError::Function(
format!("host module doesn't export function with name {}", field_name)
)
)
};
Ok(func_ref)
}
fn resolve_global(
&self,
_field_name: &str,
_global_type: &GlobalType,
) -> Result<GlobalRef, InterpreterError> {
Err(
InterpreterError::Global("host module doesn't export any globals".to_owned())
)
}
fn resolve_memory(
&self,
_field_name: &str,
_memory_type: &MemoryType,
) -> Result<MemoryRef, InterpreterError> {
Err(
InterpreterError::Global("host module doesn't export any memories".to_owned())
)
}
fn resolve_table(
&self,
_field_name: &str,
_table_type: &TableType,
) -> Result<TableRef, InterpreterError> {
Err(
InterpreterError::Global("host module doesn't export any tables".to_owned())
)
}
}
fn instantiate(
module: &Module,
env: &HostModule,
) -> Result<Rc<ModuleInstance>, Error> {
let instance = ModuleInstance::new(module)
.with_import("env", &*env)
.run_start(&mut HostState::default())?;
.with_import("env", &RuntimeImportResolver)
.assert_no_start()?;
Ok(instance)
}
fn env_host_module() -> HostModule {
HostModuleBuilder::new()
.with_func1(
"set",
|state: &mut HostState, idx: i32| -> Result<(), InterpreterError> {
state.with_mut(move |runtime: &mut Runtime| -> Result<(), InterpreterError> {
runtime.game.set(idx, runtime.player)?;
Ok(())
})
},
)
.with_func1(
"get",
|state: &mut HostState, idx: i32| -> Result<i32, InterpreterError> {
state.with(move |runtime: &Runtime| -> Result<i32, InterpreterError> {
let val: i32 = tictactoe::Player::into_i32(runtime.game.get(idx)?);
Ok(val)
})
},
)
.build()
}
fn play<'a>(
x_module: &Module,
o_module: &Module,
host_module: &HostModule,
game: &'a mut tictactoe::Game,
fn play(
x_instance: Rc<ModuleInstance>,
o_instance: Rc<ModuleInstance>,
game: &mut tictactoe::Game,
) -> Result<tictactoe::GameResult, Error> {
// Instantiate modules of X and O players.
let x_instance = instantiate(x_module, host_module)?;
let o_instance = instantiate(o_module, host_module)?;
let mut turn_of = tictactoe::Player::X;
let game_result = loop {
let (instance, next_turn_of) = match turn_of {
@ -199,11 +269,7 @@ fn play<'a>(
player: turn_of,
game: game,
};
{
let mut host_state = HostState::new();
host_state.insert::<Runtime>(&mut runtime);
let _ = instance.invoke_export("mk_turn", &[], &mut host_state)?;
}
let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?;
}
match game.game_result() {
@ -219,7 +285,6 @@ fn play<'a>(
fn main() {
let mut game = tictactoe::Game::new();
let env_host_module = env_host_module();
let args: Vec<_> = env::args().collect();
if args.len() < 3 {
@ -229,6 +294,10 @@ fn main() {
let x_module = parity_wasm::deserialize_file(&args[1]).expect("X player module to load");
let o_module = parity_wasm::deserialize_file(&args[2]).expect("Y player module to load");
let result = play(&x_module, &o_module, &env_host_module, &mut game);
// Instantiate modules of X and O players.
let x_instance = instantiate(&x_module).unwrap();
let o_instance = instantiate(&o_module).unwrap();
let result = play(x_instance, o_instance, &mut game);
println!("result = {:?}, game = {:#?}", result, game);
}

View File

@ -4,10 +4,9 @@ use std::collections::HashMap;
use std::borrow::Cow;
use elements::{FunctionType, Local, Opcodes};
use interpreter::{Error, ModuleInstance};
use interpreter::host::{Externals, HostFuncIndex};
use interpreter::runner::{prepare_function_args, FunctionContext, Interpreter};
use interpreter::host::HostFunc;
use interpreter::value::RuntimeValue;
use interpreter::state::HostState;
use common::stack::StackWithLimit;
use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
@ -29,8 +28,8 @@ pub enum FuncInstance {
body: Rc<FuncBody>,
},
Host {
func_type: Rc<FunctionType>,
host_func: Rc<HostFunc>,
func_type: FunctionType,
host_func: HostFuncIndex,
},
}
@ -57,7 +56,7 @@ impl fmt::Debug for FuncInstance {
}
impl FuncInstance {
pub fn alloc_internal(
pub(crate) fn alloc_internal(
module: Rc<ModuleInstance>,
func_type: Rc<FunctionType>,
body: FuncBody,
@ -70,7 +69,7 @@ impl FuncInstance {
FuncRef(Rc::new(func))
}
pub fn alloc_host(func_type: Rc<FunctionType>, host_func: Rc<HostFunc>) -> FuncRef {
pub fn alloc_host(func_type: FunctionType, host_func: HostFuncIndex) -> FuncRef {
let func = FuncInstance::Host {
func_type,
host_func,
@ -78,10 +77,10 @@ impl FuncInstance {
FuncRef(Rc::new(func))
}
pub fn func_type(&self) -> Rc<FunctionType> {
pub fn func_type(&self) -> &FunctionType {
match *self {
FuncInstance::Internal { ref func_type, .. } |
FuncInstance::Host { ref func_type, .. } => Rc::clone(func_type),
FuncInstance::Internal { ref func_type, .. } => func_type,
FuncInstance::Host { ref func_type, .. } => func_type,
}
}
@ -92,14 +91,14 @@ impl FuncInstance {
}
}
pub fn invoke<'a, 'b: 'a>(
pub fn invoke<E: Externals>(
func: FuncRef,
args: Cow<[RuntimeValue]>,
state: &'a mut HostState<'b>,
externals: &mut E,
) -> Result<Option<RuntimeValue>, Error> {
enum InvokeKind<'a> {
Internal(FunctionContext),
Host(Rc<HostFunc>, &'a [RuntimeValue]),
Host(HostFuncIndex, &'a [RuntimeValue]),
}
let result = match *func {
@ -117,16 +116,16 @@ impl FuncInstance {
InvokeKind::Internal(context)
}
FuncInstance::Host { ref host_func, .. } => {
InvokeKind::Host(Rc::clone(host_func), &*args)
InvokeKind::Host(*host_func, &*args)
}
};
match result {
InvokeKind::Internal(ctx) => {
let mut interpreter = Interpreter::new(state);
let mut interpreter = Interpreter::new(externals);
interpreter.run_function(ctx)
}
InvokeKind::Host(host_func, args) => host_func(state, args),
InvokeKind::Host(host_func, args) => externals.invoke_index(host_func, args),
}
}
}

View File

@ -1,405 +1,50 @@
use std::rc::Rc;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use elements::{FunctionType, GlobalType, MemoryType, TableType, ValueType};
use interpreter::module::{ExternVal, ModuleInstance};
use interpreter::func::FuncRef;
use interpreter::global::GlobalRef;
use interpreter::memory::MemoryRef;
use interpreter::table::TableRef;
use interpreter::func::FuncInstance;
use elements::FunctionType;
use interpreter::global::GlobalInstance;
use interpreter::memory::MemoryInstance;
use interpreter::table::TableInstance;
use interpreter::value::{RuntimeValue, TryInto};
use interpreter::value::RuntimeValue;
use interpreter::Error;
use interpreter::ImportResolver;
use interpreter::state::HostState;
pub type HostFunc = Fn(&mut HostState, &[RuntimeValue])
-> Result<Option<RuntimeValue>, Error>;
pub type HostFuncIndex = u32;
pub struct HostModuleBuilder {
exports: HashMap<String, ExternVal>,
}
impl HostModuleBuilder {
pub fn new() -> Self {
HostModuleBuilder {
exports: HashMap::new(),
}
}
pub fn insert_func0<
Cl: Fn(&mut HostState) -> Result<Ret, Error> + 'static,
Ret: IntoReturnVal + 'static,
N: Into<String>,
>(
&mut self,
name: N,
f: Cl,
) {
let func_type = FunctionType::new(vec![], Ret::value_type());
let host_func = Rc::new(
move |state: &mut HostState, args: &[RuntimeValue]| -> Result<Option<RuntimeValue>, Error> {
assert!(args.len() == 0);
let result = f(state)?.into_return_val();
Ok(result)
},
);
let func = FuncInstance::alloc_host(Rc::new(func_type), host_func);
self.insert_func(name, func);
}
pub fn insert_func1<
Cl: Fn(&mut HostState, P1) -> Result<Ret, Error> + 'static,
Ret: IntoReturnVal + 'static,
P1: FromArg + 'static,
N: Into<String>,
>(
&mut self,
name: N,
f: Cl,
) {
let func_type = FunctionType::new(vec![P1::value_type()], Ret::value_type());
let host_func = Rc::new(
move |state: &mut HostState, args: &[RuntimeValue]| -> Result<Option<RuntimeValue>, Error> {
assert!(args.len() == 1);
let mut args = args.into_iter();
let result = f(
state,
P1::from_arg(args.next().unwrap())
)?.into_return_val();
Ok(result)
},
);
let func = FuncInstance::alloc_host(Rc::new(func_type), host_func);
self.insert_func(name, func);
}
pub fn insert_func2<
Cl: Fn(&mut HostState, P1, P2) -> Result<Ret, Error> + 'static,
Ret: IntoReturnVal + 'static,
P1: FromArg + 'static,
P2: FromArg + 'static,
N: Into<String>,
>(
&mut self,
name: N,
f: Cl,
) {
let func_type =
FunctionType::new(vec![P1::value_type(), P2::value_type()], Ret::value_type());
let host_func = Rc::new(
move |state: &mut HostState, args: &[RuntimeValue]| -> Result<Option<RuntimeValue>, Error> {
assert!(args.len() == 2);
let mut args = args.into_iter();
let result = f(
state,
P1::from_arg(args.next().unwrap()),
P2::from_arg(args.next().unwrap()),
)?.into_return_val();
Ok(result)
},
);
let func = FuncInstance::alloc_host(Rc::new(func_type), host_func);
self.insert_func(name, func);
}
pub fn insert_func3<
Cl: Fn(&mut HostState, P1, P2, P3) -> Result<Ret, Error> + 'static,
Ret: IntoReturnVal + 'static,
P1: FromArg + 'static,
P2: FromArg + 'static,
P3: FromArg + 'static,
N: Into<String>,
>(
&mut self,
name: N,
f: Cl,
) {
let func_type = FunctionType::new(
vec![P1::value_type(), P2::value_type(), P3::value_type()],
Ret::value_type(),
);
let host_func = Rc::new(
move |state: &mut HostState, args: &[RuntimeValue]| -> Result<Option<RuntimeValue>, Error> {
assert!(args.len() == 3);
let mut args = args.into_iter();
let result = f(
state,
P1::from_arg(args.next().unwrap()),
P2::from_arg(args.next().unwrap()),
P3::from_arg(args.next().unwrap()),
)?.into_return_val();
Ok(result)
},
);
let func = FuncInstance::alloc_host(Rc::new(func_type), host_func);
self.insert_func(name, func);
}
pub fn insert_func4<
Cl: Fn(&mut HostState, P1, P2, P3, P4) -> Result<Ret, Error> + 'static,
Ret: IntoReturnVal + 'static,
P1: FromArg + 'static,
P2: FromArg + 'static,
P3: FromArg + 'static,
P4: FromArg + 'static,
N: Into<String>,
>(
&mut self,
name: N,
f: Cl,
) {
let func_type = FunctionType::new(
vec![
P1::value_type(),
P2::value_type(),
P3::value_type(),
P4::value_type(),
],
Ret::value_type(),
);
let host_func = Rc::new(
move |state: &mut HostState, args: &[RuntimeValue]| -> Result<Option<RuntimeValue>, Error> {
assert!(args.len() == 4);
let mut args = args.into_iter();
let result = f(
state,
P1::from_arg(args.next().unwrap()),
P2::from_arg(args.next().unwrap()),
P3::from_arg(args.next().unwrap()),
P4::from_arg(args.next().unwrap()),
)?.into_return_val();
Ok(result)
},
);
let func = FuncInstance::alloc_host(Rc::new(func_type), host_func);
self.insert_func(name, func);
}
pub fn with_func0<
Cl: Fn(&mut HostState) -> Result<Ret, Error> + 'static,
Ret: IntoReturnVal + 'static,
N: Into<String>,
>(
mut self,
name: N,
f: Cl,
) -> Self {
self.insert_func0(name, f);
self
}
pub fn with_func1<
Cl: Fn(&mut HostState, P1) -> Result<Ret, Error> + 'static,
Ret: IntoReturnVal + 'static,
P1: FromArg + 'static,
N: Into<String>,
>(
mut self,
name: N,
f: Cl,
) -> Self {
self.insert_func1(name, f);
self
}
pub fn with_func2<
Cl: Fn(&mut HostState, P1, P2) -> Result<Ret, Error> + 'static,
Ret: IntoReturnVal + 'static,
P1: FromArg + 'static,
P2: FromArg + 'static,
N: Into<String>,
>(
mut self,
name: N,
f: Cl,
) -> Self {
self.insert_func2(name, f);
self
}
pub fn insert_func<N: Into<String>>(&mut self, name: N, func: FuncRef) {
self.insert(name, ExternVal::Func(func));
}
pub fn insert_global<N: Into<String>>(&mut self, name: N, global: GlobalRef) {
self.insert(name, ExternVal::Global(global));
}
pub fn insert_memory<N: Into<String>>(&mut self, name: N, memory: MemoryRef) {
self.insert(name, ExternVal::Memory(memory));
}
pub fn insert_table<N: Into<String>>(&mut self, name: N, table: TableRef) {
self.insert(name, ExternVal::Table(table));
}
pub fn with_global<N: Into<String>>(mut self, name: N, global: GlobalRef) -> Self {
self.insert_global(name, global);
self
}
pub fn with_memory<N: Into<String>>(mut self, name: N, memory: MemoryRef) -> Self {
self.insert_memory(name, memory);
self
}
pub fn with_table<N: Into<String>>(mut self, name: N, table: TableRef) -> Self {
self.insert_table(name, table);
self
}
fn insert<N: Into<String>>(&mut self, name: N, extern_val: ExternVal) {
match self.exports.entry(name.into()) {
Entry::Vacant(v) => v.insert(extern_val),
Entry::Occupied(o) => panic!("Duplicate export name {}", o.key()),
};
}
pub fn build(self) -> HostModule {
let internal_instance = Rc::new(ModuleInstance::with_exports(self.exports));
HostModule { internal_instance }
}
}
pub struct HostModule {
internal_instance: Rc<ModuleInstance>,
}
impl HostModule {
pub fn export_by_name(&self, name: &str) -> Option<ExternVal> {
self.internal_instance.export_by_name(name)
}
}
impl ImportResolver for HostModule {
fn resolve_func(
&self,
field_name: &str,
func_type: &FunctionType,
) -> Result<FuncRef, Error> {
self.internal_instance.resolve_func(field_name, func_type)
}
fn resolve_global(
&self,
field_name: &str,
global_type: &GlobalType,
) -> Result<GlobalRef, Error> {
self.internal_instance
.resolve_global(field_name, global_type)
}
fn resolve_memory(
&self,
field_name: &str,
memory_type: &MemoryType,
) -> Result<MemoryRef, Error> {
self.internal_instance
.resolve_memory(field_name, memory_type)
}
fn resolve_table(
&self,
field_name: &str,
table_type: &TableType,
) -> Result<TableRef, Error> {
self.internal_instance.resolve_table(field_name, table_type)
}
}
pub trait FromArg
where
Self: Sized,
{
fn from_arg(arg: &RuntimeValue) -> Self;
fn value_type() -> ValueType;
}
macro_rules! impl_from_arg {
($ty: ident, $val_ty: ident) => {
impl FromArg for $ty {
fn from_arg(arg: &RuntimeValue) -> Self {
arg
.try_into()
.expect(
concat!("Due to validation, arg expected to be ", stringify!($val_ty))
)
}
fn value_type() -> ValueType {
use self::ValueType::*;
$val_ty
}
}
}
}
impl_from_arg!(i32, I32);
impl_from_arg!(u32, I32);
impl_from_arg!(i64, I64);
impl_from_arg!(u64, I64);
impl_from_arg!(f32, F32);
impl_from_arg!(f64, F64);
pub trait IntoReturnVal {
fn into_return_val(self) -> Option<RuntimeValue>;
fn value_type() -> Option<ValueType>;
}
macro_rules! impl_into_return_val {
($ty: ident, $val_ty: ident) => {
impl IntoReturnVal for $ty {
fn into_return_val(self) -> Option<RuntimeValue> {
Some(self.into())
}
fn value_type() -> Option<ValueType> {
use self::ValueType::*;
Some($val_ty)
}
}
}
}
impl_into_return_val!(i32, I32);
impl_into_return_val!(u32, I32);
impl_into_return_val!(i64, I64);
impl_into_return_val!(u64, I64);
impl_into_return_val!(f32, F32);
impl_into_return_val!(f64, F64);
impl IntoReturnVal for () {
fn into_return_val(self) -> Option<RuntimeValue> {
None
}
fn value_type() -> Option<ValueType> {
None
}
}
trait Externals {
pub trait Externals {
fn invoke_index(
&mut self,
index: u32,
index: HostFuncIndex,
args: &[RuntimeValue],
) -> Result<Option<RuntimeValue>, Error>;
// TODO: or check signature?
fn signature(&self, index: usize) -> &FunctionType;
fn check_signature(&self, index: HostFuncIndex, signature: &FunctionType) -> bool;
fn memory_by_index(&self, index: usize) -> &MemoryInstance;
fn table_by_index(&self, index: usize) -> &TableInstance;
fn global_by_index(&self, index: usize) -> &GlobalInstance;
}
pub struct EmptyExternals;
impl Externals for EmptyExternals {
fn invoke_index(
&mut self,
_index: HostFuncIndex,
_args: &[RuntimeValue],
) -> Result<Option<RuntimeValue>, Error> {
panic!("called invoke_index on EmptyExternals")
}
fn check_signature(&self, _index: HostFuncIndex, _signature: &FunctionType) -> bool {
panic!("called check_signature on EmptyExternals")
}
fn memory_by_index(&self, _index: usize) -> &MemoryInstance {
panic!("called memory_by_index on EmptyExternals")
}
fn table_by_index(&self, _index: usize) -> &TableInstance {
panic!("called table_by_index on EmptyExternals")
}
fn global_by_index(&self, _index: usize) -> &GlobalInstance {
panic!("called global_by_index on EmptyExternals")
}
}

View File

@ -146,7 +146,6 @@ mod host;
mod imports;
mod global;
mod func;
mod state;
#[cfg(test)]
mod tests;
@ -154,10 +153,9 @@ mod tests;
pub use self::memory::{MemoryInstance, MemoryRef};
pub use self::table::{TableInstance, TableRef};
pub use self::program::ProgramInstance;
pub use self::value::RuntimeValue;
pub use self::host::{HostModule, HostModuleBuilder, HostFunc, IntoReturnVal, FromArg};
pub use self::value::{RuntimeValue, TryInto};
pub use self::host::{Externals, HostFuncIndex, EmptyExternals};
pub use self::imports::{ImportResolver, Imports};
pub use self::module::ModuleInstance;
pub use self::global::{GlobalInstance, GlobalRef};
pub use self::func::{FuncInstance, FuncRef};
pub use self::state::{HostState, StateKey};

View File

@ -10,8 +10,8 @@ use interpreter::imports::{ImportResolver, Imports};
use interpreter::global::{GlobalInstance, GlobalRef};
use interpreter::func::{FuncRef, FuncBody, FuncInstance};
use interpreter::table::TableRef;
use interpreter::state::HostState;
use interpreter::memory::MemoryRef;
use interpreter::host::Externals;
use validation::validate_module;
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
@ -187,7 +187,7 @@ impl ModuleInstance {
"Due to validation function type should exists",
);
let actual_fn_type = func.func_type();
if expected_fn_type != actual_fn_type {
if &*expected_fn_type != actual_fn_type {
return Err(Error::Instantiation(format!(
"Expected function with type {:?}, but actual type is {:?} for entry {}",
expected_fn_type,
@ -405,11 +405,11 @@ impl ModuleInstance {
InstantiationBuilder::new(module)
}
pub fn invoke_index<'a>(
pub fn invoke_index<E: Externals>(
&self,
func_idx: u32,
args: &[RuntimeValue],
state: &'a mut HostState<'a>,
state: &mut E,
) -> Result<Option<RuntimeValue>, Error> {
let func_instance = self.func_by_index(func_idx).ok_or_else(|| {
Error::Program(format!(
@ -420,11 +420,11 @@ impl ModuleInstance {
FuncInstance::invoke(func_instance, Cow::Borrowed(args), state)
}
pub fn invoke_export<'a>(
pub fn invoke_export<E: Externals>(
&self,
func_name: &str,
args: &[RuntimeValue],
state: &'a mut HostState<'a>,
state: &mut E,
) -> Result<Option<RuntimeValue>, Error> {
let extern_val = self.export_by_name(func_name).ok_or_else(|| {
Error::Program(format!("Module doesn't have export {}", func_name))
@ -474,7 +474,7 @@ impl<'a> InstantiationBuilder<'a> {
self
}
pub fn run_start<'b>(mut self, state: &'b mut HostState<'b>) -> Result<Rc<ModuleInstance>, Error> {
pub fn run_start<'b, E: Externals>(mut self, state: &'b mut E) -> Result<Rc<ModuleInstance>, Error> {
let imports = self.imports.get_or_insert_with(|| Imports::default());
let instance = ModuleInstance::instantiate_with_imports(self.module, imports)?;

View File

@ -5,10 +5,9 @@ use elements::Module;
use interpreter::Error;
use interpreter::module::{ModuleInstance};
use interpreter::func::{FuncInstance, FuncRef};
use interpreter::host::HostModule;
use interpreter::value::RuntimeValue;
use interpreter::imports::{Imports, ImportResolver};
use interpreter::state::HostState;
use interpreter::host::Externals;
/// Program instance. Program is a set of instantiated modules.
#[deprecated]
@ -27,11 +26,11 @@ impl ProgramInstance {
}
/// Instantiate module with validation.
pub fn add_module<'a>(
pub fn add_module<'a, E: Externals>(
&mut self,
name: &str,
module: Module,
state: &'a mut HostState<'a>,
externals: &'a mut E,
) -> Result<Rc<ModuleInstance>, Error> {
let module_instance = {
let mut imports = Imports::new();
@ -43,7 +42,7 @@ impl ProgramInstance {
}
ModuleInstance::new(&module)
.with_imports(imports)
.run_start(state)?
.run_start(externals)?
};
self.modules.insert(name.to_owned(), Rc::clone(&module_instance));
@ -58,49 +57,41 @@ impl ProgramInstance {
self.resolvers.insert(name.to_owned(), import_resolver);
}
pub fn add_host_module(
&mut self,
name: &str,
host_module: HostModule,
) {
self.resolvers.insert(name.to_owned(), Box::new(host_module) as Box<ImportResolver>);
}
pub fn insert_loaded_module(&mut self, name: &str, module: Rc<ModuleInstance>) {
self.modules.insert(name.to_owned(), module);
}
pub fn invoke_export<'a>(
pub fn invoke_export<'a, E: Externals>(
&mut self,
module_name: &str,
func_name: &str,
args: &[RuntimeValue],
state: &'a mut HostState<'a>,
externals: &'a mut E,
) -> Result<Option<RuntimeValue>, Error> {
let module_instance = self.modules.get(module_name).ok_or_else(|| {
Error::Program(format!("Module {} not found", module_name))
})?;
module_instance.invoke_export(func_name, args, state)
module_instance.invoke_export(func_name, args, externals)
}
pub fn invoke_index<'a>(
pub fn invoke_index<'a, E: Externals>(
&mut self,
module_name: &str,
func_idx: u32,
args: &[RuntimeValue],
state: &'a mut HostState<'a>,
externals: &'a mut E,
) -> Result<Option<RuntimeValue>, Error> {
let module_instance = self.modules.get(module_name).cloned().ok_or_else(|| {
Error::Program(format!("Module {} not found", module_name))
})?;
module_instance.invoke_index(func_idx, args, state)
module_instance.invoke_index(func_idx, args, externals)
}
pub fn invoke_func<'a>(
pub fn invoke_func<'a, E: Externals>(
&mut self,
func_instance: FuncRef,
args: &[RuntimeValue],
state: &'a mut HostState<'a>,
state: &'a mut E,
) -> Result<Option<RuntimeValue>, Error> {
FuncInstance::invoke(func_instance.clone(), Cow::Borrowed(args), state)
}

View File

@ -14,13 +14,13 @@ use interpreter::value::{
RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto,
ArithmeticOps, Integer, Float, LittleEndianConvert, TransmuteInto,
};
use interpreter::host::Externals;
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType};
use common::stack::StackWithLimit;
use interpreter::state::HostState;
/// Function interpreter.
pub struct Interpreter<'a, 'b: 'a> {
state: &'a mut HostState<'b>,
pub struct Interpreter<'a, E: Externals + 'a> {
externals: &'a mut E,
}
/// Function execution context.
@ -64,10 +64,10 @@ enum RunResult {
NestedCall(FuncRef),
}
impl<'a, 'b: 'a> Interpreter<'a, 'b> {
pub fn new(state: &'a mut HostState<'b>) -> Interpreter<'a, 'b> {
impl<'a, E: Externals> Interpreter<'a, E> {
pub fn new(externals: &'a mut E) -> Interpreter<'a, E> {
Interpreter {
state: state,
externals,
}
}
@ -105,7 +105,7 @@ impl<'a, 'b: 'a> Interpreter<'a, 'b> {
},
FuncInstance::Host { ref func_type, .. } => {
let args = prepare_function_args(func_type, &mut function_context.value_stack)?;
let return_val = FuncInstance::invoke(nested_func.clone(), args.into(), self.state)?;
let return_val = FuncInstance::invoke(nested_func.clone(), args.into(), self.externals)?;
if let Some(return_val) = return_val {
function_context.value_stack_mut().push(return_val)?;
}
@ -439,13 +439,14 @@ impl<'a, 'b: 'a> Interpreter<'a, 'b> {
.expect("Due to validation table should exists");
let func_ref = table.get(table_func_idx)?;
{
let actual_function_type = func_ref.func_type();
let required_function_type = context
.module()
.type_by_index(type_idx)
.expect("Due to validation type should exists");
if required_function_type != actual_function_type {
if &*required_function_type != actual_function_type {
return Err(Error::Function(format!(
"expected function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}",
required_function_type.params(),
@ -454,6 +455,7 @@ impl<'a, 'b: 'a> Interpreter<'a, 'b> {
actual_function_type.return_type()
)));
}
}
Ok(InstructionOutcome::ExecuteCall(func_ref))
}

View File

@ -1,138 +0,0 @@
use std::any::{Any, TypeId};
use std::collections::HashMap;
pub unsafe trait StateKey {
type Static: ?Sized + 'static;
}
pub fn type_id<T>() -> TypeId
where
T: StateKey,
T::Static: Any,
{
TypeId::of::<T::Static>()
}
#[derive(Default)]
pub struct HostState<'a> {
data: HashMap<TypeId, *mut ()>,
_marker: ::std::marker::PhantomData<&'a mut ()>,
}
impl<'a> HostState<'a> {
pub fn new() -> Self {
HostState {
data: HashMap::default(),
_marker: ::std::marker::PhantomData,
}
}
pub fn insert<V: StateKey + 'a>(&mut self, val: &'a mut V) {
let ty_id = type_id::<V>();
let ptr = val as *mut V as *mut ();
let existing = self.data.insert(ty_id, ptr);
assert!(existing.is_none());
}
pub fn with<V: StateKey + 'a, R, F: FnOnce(&V) -> R>(&self, f: F) -> R {
let ptr = self.data.get(&type_id::<V>()).unwrap();
unsafe {
let val_ref = &*(*ptr as *const V);
f(val_ref)
}
}
pub fn with_mut<V: StateKey + 'a, R, F: FnOnce(&mut V) -> R + 'static>(&mut self, f: F) -> R {
let ptr = self.data.get_mut(&type_id::<V>()).unwrap();
unsafe {
let val_ref = &mut *(*ptr as *mut V);
f(val_ref)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MyState<'a>(&'a mut i32);
impl<'a> MyState<'a> {
fn inc(&mut self) {
*self.0 += 1;
}
fn get(&self) -> i32 {
*self.0
}
}
/// Safety of this impl should be ensured by a macro.
unsafe impl<'a> StateKey for MyState<'a> {
type Static = MyState<'static>;
}
#[test]
fn it_works() {
let mut counter = 33i32;
let new_value = {
let mut my_state = MyState(&mut counter);
let mut host_state = HostState::new();
host_state.insert::<MyState>(&mut my_state);
host_state.with_mut(|my_state: &mut MyState| {
my_state.inc();
my_state.get()
})
};
assert_eq!(new_value, counter);
}
struct MyImmutableState<'a>(&'a i32);
/// Safety of this impl should be ensured by a macro.
unsafe impl<'a> StateKey for MyImmutableState<'a> {
type Static = MyImmutableState<'static>;
}
struct StaticState(i32);
impl StaticState {
fn inc(&mut self) {
self.0 += 1;
}
fn get(&self) -> i32 {
self.0
}
}
/// Safety of this impl should be ensured by a macro.
unsafe impl<'a> StateKey for StaticState {
type Static = StaticState;
}
#[test]
fn compiles_with_static() {
let mut static_state = StaticState(45);
let mut host_state = HostState::new();
host_state.insert::<StaticState>(&mut static_state);
host_state.with_mut(|my_state: &mut StaticState| {
my_state.inc();
});
host_state.with_mut(|my_state: &mut StaticState| {
my_state.inc();
assert_eq!(47, my_state.get());
})
}
#[test]
#[should_panic]
fn doesnt_allow_dups() {
let mut static_state_1 = StaticState(45);
let mut static_state_2 = StaticState(45);
let mut host_state = HostState::new();
host_state.insert::<StaticState>(&mut static_state_1);
host_state.insert::<StaticState>(&mut static_state_2);
}
}