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; extern crate parity_wasm;
use std::env::args; use std::env::args;
use parity_wasm::interpreter::{ModuleInstance, HostState}; use parity_wasm::interpreter::{ModuleInstance, EmptyExternals};
fn main() { fn main() {
let args: Vec<_> = args().collect(); 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 // - "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 // 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) let main = ModuleInstance::new(&module)
.run_start(&mut HostState::default()) .run_start(&mut EmptyExternals)
.expect("Failed to initialize module"); .expect("Failed to initialize module");
// The argument should be parsable as a valid integer // The argument should be parsable as a valid integer
let argument: i32 = args[2].parse().expect("Integer argument required"); 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 // "_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::RuntimeValue;
use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType}; use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType};
use parity_wasm::interpreter::{ModuleInstance, HostState}; use parity_wasm::interpreter::{ModuleInstance, EmptyExternals};
fn main() { 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 // - "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 // 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) let main = ModuleInstance::new(&module)
.run_start(&mut HostState::default()) .run_start(&mut EmptyExternals)
.expect("Failed to initialize module"); .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::env;
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use parity_wasm::elements::Module; use parity_wasm::elements::{Module, FunctionType, ValueType, TableType, GlobalType, MemoryType};
use parity_wasm::interpreter::{ use parity_wasm::interpreter::{
Error as InterpreterError, HostModule, HostModuleBuilder, Error as InterpreterError, ModuleInstance, UserError,
ModuleInstance, UserError, HostState, StateKey HostFuncIndex, Externals, RuntimeValue, GlobalInstance, TableInstance, MemoryInstance,
TableRef, MemoryRef, GlobalRef, FuncRef, TryInto, ImportResolver, FuncInstance,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -139,54 +140,123 @@ struct Runtime<'a> {
game: &'a mut tictactoe::Game, game: &'a mut tictactoe::Game,
} }
unsafe impl<'a> StateKey for Runtime<'a> { const SET_FUNC_INDEX: HostFuncIndex = 0;
type Static = Runtime<'static>; 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( fn instantiate(
module: &Module, module: &Module,
env: &HostModule,
) -> Result<Rc<ModuleInstance>, Error> { ) -> Result<Rc<ModuleInstance>, Error> {
let instance = ModuleInstance::new(module) let instance = ModuleInstance::new(module)
.with_import("env", &*env) .with_import("env", &RuntimeImportResolver)
.run_start(&mut HostState::default())?; .assert_no_start()?;
Ok(instance) Ok(instance)
} }
fn env_host_module() -> HostModule { fn play(
HostModuleBuilder::new() x_instance: Rc<ModuleInstance>,
.with_func1( o_instance: Rc<ModuleInstance>,
"set", game: &mut tictactoe::Game,
|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,
) -> Result<tictactoe::GameResult, Error> { ) -> 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 mut turn_of = tictactoe::Player::X;
let game_result = loop { let game_result = loop {
let (instance, next_turn_of) = match turn_of { let (instance, next_turn_of) = match turn_of {
@ -199,11 +269,7 @@ fn play<'a>(
player: turn_of, player: turn_of,
game: game, game: game,
}; };
{ let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?;
let mut host_state = HostState::new();
host_state.insert::<Runtime>(&mut runtime);
let _ = instance.invoke_export("mk_turn", &[], &mut host_state)?;
}
} }
match game.game_result() { match game.game_result() {
@ -219,7 +285,6 @@ fn play<'a>(
fn main() { fn main() {
let mut game = tictactoe::Game::new(); let mut game = tictactoe::Game::new();
let env_host_module = env_host_module();
let args: Vec<_> = env::args().collect(); let args: Vec<_> = env::args().collect();
if args.len() < 3 { 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 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 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); println!("result = {:?}, game = {:#?}", result, game);
} }

View File

@ -4,10 +4,9 @@ use std::collections::HashMap;
use std::borrow::Cow; use std::borrow::Cow;
use elements::{FunctionType, Local, Opcodes}; use elements::{FunctionType, Local, Opcodes};
use interpreter::{Error, ModuleInstance}; use interpreter::{Error, ModuleInstance};
use interpreter::host::{Externals, HostFuncIndex};
use interpreter::runner::{prepare_function_args, FunctionContext, Interpreter}; use interpreter::runner::{prepare_function_args, FunctionContext, Interpreter};
use interpreter::host::HostFunc;
use interpreter::value::RuntimeValue; use interpreter::value::RuntimeValue;
use interpreter::state::HostState;
use common::stack::StackWithLimit; use common::stack::StackWithLimit;
use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT}; use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
@ -29,8 +28,8 @@ pub enum FuncInstance {
body: Rc<FuncBody>, body: Rc<FuncBody>,
}, },
Host { Host {
func_type: Rc<FunctionType>, func_type: FunctionType,
host_func: Rc<HostFunc>, host_func: HostFuncIndex,
}, },
} }
@ -57,7 +56,7 @@ impl fmt::Debug for FuncInstance {
} }
impl FuncInstance { impl FuncInstance {
pub fn alloc_internal( pub(crate) fn alloc_internal(
module: Rc<ModuleInstance>, module: Rc<ModuleInstance>,
func_type: Rc<FunctionType>, func_type: Rc<FunctionType>,
body: FuncBody, body: FuncBody,
@ -70,7 +69,7 @@ impl FuncInstance {
FuncRef(Rc::new(func)) 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 { let func = FuncInstance::Host {
func_type, func_type,
host_func, host_func,
@ -78,10 +77,10 @@ impl FuncInstance {
FuncRef(Rc::new(func)) FuncRef(Rc::new(func))
} }
pub fn func_type(&self) -> Rc<FunctionType> { pub fn func_type(&self) -> &FunctionType {
match *self { match *self {
FuncInstance::Internal { ref func_type, .. } | FuncInstance::Internal { ref func_type, .. } => func_type,
FuncInstance::Host { ref func_type, .. } => Rc::clone(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, func: FuncRef,
args: Cow<[RuntimeValue]>, args: Cow<[RuntimeValue]>,
state: &'a mut HostState<'b>, externals: &mut E,
) -> Result<Option<RuntimeValue>, Error> { ) -> Result<Option<RuntimeValue>, Error> {
enum InvokeKind<'a> { enum InvokeKind<'a> {
Internal(FunctionContext), Internal(FunctionContext),
Host(Rc<HostFunc>, &'a [RuntimeValue]), Host(HostFuncIndex, &'a [RuntimeValue]),
} }
let result = match *func { let result = match *func {
@ -117,16 +116,16 @@ impl FuncInstance {
InvokeKind::Internal(context) InvokeKind::Internal(context)
} }
FuncInstance::Host { ref host_func, .. } => { FuncInstance::Host { ref host_func, .. } => {
InvokeKind::Host(Rc::clone(host_func), &*args) InvokeKind::Host(*host_func, &*args)
} }
}; };
match result { match result {
InvokeKind::Internal(ctx) => { InvokeKind::Internal(ctx) => {
let mut interpreter = Interpreter::new(state); let mut interpreter = Interpreter::new(externals);
interpreter.run_function(ctx) 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 elements::FunctionType;
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 interpreter::global::GlobalInstance; use interpreter::global::GlobalInstance;
use interpreter::memory::MemoryInstance; use interpreter::memory::MemoryInstance;
use interpreter::table::TableInstance; use interpreter::table::TableInstance;
use interpreter::value::{RuntimeValue, TryInto}; use interpreter::value::RuntimeValue;
use interpreter::Error; use interpreter::Error;
use interpreter::ImportResolver;
use interpreter::state::HostState;
pub type HostFunc = Fn(&mut HostState, &[RuntimeValue]) pub type HostFuncIndex = u32;
-> Result<Option<RuntimeValue>, Error>;
pub struct HostModuleBuilder { pub trait Externals {
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 {
fn invoke_index( fn invoke_index(
&mut self, &mut self,
index: u32, index: HostFuncIndex,
args: &[RuntimeValue], args: &[RuntimeValue],
) -> Result<Option<RuntimeValue>, Error>; ) -> Result<Option<RuntimeValue>, Error>;
// TODO: or check signature? fn check_signature(&self, index: HostFuncIndex, signature: &FunctionType) -> bool;
fn signature(&self, index: usize) -> &FunctionType;
fn memory_by_index(&self, index: usize) -> &MemoryInstance; fn memory_by_index(&self, index: usize) -> &MemoryInstance;
fn table_by_index(&self, index: usize) -> &TableInstance; fn table_by_index(&self, index: usize) -> &TableInstance;
fn global_by_index(&self, index: usize) -> &GlobalInstance; 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 imports;
mod global; mod global;
mod func; mod func;
mod state;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -154,10 +153,9 @@ mod tests;
pub use self::memory::{MemoryInstance, MemoryRef}; pub use self::memory::{MemoryInstance, MemoryRef};
pub use self::table::{TableInstance, TableRef}; pub use self::table::{TableInstance, TableRef};
pub use self::program::ProgramInstance; pub use self::program::ProgramInstance;
pub use self::value::RuntimeValue; pub use self::value::{RuntimeValue, TryInto};
pub use self::host::{HostModule, HostModuleBuilder, HostFunc, IntoReturnVal, FromArg}; pub use self::host::{Externals, HostFuncIndex, EmptyExternals};
pub use self::imports::{ImportResolver, Imports}; pub use self::imports::{ImportResolver, Imports};
pub use self::module::ModuleInstance; pub use self::module::ModuleInstance;
pub use self::global::{GlobalInstance, GlobalRef}; pub use self::global::{GlobalInstance, GlobalRef};
pub use self::func::{FuncInstance, FuncRef}; 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::global::{GlobalInstance, GlobalRef};
use interpreter::func::{FuncRef, FuncBody, FuncInstance}; use interpreter::func::{FuncRef, FuncBody, FuncInstance};
use interpreter::table::TableRef; use interpreter::table::TableRef;
use interpreter::state::HostState;
use interpreter::memory::MemoryRef; use interpreter::memory::MemoryRef;
use interpreter::host::Externals;
use validation::validate_module; use validation::validate_module;
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
@ -187,7 +187,7 @@ impl ModuleInstance {
"Due to validation function type should exists", "Due to validation function type should exists",
); );
let actual_fn_type = func.func_type(); 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!( return Err(Error::Instantiation(format!(
"Expected function with type {:?}, but actual type is {:?} for entry {}", "Expected function with type {:?}, but actual type is {:?} for entry {}",
expected_fn_type, expected_fn_type,
@ -405,11 +405,11 @@ impl ModuleInstance {
InstantiationBuilder::new(module) InstantiationBuilder::new(module)
} }
pub fn invoke_index<'a>( pub fn invoke_index<E: Externals>(
&self, &self,
func_idx: u32, func_idx: u32,
args: &[RuntimeValue], args: &[RuntimeValue],
state: &'a mut HostState<'a>, state: &mut E,
) -> Result<Option<RuntimeValue>, Error> { ) -> Result<Option<RuntimeValue>, Error> {
let func_instance = self.func_by_index(func_idx).ok_or_else(|| { let func_instance = self.func_by_index(func_idx).ok_or_else(|| {
Error::Program(format!( Error::Program(format!(
@ -420,11 +420,11 @@ impl ModuleInstance {
FuncInstance::invoke(func_instance, Cow::Borrowed(args), state) FuncInstance::invoke(func_instance, Cow::Borrowed(args), state)
} }
pub fn invoke_export<'a>( pub fn invoke_export<E: Externals>(
&self, &self,
func_name: &str, func_name: &str,
args: &[RuntimeValue], args: &[RuntimeValue],
state: &'a mut HostState<'a>, state: &mut E,
) -> Result<Option<RuntimeValue>, Error> { ) -> Result<Option<RuntimeValue>, Error> {
let extern_val = self.export_by_name(func_name).ok_or_else(|| { let extern_val = self.export_by_name(func_name).ok_or_else(|| {
Error::Program(format!("Module doesn't have export {}", func_name)) Error::Program(format!("Module doesn't have export {}", func_name))
@ -474,7 +474,7 @@ impl<'a> InstantiationBuilder<'a> {
self 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 imports = self.imports.get_or_insert_with(|| Imports::default());
let instance = ModuleInstance::instantiate_with_imports(self.module, imports)?; let instance = ModuleInstance::instantiate_with_imports(self.module, imports)?;

View File

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

View File

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