mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-14 23:41:43 +00:00
Update state module and update tictactoe
This commit is contained in:
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user