Add examples
This commit is contained in:
parent
da2c02e2ab
commit
0893b39156
|
@ -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));
|
||||
}
|
|
@ -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(""));
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue