Update state module and update tictactoe

This commit is contained in:
Sergey Pepyakin
2017-12-20 13:06:38 +03:00
parent a2a3290506
commit e0ddc56fec
2 changed files with 99 additions and 42 deletions

View File

@ -6,7 +6,8 @@ use std::rc::Rc;
use parity_wasm::elements::Module; use parity_wasm::elements::Module;
use parity_wasm::interpreter::{ use parity_wasm::interpreter::{
Error as InterpreterError, HostModule, HostModuleBuilder, Error as InterpreterError, HostModule, HostModuleBuilder,
ModuleInstance, UserError, HostState, StateKey}; ModuleInstance, UserError, HostState, StateKey
};
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -30,7 +31,6 @@ impl From<InterpreterError> for Error {
impl UserError for Error {} impl UserError for Error {}
mod tictactoe { mod tictactoe {
use std::cell::RefCell;
use super::Error; use super::Error;
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -57,25 +57,24 @@ mod tictactoe {
#[derive(Debug)] #[derive(Debug)]
pub struct Game { pub struct Game {
board: RefCell<[Option<Player>; 9]>, board: [Option<Player>; 9],
} }
impl Game { impl Game {
pub fn new() -> Game { pub fn new() -> Game {
Game { Game {
board: RefCell::new([None; 9]), board: [None; 9],
} }
} }
pub fn set(&self, idx: i32, player: Player) -> Result<(), Error> { pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> {
let mut board = self.board.borrow_mut();
if idx < 0 || idx > 9 { if idx < 0 || idx > 9 {
return Err(Error::OutOfRange); return Err(Error::OutOfRange);
} }
if board[idx as usize] != None { if self.board[idx as usize] != None {
return Err(Error::AlreadyOccupied); return Err(Error::AlreadyOccupied);
} }
board[idx as usize] = Some(player); self.board[idx as usize] = Some(player);
Ok(()) Ok(())
} }
@ -83,12 +82,10 @@ mod tictactoe {
if idx < 0 || idx > 9 { if idx < 0 || idx > 9 {
return Err(Error::OutOfRange); return Err(Error::OutOfRange);
} }
Ok(self.board.borrow()[idx as usize]) Ok(self.board[idx as usize])
} }
pub fn game_result(&self) -> Option<GameResult> { pub fn game_result(&self) -> Option<GameResult> {
let board = self.board.borrow();
// 0, 1, 2 // 0, 1, 2
// 3, 4, 5 // 3, 4, 5
// 6, 7, 8 // 6, 7, 8
@ -110,11 +107,11 @@ mod tictactoe {
// Returns Some(player) if all cells contain same Player. // Returns Some(player) if all cells contain same Player.
let all_same = |i1: usize, i2: usize, i3: usize| -> Option<Player> { let all_same = |i1: usize, i2: usize, i3: usize| -> Option<Player> {
if board[i1].is_none() { if self.board[i1].is_none() {
return None; return None;
} }
if board[i1] == board[i2] && board[i2] == board[i3] { if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] {
return board[i1]; return self.board[i1];
} }
None None
}; };
@ -126,7 +123,7 @@ mod tictactoe {
} }
// Ok, there is no winner. Check if it's draw. // Ok, there is no winner. Check if it's draw.
let all_occupied = board.iter().all(|&cell| cell.is_some()); let all_occupied = self.board.iter().all(|&cell| cell.is_some());
if all_occupied { if all_occupied {
Some(GameResult::Draw) Some(GameResult::Draw)
} else { } else {
@ -139,14 +136,14 @@ mod tictactoe {
struct Runtime<'a> { struct Runtime<'a> {
player: tictactoe::Player, player: tictactoe::Player,
game: &'a tictactoe::Game, game: &'a mut tictactoe::Game,
} }
unsafe impl<'a> StateKey for Runtime<'a> { unsafe impl<'a> StateKey for Runtime<'a> {
type Static = Runtime<'static>; type Static = Runtime<'static>;
} }
fn instantiate<'a, 'b>( fn instantiate(
module: &Module, module: &Module,
env: &HostModule, env: &HostModule,
) -> Result<Rc<ModuleInstance>, Error> { ) -> Result<Rc<ModuleInstance>, Error> {
@ -157,12 +154,12 @@ fn instantiate<'a, 'b>(
Ok(instance) Ok(instance)
} }
fn env_host_module<'a>() -> HostModule { fn env_host_module() -> HostModule {
HostModuleBuilder::new() HostModuleBuilder::new()
.with_func1( .with_func1(
"set", "set",
|state: &mut HostState, idx: i32| -> Result<(), InterpreterError> { |state: &mut HostState, idx: i32| -> Result<(), InterpreterError> {
state.with_state(|runtime: &mut Runtime| -> Result<(), InterpreterError> { state.with_mut(move |runtime: &mut Runtime| -> Result<(), InterpreterError> {
runtime.game.set(idx, runtime.player)?; runtime.game.set(idx, runtime.player)?;
Ok(()) Ok(())
}) })
@ -171,7 +168,7 @@ fn env_host_module<'a>() -> HostModule {
.with_func1( .with_func1(
"get", "get",
|state: &mut HostState, idx: i32| -> Result<i32, InterpreterError> { |state: &mut HostState, idx: i32| -> Result<i32, InterpreterError> {
state.with_state(|runtime: &mut Runtime| -> Result<i32, InterpreterError> { state.with(move |runtime: &Runtime| -> Result<i32, InterpreterError> {
let val: i32 = tictactoe::Player::into_i32(runtime.game.get(idx)?); let val: i32 = tictactoe::Player::into_i32(runtime.game.get(idx)?);
Ok(val) Ok(val)
}) })
@ -184,7 +181,7 @@ fn play<'a>(
x_module: &Module, x_module: &Module,
o_module: &Module, o_module: &Module,
host_module: &HostModule, host_module: &HostModule,
game: &'a tictactoe::Game, game: &'a mut tictactoe::Game,
) -> Result<tictactoe::GameResult, Error> { ) -> Result<tictactoe::GameResult, Error> {
// Instantiate modules of X and O players. // Instantiate modules of X and O players.
let x_instance = instantiate(x_module, host_module)?; let x_instance = instantiate(x_module, host_module)?;
@ -197,13 +194,17 @@ fn play<'a>(
tictactoe::Player::O => (&o_instance, tictactoe::Player::X), tictactoe::Player::O => (&o_instance, tictactoe::Player::X),
}; };
let mut runtime = Runtime { {
player: turn_of, let mut runtime = Runtime {
game: game, 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 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() {
Some(game_result) => break game_result, Some(game_result) => break game_result,
@ -217,7 +218,7 @@ fn play<'a>(
} }
fn main() { fn main() {
let game = tictactoe::Game::new(); let mut game = tictactoe::Game::new();
let env_host_module = env_host_module(); let env_host_module = env_host_module();
let args: Vec<_> = env::args().collect(); let args: Vec<_> = env::args().collect();
@ -228,6 +229,6 @@ 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, &game); let result = play(&x_module, &o_module, &env_host_module, &mut game);
println!("result = {:?}, game = {:#?}", result, game); println!("result = {:?}, game = {:#?}", result, game);
} }

View File

@ -1,22 +1,23 @@
use std::any::{TypeId, Any}; use std::any::{Any, TypeId};
use std::collections::HashMap; use std::collections::HashMap;
pub unsafe trait StateKey { pub unsafe trait StateKey {
type Static: ?Sized + 'static; type Static: ?Sized + 'static;
} }
/// Returns the `TypeId` for `T::Static`
pub fn type_id<T>() -> TypeId pub fn type_id<T>() -> TypeId
where T: StateKey, T::Static: Any where
T: StateKey,
T::Static: Any,
{ {
TypeId::of::<T::Static>() TypeId::of::<T::Static>()
} }
#[derive(Default)] #[derive(Default)]
pub struct HostState<'a> { pub struct HostState<'a> {
data: HashMap<TypeId, *mut ()>, data: HashMap<TypeId, *mut ()>,
_marker: ::std::marker::PhantomData<&'a ()>, _marker: ::std::marker::PhantomData<&'a mut ()>,
} }
impl<'a> HostState<'a> { impl<'a> HostState<'a> {
@ -27,18 +28,25 @@ impl<'a> HostState<'a> {
} }
} }
pub fn insert<V: StateKey>(&mut self, val: &'a mut V) { pub fn insert<V: StateKey + 'a>(&mut self, val: &'a mut V) {
let ty_id = type_id::<V>(); let ty_id = type_id::<V>();
let ptr = val as *mut V as *mut (); let ptr = val as *mut V as *mut ();
let existing = self.data.insert(ty_id, ptr); let existing = self.data.insert(ty_id, ptr);
assert!(existing.is_none()); assert!(existing.is_none());
} }
pub fn with_state<V: StateKey, R, F: FnOnce(&mut V) -> R>(&mut self, f: F) -> R { pub fn with<V: StateKey + 'a, R, F: FnOnce(&V) -> R>(&self, f: F) -> R {
let ty_id = type_id::<V>(); let ptr = self.data.get(&type_id::<V>()).unwrap();
let ptr = self.data.get_mut(&ty_id).unwrap();
unsafe { unsafe {
let val_ref = &mut * { *ptr as *mut V }; 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) f(val_ref)
} }
} }
@ -59,6 +67,7 @@ mod tests {
} }
} }
/// Safety of this impl should be ensured by a macro.
unsafe impl<'a> StateKey for MyState<'a> { unsafe impl<'a> StateKey for MyState<'a> {
type Static = MyState<'static>; type Static = MyState<'static>;
} }
@ -71,7 +80,7 @@ mod tests {
let mut my_state = MyState(&mut counter); let mut my_state = MyState(&mut counter);
let mut host_state = HostState::new(); let mut host_state = HostState::new();
host_state.insert::<MyState>(&mut my_state); host_state.insert::<MyState>(&mut my_state);
host_state.with_state(|my_state: &mut MyState| { host_state.with_mut(|my_state: &mut MyState| {
my_state.inc(); my_state.inc();
my_state.get() my_state.get()
}) })
@ -79,4 +88,51 @@ mod tests {
assert_eq!(new_value, counter); 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);
}
} }