Add examples

This commit is contained in:
Sergey Pepyakin 2018-01-22 18:39:54 +03:00
parent da2c02e2ab
commit 0893b39156
3 changed files with 384 additions and 0 deletions

44
examples/interpret.rs Normal file
View File

@ -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: {} <wasm file> <arg>", 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));
}

85
examples/invoke.rs Normal file
View File

@ -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: {} <wasm file> <exported func> [<arg>...]", 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::<i32>().expect(&format!("Can't parse arg #{} as i32", program_args[i]))),
&ValueType::I64 => RuntimeValue::I64(program_args[i].parse::<i64>().expect(&format!("Can't parse arg #{} as i64", program_args[i]))),
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i]))),
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i]))),
}).collect::<Vec<RuntimeValue>>()
};
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(""));
}

255
examples/tictactoe.rs Normal file
View File

@ -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<InterpreterError> 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<Player>) -> i32 {
match maybe_player {
None => 0,
Some(Player::X) => 1,
Some(Player::O) => 2,
}
}
}
#[derive(Debug)]
pub struct Game {
board: [Option<Player>; 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<Option<Player>, Error> {
if idx < 0 || idx > 9 {
return Err(Error::OutOfRange);
}
Ok(self.board[idx as usize])
}
pub fn game_result(&self) -> Option<GameResult> {
// 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<Player> {
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<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")
}
}
}
struct RuntimeModuleImportResolver;
impl<'a> ModuleImportResolver for RuntimeModuleImportResolver {
fn resolve_func(
&self,
field_name: &str,
_signature: &Signature,
) -> Result<FuncRef, InterpreterError> {
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<ModuleRef, Error> {
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<tictactoe::GameResult, Error> {
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: {} <x player module> <y player module>", 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);
}