diff --git a/examples/interpret.rs b/examples/interpret.rs new file mode 100644 index 0000000..d87c442 --- /dev/null +++ b/examples/interpret.rs @@ -0,0 +1,44 @@ +// In this example we execute a contract funciton exported as "_call" + +extern crate wasmi; + +use std::env::args; +use std::fs::File; +use wasmi::{ModuleInstance, NopExternals, RuntimeValue, ImportsBuilder, load_from_buffer, LoadedModule}; + +fn load_from_file(filename: &str) -> LoadedModule { + use std::io::prelude::*; + let mut file = File::open(filename).unwrap(); + let mut buf = Vec::new(); + file.read_to_end(&mut buf).unwrap(); + load_from_buffer(buf).unwrap() +} + +fn main() { + let args: Vec<_> = args().collect(); + if args.len() != 3 { + println!("Usage: {} ", args[0]); + println!(" wasm file should contain exported `_call` function with single I32 argument"); + return; + } + + // Here we load module using dedicated for this purpose + // `load_from_file` function (which works only with modules) + let module = load_from_file(&args[1]); + + // Intialize deserialized module. It adds module into It expects 3 parameters: + // - a name for the module + // - a module declaration + // - "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, &ImportsBuilder::default()) + .expect("Failed to instantiate module") + .run_start(&mut NopExternals) + .expect("Failed to run start function in 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", &[RuntimeValue::I32(argument)], &mut NopExternals)); +} diff --git a/examples/invoke.rs b/examples/invoke.rs new file mode 100644 index 0000000..42b68c9 --- /dev/null +++ b/examples/invoke.rs @@ -0,0 +1,85 @@ +extern crate parity_wasm; +extern crate wasmi; + +use std::env::args; + +use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType}; +use wasmi::{RuntimeValue, ModuleInstance, NopExternals, ImportsBuilder, load_from_module}; + + +fn main() { + let args: Vec<_> = args().collect(); + if args.len() < 3 { + println!("Usage: {} [...]", args[0]); + return; + } + let func_name = &args[2]; + let (_, program_args) = args.split_at(3); + + let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized"); + + // Extracts call arguments from command-line arguments + let args = { + // Export section has an entry with a func_name with an index inside a module + let export_section = module.export_section().expect("No export section found"); + // It's a section with function declarations (which are references to the type section entries) + let function_section = module.function_section().expect("No function section found"); + // Type section stores function types which are referenced by function_section entries + let type_section = module.type_section().expect("No type section found"); + + // Given function name used to find export section entry which contains + // an `internal` field which points to the index in the function index space + let found_entry = export_section.entries().iter() + .find(|entry| func_name == entry.field()).expect(&format!("No export with name {} found", func_name)); + + // Function index in the function index space (internally-defined + imported) + let function_index: usize = match found_entry.internal() { + &Internal::Function(index) => index as usize, + _ => panic!("Founded export is not a function"), + }; + + // We need to count import section entries (functions only!) to subtract it from function_index + // and obtain the index within the function section + let import_section_len: usize = match module.import_section() { + Some(import) => + import.entries().iter().filter(|entry| match entry.external() { + &External::Function(_) => true, + _ => false, + }).count(), + None => 0, + }; + + // Calculates a function index within module's function section + let function_index_in_section = function_index - import_section_len; + + // Getting a type reference from a function section entry + let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize; + + // Use the reference to get an actual function type + let function_type: &FunctionType = match &type_section.types()[func_type_ref] { + &Type::Function(ref func_type) => func_type, + }; + + // Parses arguments and constructs runtime values in correspondence of their types + function_type.params().iter().enumerate().map(|(i, value)| match value { + &ValueType::I32 => RuntimeValue::I32(program_args[i].parse::().expect(&format!("Can't parse arg #{} as i32", program_args[i]))), + &ValueType::I64 => RuntimeValue::I64(program_args[i].parse::().expect(&format!("Can't parse arg #{} as i64", program_args[i]))), + &ValueType::F32 => RuntimeValue::F32(program_args[i].parse::().expect(&format!("Can't parse arg #{} as f32", program_args[i]))), + &ValueType::F64 => RuntimeValue::F64(program_args[i].parse::().expect(&format!("Can't parse arg #{} as f64", program_args[i]))), + }).collect::>() + }; + + let loaded_module = load_from_module(module).expect("Module to be valid"); + + // Intialize deserialized module. It adds module into It expects 3 parameters: + // - a name for the module + // - a module declaration + // - "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(&loaded_module, &ImportsBuilder::default()) + .expect("Failed to instantiate module") + .run_start(&mut NopExternals) + .expect("Failed to run start function in module"); + + println!("Result: {:?}", main.invoke_export(func_name, &args, &mut NopExternals).expect("")); +} diff --git a/examples/tictactoe.rs b/examples/tictactoe.rs new file mode 100644 index 0000000..76760fc --- /dev/null +++ b/examples/tictactoe.rs @@ -0,0 +1,255 @@ +extern crate wasmi; +extern crate parity_wasm; + +use std::env; +use std::fmt; +use std::fs::File; +use wasmi::{ + Error as InterpreterError, ModuleInstance, ModuleRef, + Externals, RuntimeValue, FuncRef, TryInto, ModuleImportResolver, + FuncInstance, HostError, ImportsBuilder, Signature, ValueType, +}; + +#[derive(Debug)] +pub enum Error { + OutOfRange, + AlreadyOccupied, + Interpreter(InterpreterError), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for Error { + fn from(e: InterpreterError) -> Self { + Error::Interpreter(e) + } +} + +impl HostError for Error {} + +mod tictactoe { + use super::Error; + + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub enum Player { + X, + O, + } + + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub enum GameResult { + Draw, + Won(Player), + } + + impl Player { + pub fn into_i32(maybe_player: Option) -> i32 { + match maybe_player { + None => 0, + Some(Player::X) => 1, + Some(Player::O) => 2, + } + } + } + + #[derive(Debug)] + pub struct Game { + board: [Option; 9], + } + + impl Game { + pub fn new() -> Game { + Game { + board: [None; 9], + } + } + + pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> { + if idx < 0 || idx > 9 { + return Err(Error::OutOfRange); + } + if self.board[idx as usize] != None { + return Err(Error::AlreadyOccupied); + } + self.board[idx as usize] = Some(player); + Ok(()) + } + + pub fn get(&self, idx: i32) -> Result, Error> { + if idx < 0 || idx > 9 { + return Err(Error::OutOfRange); + } + Ok(self.board[idx as usize]) + } + + pub fn game_result(&self) -> Option { + // 0, 1, 2 + // 3, 4, 5 + // 6, 7, 8 + let patterns = &[ + // Rows + (0, 1, 2), + (3, 4, 5), + (6, 7, 8), + + // Columns + (0, 3, 6), + (1, 4, 7), + (2, 5, 8), + + // Diagonals + (0, 4, 8), + (2, 4, 6), + ]; + + // Returns Some(player) if all cells contain same Player. + let all_same = |i1: usize, i2: usize, i3: usize| -> Option { + if self.board[i1].is_none() { + return None; + } + if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] { + return self.board[i1]; + } + None + }; + + for &(i1, i2, i3) in patterns { + if let Some(player) = all_same(i1, i2, i3) { + return Some(GameResult::Won(player)); + } + } + + // Ok, there is no winner. Check if it's draw. + let all_occupied = self.board.iter().all(|&cell| cell.is_some()); + if all_occupied { + Some(GameResult::Draw) + } else { + // Nah, there are still empty cells left. + None + } + } + } +} + +struct Runtime<'a> { + player: tictactoe::Player, + game: &'a mut tictactoe::Game, +} + +const SET_FUNC_INDEX: usize = 0; +const GET_FUNC_INDEX: usize = 1; + +impl<'a> Externals for Runtime<'a> { + fn invoke_index( + &mut self, + index: usize, + args: &[RuntimeValue], + ) -> Result, 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") + } + } +} + +struct RuntimeModuleImportResolver; + +impl<'a> ModuleImportResolver for RuntimeModuleImportResolver { + fn resolve_func( + &self, + field_name: &str, + _signature: &Signature, + ) -> Result { + let func_ref = match field_name { + "set" => { + FuncInstance::alloc_host(Signature::new(&[ValueType::I32], None), SET_FUNC_INDEX) + }, + "get" => FuncInstance::alloc_host(Signature::new(&[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 instantiate(path: &str) -> Result { + let module = { + use std::io::prelude::*; + let mut file = File::open(path).unwrap(); + let mut wasm_buf = Vec::new(); + file.read_to_end(&mut wasm_buf).unwrap(); + wasmi::load_from_buffer(&wasm_buf)? + }; + + let mut imports = ImportsBuilder::new(); + imports.push_resolver("env", &RuntimeModuleImportResolver); + + let instance = ModuleInstance::new(&module, &imports)? + .assert_no_start(); + + Ok(instance) +} + +fn play( + x_instance: ModuleRef, + o_instance: ModuleRef, + game: &mut tictactoe::Game, +) -> Result { + let mut turn_of = tictactoe::Player::X; + let game_result = loop { + let (instance, next_turn_of) = match turn_of { + tictactoe::Player::X => (&x_instance, tictactoe::Player::O), + tictactoe::Player::O => (&o_instance, tictactoe::Player::X), + }; + + { + let mut runtime = Runtime { + player: turn_of, + game: game, + }; + let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?; + } + + match game.game_result() { + Some(game_result) => break game_result, + None => {} + } + + turn_of = next_turn_of; + }; + + Ok(game_result) +} + +fn main() { + let mut game = tictactoe::Game::new(); + + let args: Vec<_> = env::args().collect(); + if args.len() < 3 { + println!("Usage: {} ", args[0]); + return; + } + + // Instantiate modules of X and O players. + let x_instance = instantiate(&args[1]).expect("X player module to load"); + let o_instance = instantiate(&args[2]).expect("Y player module to load"); + + let result = play(x_instance, o_instance, &mut game); + println!("result = {:?}, game = {:#?}", result, game); +}