mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-11 22:11:35 +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::interpreter::{
|
||||
Error as InterpreterError, HostModule, HostModuleBuilder,
|
||||
ModuleInstance, UserError, HostState, StateKey};
|
||||
ModuleInstance, UserError, HostState, StateKey
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@ -30,7 +31,6 @@ impl From<InterpreterError> for Error {
|
||||
impl UserError for Error {}
|
||||
|
||||
mod tictactoe {
|
||||
use std::cell::RefCell;
|
||||
use super::Error;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
@ -57,25 +57,24 @@ mod tictactoe {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Game {
|
||||
board: RefCell<[Option<Player>; 9]>,
|
||||
board: [Option<Player>; 9],
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new() -> Game {
|
||||
Game {
|
||||
board: RefCell::new([None; 9]),
|
||||
board: [None; 9],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&self, idx: i32, player: Player) -> Result<(), Error> {
|
||||
let mut board = self.board.borrow_mut();
|
||||
pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> {
|
||||
if idx < 0 || idx > 9 {
|
||||
return Err(Error::OutOfRange);
|
||||
}
|
||||
if board[idx as usize] != None {
|
||||
if self.board[idx as usize] != None {
|
||||
return Err(Error::AlreadyOccupied);
|
||||
}
|
||||
board[idx as usize] = Some(player);
|
||||
self.board[idx as usize] = Some(player);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -83,12 +82,10 @@ mod tictactoe {
|
||||
if idx < 0 || idx > 9 {
|
||||
return Err(Error::OutOfRange);
|
||||
}
|
||||
Ok(self.board.borrow()[idx as usize])
|
||||
Ok(self.board[idx as usize])
|
||||
}
|
||||
|
||||
pub fn game_result(&self) -> Option<GameResult> {
|
||||
let board = self.board.borrow();
|
||||
|
||||
// 0, 1, 2
|
||||
// 3, 4, 5
|
||||
// 6, 7, 8
|
||||
@ -110,11 +107,11 @@ mod tictactoe {
|
||||
|
||||
// Returns Some(player) if all cells contain same 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;
|
||||
}
|
||||
if board[i1] == board[i2] && board[i2] == board[i3] {
|
||||
return board[i1];
|
||||
if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] {
|
||||
return self.board[i1];
|
||||
}
|
||||
None
|
||||
};
|
||||
@ -126,7 +123,7 @@ mod tictactoe {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
Some(GameResult::Draw)
|
||||
} else {
|
||||
@ -139,14 +136,14 @@ mod tictactoe {
|
||||
|
||||
struct Runtime<'a> {
|
||||
player: tictactoe::Player,
|
||||
game: &'a tictactoe::Game,
|
||||
game: &'a mut tictactoe::Game,
|
||||
}
|
||||
|
||||
unsafe impl<'a> StateKey for Runtime<'a> {
|
||||
type Static = Runtime<'static>;
|
||||
}
|
||||
|
||||
fn instantiate<'a, 'b>(
|
||||
fn instantiate(
|
||||
module: &Module,
|
||||
env: &HostModule,
|
||||
) -> Result<Rc<ModuleInstance>, Error> {
|
||||
@ -157,12 +154,12 @@ fn instantiate<'a, 'b>(
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
fn env_host_module<'a>() -> HostModule {
|
||||
fn env_host_module() -> HostModule {
|
||||
HostModuleBuilder::new()
|
||||
.with_func1(
|
||||
"set",
|
||||
|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)?;
|
||||
Ok(())
|
||||
})
|
||||
@ -171,7 +168,7 @@ fn env_host_module<'a>() -> HostModule {
|
||||
.with_func1(
|
||||
"get",
|
||||
|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)?);
|
||||
Ok(val)
|
||||
})
|
||||
@ -184,7 +181,7 @@ fn play<'a>(
|
||||
x_module: &Module,
|
||||
o_module: &Module,
|
||||
host_module: &HostModule,
|
||||
game: &'a tictactoe::Game,
|
||||
game: &'a mut tictactoe::Game,
|
||||
) -> Result<tictactoe::GameResult, Error> {
|
||||
// Instantiate modules of X and O players.
|
||||
let x_instance = instantiate(x_module, host_module)?;
|
||||
@ -197,13 +194,17 @@ fn play<'a>(
|
||||
tictactoe::Player::O => (&o_instance, tictactoe::Player::X),
|
||||
};
|
||||
|
||||
let mut runtime = Runtime {
|
||||
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 runtime = Runtime {
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
|
||||
match game.game_result() {
|
||||
Some(game_result) => break game_result,
|
||||
@ -217,7 +218,7 @@ fn play<'a>(
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let game = tictactoe::Game::new();
|
||||
let mut game = tictactoe::Game::new();
|
||||
let env_host_module = env_host_module();
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
@ -1,22 +1,23 @@
|
||||
|
||||
use std::any::{TypeId, Any};
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub unsafe trait StateKey {
|
||||
type Static: ?Sized + 'static;
|
||||
type Static: ?Sized + 'static;
|
||||
}
|
||||
|
||||
/// Returns the `TypeId` for `T::Static`
|
||||
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)]
|
||||
pub struct HostState<'a> {
|
||||
data: HashMap<TypeId, *mut ()>,
|
||||
_marker: ::std::marker::PhantomData<&'a ()>,
|
||||
_marker: ::std::marker::PhantomData<&'a mut ()>,
|
||||
}
|
||||
|
||||
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 ptr = val as *mut V as *mut ();
|
||||
let existing = self.data.insert(ty_id, ptr);
|
||||
assert!(existing.is_none());
|
||||
}
|
||||
|
||||
pub fn with_state<V: StateKey, R, F: FnOnce(&mut V) -> R>(&mut self, f: F) -> R {
|
||||
let ty_id = type_id::<V>();
|
||||
let ptr = self.data.get_mut(&ty_id).unwrap();
|
||||
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 = &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)
|
||||
}
|
||||
}
|
||||
@ -59,6 +67,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Safety of this impl should be ensured by a macro.
|
||||
unsafe impl<'a> StateKey for MyState<'a> {
|
||||
type Static = MyState<'static>;
|
||||
}
|
||||
@ -71,7 +80,7 @@ mod tests {
|
||||
let mut my_state = MyState(&mut counter);
|
||||
let mut host_state = HostState::new();
|
||||
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.get()
|
||||
})
|
||||
@ -79,4 +88,51 @@ mod tests {
|
||||
|
||||
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