This commit is contained in:
Jef 2018-12-11 12:54:06 +01:00 committed by Sergei Pepyakin
parent da558c7ce7
commit 899cc32e45
31 changed files with 9157 additions and 8968 deletions

View File

@ -1,32 +1,31 @@
use std::env; use std::env;
use std::process; use std::process;
fn main() { fn main() {
println!("cargo:rerun-if-changed=./wasm-kernel/"); println!("cargo:rerun-if-changed=./wasm-kernel/");
// The CARGO environment variable provides a path to the executable that // The CARGO environment variable provides a path to the executable that
// runs this build process. // runs this build process.
let cargo_bin = env::var("CARGO").expect("CARGO env variable should be defined"); let cargo_bin = env::var("CARGO").expect("CARGO env variable should be defined");
// Build a release version of wasm-kernel. The code in the output wasm binary // Build a release version of wasm-kernel. The code in the output wasm binary
// will be used in benchmarks. // will be used in benchmarks.
let output = process::Command::new(cargo_bin) let output = process::Command::new(cargo_bin)
.arg("build") .arg("build")
.arg("--target=wasm32-unknown-unknown") .arg("--target=wasm32-unknown-unknown")
.arg("--release") .arg("--release")
.arg("--manifest-path=./wasm-kernel/Cargo.toml") .arg("--manifest-path=./wasm-kernel/Cargo.toml")
.arg("--verbose") .arg("--verbose")
.output() .output()
.expect("failed to execute `cargo`"); .expect("failed to execute `cargo`");
if !output.status.success() { if !output.status.success() {
let msg = format!( let msg = format!(
"status: {status}\nstdout: {stdout}\nstderr: {stderr}\n", "status: {status}\nstdout: {stdout}\nstderr: {stderr}\n",
status=output.status, status = output.status,
stdout=String::from_utf8_lossy(&output.stdout), stdout = String::from_utf8_lossy(&output.stdout),
stderr=String::from_utf8_lossy(&output.stderr), stderr = String::from_utf8_lossy(&output.stderr),
); );
panic!("{}", msg); panic!("{}", msg);
} }
} }

View File

@ -4,14 +4,14 @@ extern crate wasmi;
use std::env::args; use std::env::args;
use std::fs::File; use std::fs::File;
use wasmi::{ModuleInstance, NopExternals, RuntimeValue, ImportsBuilder, Module}; use wasmi::{ImportsBuilder, Module, ModuleInstance, NopExternals, RuntimeValue};
fn load_from_file(filename: &str) -> Module { fn load_from_file(filename: &str) -> Module {
use std::io::prelude::*; use std::io::prelude::*;
let mut file = File::open(filename).unwrap(); let mut file = File::open(filename).unwrap();
let mut buf = Vec::new(); let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap(); file.read_to_end(&mut buf).unwrap();
Module::from_buffer(buf).unwrap() Module::from_buffer(buf).unwrap()
} }
fn main() { fn main() {
@ -27,10 +27,10 @@ fn main() {
let module = load_from_file(&args[1]); let module = load_from_file(&args[1]);
// Intialize deserialized module. It adds module into It expects 3 parameters: // Intialize deserialized module. It adds module into It expects 3 parameters:
// - a name for the module // - a name for the module
// - a module declaration // - a module declaration
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here // - "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 // 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()) let main = ModuleInstance::new(&module, &ImportsBuilder::default())
.expect("Failed to instantiate module") .expect("Failed to instantiate module")
.run_start(&mut NopExternals) .run_start(&mut NopExternals)
@ -40,5 +40,8 @@ fn main() {
let argument: i32 = args[2].parse().expect("Integer argument required"); 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 // "_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)); println!(
"Result: {:?}",
main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals)
);
} }

View File

@ -3,9 +3,8 @@ extern crate wasmi;
use std::env::args; use std::env::args;
use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType}; use parity_wasm::elements::{External, FunctionType, Internal, Type, ValueType};
use wasmi::{RuntimeValue, ModuleInstance, NopExternals, ImportsBuilder}; use wasmi::{ImportsBuilder, ModuleInstance, NopExternals, RuntimeValue};
fn main() { fn main() {
let args: Vec<_> = args().collect(); let args: Vec<_> = args().collect();
@ -23,14 +22,19 @@ fn main() {
// Export section has an entry with a func_name with an index inside a module // 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"); 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) // 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"); let function_section = module
.function_section()
.expect("No function section found");
// Type section stores function types which are referenced by function_section entries // Type section stores function types which are referenced by function_section entries
let type_section = module.type_section().expect("No type section found"); let type_section = module.type_section().expect("No type section found");
// Given function name used to find export section entry which contains // Given function name used to find export section entry which contains
// an `internal` field which points to the index in the function index space // an `internal` field which points to the index in the function index space
let found_entry = export_section.entries().iter() let found_entry = export_section
.find(|entry| func_name == entry.field()).expect(&format!("No export with name {} found", func_name)); .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) // Function index in the function index space (internally-defined + imported)
let function_index: usize = match found_entry.internal() { let function_index: usize = match found_entry.internal() {
@ -41,11 +45,14 @@ fn main() {
// We need to count import section entries (functions only!) to subtract it from function_index // We need to count import section entries (functions only!) to subtract it from function_index
// and obtain the index within the function section // and obtain the index within the function section
let import_section_len: usize = match module.import_section() { let import_section_len: usize = match module.import_section() {
Some(import) => Some(import) => import
import.entries().iter().filter(|entry| match entry.external() { .entries()
.iter()
.filter(|entry| match entry.external() {
&External::Function(_) => true, &External::Function(_) => true,
_ => false, _ => false,
}).count(), })
.count(),
None => 0, None => 0,
}; };
@ -53,7 +60,8 @@ fn main() {
let function_index_in_section = function_index - import_section_len; let function_index_in_section = function_index - import_section_len;
// Getting a type reference from a function section entry // 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; 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 // Use the reference to get an actual function type
let function_type: &FunctionType = match &type_section.types()[func_type_ref] { let function_type: &FunctionType = match &type_section.types()[func_type_ref] {
@ -61,12 +69,35 @@ fn main() {
}; };
// Parses arguments and constructs runtime values in correspondence of their types // Parses arguments and constructs runtime values in correspondence of their types
function_type.params().iter().enumerate().map(|(i, value)| match value { function_type
&ValueType::I32 => RuntimeValue::I32(program_args[i].parse::<i32>().expect(&format!("Can't parse arg #{} as i32", program_args[i]))), .params()
&ValueType::I64 => RuntimeValue::I64(program_args[i].parse::<i64>().expect(&format!("Can't parse arg #{} as i64", program_args[i]))), .iter()
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i])).into()), .enumerate()
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i])).into()), .map(|(i, value)| match value {
}).collect::<Vec<RuntimeValue>>() &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]))
.into(),
),
&ValueType::F64 => RuntimeValue::F64(
program_args[i]
.parse::<f64>()
.expect(&format!("Can't parse arg #{} as f64", program_args[i]))
.into(),
),
})
.collect::<Vec<RuntimeValue>>()
}; };
let loaded_module = wasmi::Module::from_parity_wasm_module(module).expect("Module to be valid"); let loaded_module = wasmi::Module::from_parity_wasm_module(module).expect("Module to be valid");
@ -81,5 +112,9 @@ fn main() {
.run_start(&mut NopExternals) .run_start(&mut NopExternals)
.expect("Failed to run start function in module"); .expect("Failed to run start function in module");
println!("Result: {:?}", main.invoke_export(func_name, &args, &mut NopExternals).expect("")); println!(
"Result: {:?}",
main.invoke_export(func_name, &args, &mut NopExternals)
.expect("")
);
} }

View File

@ -1,256 +1,255 @@
extern crate wasmi;
extern crate parity_wasm; extern crate parity_wasm;
extern crate wasmi;
use std::env; use std::env;
use std::fmt; use std::fmt;
use std::fs::File; use std::fs::File;
use wasmi::{ use wasmi::{
Error as InterpreterError, ModuleInstance, ModuleRef, Error as InterpreterError, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder,
Externals, RuntimeValue, FuncRef, ModuleImportResolver, ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature, Trap,
FuncInstance, HostError, ImportsBuilder, Signature, ValueType, ValueType,
RuntimeArgs, Trap,
}; };
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
OutOfRange, OutOfRange,
AlreadyOccupied, AlreadyOccupied,
Interpreter(InterpreterError), Interpreter(InterpreterError),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self) write!(f, "{:?}", self)
} }
} }
impl From<InterpreterError> for Error { impl From<InterpreterError> for Error {
fn from(e: InterpreterError) -> Self { fn from(e: InterpreterError) -> Self {
Error::Interpreter(e) Error::Interpreter(e)
} }
} }
impl HostError for Error {} impl HostError for Error {}
mod tictactoe { mod tictactoe {
use super::Error; use super::Error;
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Player { pub enum Player {
X, X,
O, O,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum GameResult { pub enum GameResult {
Draw, Draw,
Won(Player), Won(Player),
} }
impl Player { impl Player {
pub fn into_i32(maybe_player: Option<Player>) -> i32 { pub fn into_i32(maybe_player: Option<Player>) -> i32 {
match maybe_player { match maybe_player {
None => 0, None => 0,
Some(Player::X) => 1, Some(Player::X) => 1,
Some(Player::O) => 2, Some(Player::O) => 2,
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Game { pub struct Game {
board: [Option<Player>; 9], board: [Option<Player>; 9],
} }
impl Game { impl Game {
pub fn new() -> Game { pub fn new() -> Game {
Game { Game { board: [None; 9] }
board: [None; 9], }
}
}
pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> { pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> {
if idx < 0 || idx > 9 { if idx < 0 || idx > 9 {
return Err(Error::OutOfRange); return Err(Error::OutOfRange);
} }
if self.board[idx as usize] != None { if self.board[idx as usize] != None {
return Err(Error::AlreadyOccupied); return Err(Error::AlreadyOccupied);
} }
self.board[idx as usize] = Some(player); self.board[idx as usize] = Some(player);
Ok(()) Ok(())
} }
pub fn get(&self, idx: i32) -> Result<Option<Player>, Error> { pub fn get(&self, idx: i32) -> Result<Option<Player>, Error> {
if idx < 0 || idx > 9 { if idx < 0 || idx > 9 {
return Err(Error::OutOfRange); return Err(Error::OutOfRange);
} }
Ok(self.board[idx as usize]) Ok(self.board[idx as usize])
} }
pub fn game_result(&self) -> Option<GameResult> { pub fn game_result(&self) -> Option<GameResult> {
// 0, 1, 2 // 0, 1, 2
// 3, 4, 5 // 3, 4, 5
// 6, 7, 8 // 6, 7, 8
let patterns = &[ let patterns = &[
// Rows // Rows
(0, 1, 2), (0, 1, 2),
(3, 4, 5), (3, 4, 5),
(6, 7, 8), (6, 7, 8),
// Columns
(0, 3, 6),
(1, 4, 7),
(2, 5, 8),
// Diagonals
(0, 4, 8),
(2, 4, 6),
];
// Columns // Returns Some(player) if all cells contain same Player.
(0, 3, 6), let all_same = |i1: usize, i2: usize, i3: usize| -> Option<Player> {
(1, 4, 7), if self.board[i1].is_none() {
(2, 5, 8), return None;
}
if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] {
return self.board[i1];
}
None
};
// Diagonals for &(i1, i2, i3) in patterns {
(0, 4, 8), if let Some(player) = all_same(i1, i2, i3) {
(2, 4, 6), return Some(GameResult::Won(player));
]; }
}
// Returns Some(player) if all cells contain same Player. // Ok, there is no winner. Check if it's draw.
let all_same = |i1: usize, i2: usize, i3: usize| -> Option<Player> { let all_occupied = self.board.iter().all(|&cell| cell.is_some());
if self.board[i1].is_none() { if all_occupied {
return None; Some(GameResult::Draw)
} } else {
if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] { // Nah, there are still empty cells left.
return self.board[i1]; None
} }
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> { struct Runtime<'a> {
player: tictactoe::Player, player: tictactoe::Player,
game: &'a mut tictactoe::Game, game: &'a mut tictactoe::Game,
} }
const SET_FUNC_INDEX: usize = 0; const SET_FUNC_INDEX: usize = 0;
const GET_FUNC_INDEX: usize = 1; const GET_FUNC_INDEX: usize = 1;
impl<'a> Externals for Runtime<'a> { impl<'a> Externals for Runtime<'a> {
fn invoke_index( fn invoke_index(
&mut self, &mut self,
index: usize, index: usize,
args: RuntimeArgs, args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Trap> { ) -> Result<Option<RuntimeValue>, Trap> {
match index { match index {
SET_FUNC_INDEX => { SET_FUNC_INDEX => {
let idx: i32 = args.nth(0); let idx: i32 = args.nth(0);
self.game.set(idx, self.player)?; self.game.set(idx, self.player)?;
Ok(None) Ok(None)
} }
GET_FUNC_INDEX => { GET_FUNC_INDEX => {
let idx: i32 = args.nth(0); let idx: i32 = args.nth(0);
let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?); let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?);
Ok(Some(val.into())) Ok(Some(val.into()))
} }
_ => panic!("unknown function index") _ => panic!("unknown function index"),
} }
} }
} }
struct RuntimeModuleImportResolver; struct RuntimeModuleImportResolver;
impl<'a> ModuleImportResolver for RuntimeModuleImportResolver { impl<'a> ModuleImportResolver for RuntimeModuleImportResolver {
fn resolve_func( fn resolve_func(
&self, &self,
field_name: &str, field_name: &str,
_signature: &Signature, _signature: &Signature,
) -> Result<FuncRef, InterpreterError> { ) -> Result<FuncRef, InterpreterError> {
let func_ref = match field_name { let func_ref = match field_name {
"set" => { "set" => FuncInstance::alloc_host(
FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], None), SET_FUNC_INDEX) Signature::new(&[ValueType::I32][..], None),
}, SET_FUNC_INDEX,
"get" => FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], Some(ValueType::I32)), GET_FUNC_INDEX), ),
_ => return Err( "get" => FuncInstance::alloc_host(
InterpreterError::Function( Signature::new(&[ValueType::I32][..], Some(ValueType::I32)),
format!("host module doesn't export function with name {}", field_name) GET_FUNC_INDEX,
) ),
) _ => {
}; return Err(InterpreterError::Function(format!(
Ok(func_ref) "host module doesn't export function with name {}",
} field_name
)));
}
};
Ok(func_ref)
}
} }
fn instantiate(path: &str) -> Result<ModuleRef, Error> { fn instantiate(path: &str) -> Result<ModuleRef, Error> {
let module = { let module = {
use std::io::prelude::*; use std::io::prelude::*;
let mut file = File::open(path).unwrap(); let mut file = File::open(path).unwrap();
let mut wasm_buf = Vec::new(); let mut wasm_buf = Vec::new();
file.read_to_end(&mut wasm_buf).unwrap(); file.read_to_end(&mut wasm_buf).unwrap();
wasmi::Module::from_buffer(&wasm_buf)? wasmi::Module::from_buffer(&wasm_buf)?
}; };
let mut imports = ImportsBuilder::new(); let mut imports = ImportsBuilder::new();
imports.push_resolver("env", &RuntimeModuleImportResolver); imports.push_resolver("env", &RuntimeModuleImportResolver);
let instance = ModuleInstance::new(&module, &imports)? let instance = ModuleInstance::new(&module, &imports)?.assert_no_start();
.assert_no_start();
Ok(instance) Ok(instance)
} }
fn play( fn play(
x_instance: ModuleRef, x_instance: ModuleRef,
o_instance: ModuleRef, o_instance: ModuleRef,
game: &mut tictactoe::Game, game: &mut tictactoe::Game,
) -> Result<tictactoe::GameResult, Error> { ) -> Result<tictactoe::GameResult, Error> {
let mut turn_of = tictactoe::Player::X; let mut turn_of = tictactoe::Player::X;
let game_result = loop { let game_result = loop {
let (instance, next_turn_of) = match turn_of { let (instance, next_turn_of) = match turn_of {
tictactoe::Player::X => (&x_instance, tictactoe::Player::O), tictactoe::Player::X => (&x_instance, tictactoe::Player::O),
tictactoe::Player::O => (&o_instance, tictactoe::Player::X), tictactoe::Player::O => (&o_instance, tictactoe::Player::X),
}; };
{ {
let mut runtime = Runtime { let mut runtime = Runtime {
player: turn_of, player: turn_of,
game: game, game: game,
}; };
let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?; let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?;
} }
match game.game_result() { match game.game_result() {
Some(game_result) => break game_result, Some(game_result) => break game_result,
None => {} None => {}
} }
turn_of = next_turn_of; turn_of = next_turn_of;
}; };
Ok(game_result) Ok(game_result)
} }
fn main() { fn main() {
let mut game = tictactoe::Game::new(); let mut game = tictactoe::Game::new();
let args: Vec<_> = env::args().collect(); let args: Vec<_> = env::args().collect();
if args.len() < 3 { if args.len() < 3 {
println!("Usage: {} <x player module> <y player module>", args[0]); println!("Usage: {} <x player module> <y player module>", args[0]);
return; return;
} }
// Instantiate modules of X and O players. // Instantiate modules of X and O players.
let x_instance = instantiate(&args[1]).expect("X player module to load"); 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 o_instance = instantiate(&args[2]).expect("Y player module to load");
let result = play(x_instance, o_instance, &mut game); let result = play(x_instance, o_instance, &mut game);
println!("result = {:?}, game = {:#?}", result, game); println!("result = {:?}, game = {:#?}", result, game);
} }

View File

@ -5,77 +5,79 @@ extern crate wasmi;
use std::env::args; use std::env::args;
use std::fs::File; use std::fs::File;
use wasmi::{
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef,
ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue, Signature,
TableDescriptor, TableInstance, TableRef};
use wasmi::memory_units::*; use wasmi::memory_units::*;
use wasmi::{
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder,
MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance,
NopExternals, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
};
fn load_from_file(filename: &str) -> Module { fn load_from_file(filename: &str) -> Module {
use std::io::prelude::*; use std::io::prelude::*;
let mut file = File::open(filename).unwrap(); let mut file = File::open(filename).unwrap();
let mut buf = Vec::new(); let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap(); file.read_to_end(&mut buf).unwrap();
Module::from_buffer(buf).unwrap() Module::from_buffer(buf).unwrap()
} }
struct ResolveAll; struct ResolveAll;
impl ModuleImportResolver for ResolveAll { impl ModuleImportResolver for ResolveAll {
fn resolve_func(&self, _field_name: &str, signature: &Signature) -> Result<FuncRef, Error> { fn resolve_func(&self, _field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
Ok(FuncInstance::alloc_host(signature.clone(), 0)) Ok(FuncInstance::alloc_host(signature.clone(), 0))
} }
fn resolve_global( fn resolve_global(
&self, &self,
_field_name: &str, _field_name: &str,
global_type: &GlobalDescriptor, global_type: &GlobalDescriptor,
) -> Result<GlobalRef, Error> { ) -> Result<GlobalRef, Error> {
Ok(GlobalInstance::alloc( Ok(GlobalInstance::alloc(
RuntimeValue::default(global_type.value_type()), RuntimeValue::default(global_type.value_type()),
global_type.is_mutable(), global_type.is_mutable(),
)) ))
} }
fn resolve_memory( fn resolve_memory(
&self, &self,
_field_name: &str, _field_name: &str,
memory_type: &MemoryDescriptor, memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, Error> { ) -> Result<MemoryRef, Error> {
Ok(MemoryInstance::alloc( Ok(MemoryInstance::alloc(
Pages(memory_type.initial() as usize), Pages(memory_type.initial() as usize),
memory_type.maximum().map(|m| Pages(m as usize)), memory_type.maximum().map(|m| Pages(m as usize)),
).unwrap()) )
} .unwrap())
}
fn resolve_table( fn resolve_table(
&self, &self,
_field_name: &str, _field_name: &str,
table_type: &TableDescriptor, table_type: &TableDescriptor,
) -> Result<TableRef, Error> { ) -> Result<TableRef, Error> {
Ok(TableInstance::alloc(table_type.initial(), table_type.maximum()).unwrap()) Ok(TableInstance::alloc(table_type.initial(), table_type.maximum()).unwrap())
} }
} }
fn main() { fn main() {
let args: Vec<_> = args().collect(); let args: Vec<_> = args().collect();
if args.len() != 2 { if args.len() != 2 {
println!("Usage: {} <wasm file>", args[0]); println!("Usage: {} <wasm file>", args[0]);
return; return;
} }
let module = load_from_file(&args[1]); let module = load_from_file(&args[1]);
let _ = ModuleInstance::new( let _ = ModuleInstance::new(
&module, &module,
&ImportsBuilder::default() &ImportsBuilder::default()
// Well known imports. // Well known imports.
.with_resolver("env", &ResolveAll) .with_resolver("env", &ResolveAll)
.with_resolver("global", &ResolveAll) .with_resolver("global", &ResolveAll)
.with_resolver("foo", &ResolveAll) .with_resolver("foo", &ResolveAll)
.with_resolver("global.Math", &ResolveAll) .with_resolver("global.Math", &ResolveAll)
.with_resolver("asm2wasm", &ResolveAll) .with_resolver("asm2wasm", &ResolveAll)
.with_resolver("spectest", &ResolveAll), .with_resolver("spectest", &ResolveAll),
).expect("Failed to instantiate module") )
.run_start(&mut NopExternals) .expect("Failed to instantiate module")
.expect("Failed to run start function in module"); .run_start(&mut NopExternals)
.expect("Failed to run start function in module");
} }

View File

@ -1,4 +1,3 @@
pub mod stack; pub mod stack;
/// Index of default linear memory. /// Index of default linear memory.

View File

@ -1,88 +1,101 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use core::fmt;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::error; use std::error;
use core::fmt;
#[derive(Debug)] #[derive(Debug)]
pub struct Error(String); pub struct Error(String);
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0) write!(f, "{}", self.0)
} }
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl error::Error for Error { impl error::Error for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
&self.0 &self.0
} }
} }
/// Stack with limit. /// Stack with limit.
#[derive(Debug)] #[derive(Debug)]
pub struct StackWithLimit<T> where T: Clone { pub struct StackWithLimit<T>
/// Stack values. where
values: Vec<T>, T: Clone,
/// Stack limit (maximal stack len). {
limit: usize, /// Stack values.
values: Vec<T>,
/// Stack limit (maximal stack len).
limit: usize,
} }
impl<T> StackWithLimit<T> where T: Clone { impl<T> StackWithLimit<T>
pub fn with_limit(limit: usize) -> Self { where
StackWithLimit { T: Clone,
values: Vec::new(), {
limit: limit pub fn with_limit(limit: usize) -> Self {
} StackWithLimit {
} values: Vec::new(),
limit: limit,
}
}
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.values.is_empty() self.values.is_empty()
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.values.len() self.values.len()
} }
pub fn top(&self) -> Result<&T, Error> { pub fn top(&self) -> Result<&T, Error> {
self.values self.values
.last() .last()
.ok_or_else(|| Error("non-empty stack expected".into())) .ok_or_else(|| Error("non-empty stack expected".into()))
} }
pub fn top_mut(&mut self) -> Result<&mut T, Error> { pub fn top_mut(&mut self) -> Result<&mut T, Error> {
self.values self.values
.last_mut() .last_mut()
.ok_or_else(|| Error("non-empty stack expected".into())) .ok_or_else(|| Error("non-empty stack expected".into()))
} }
pub fn get(&self, index: usize) -> Result<&T, Error> { pub fn get(&self, index: usize) -> Result<&T, Error> {
if index >= self.values.len() { if index >= self.values.len() {
return Err(Error(format!("trying to get value at position {} on stack of size {}", index, self.values.len()))); return Err(Error(format!(
} "trying to get value at position {} on stack of size {}",
index,
self.values.len()
)));
}
Ok(self.values.get(self.values.len() - 1 - index).expect("checked couple of lines above")) Ok(self
} .values
.get(self.values.len() - 1 - index)
.expect("checked couple of lines above"))
}
pub fn push(&mut self, value: T) -> Result<(), Error> { pub fn push(&mut self, value: T) -> Result<(), Error> {
if self.values.len() >= self.limit { if self.values.len() >= self.limit {
return Err(Error(format!("exceeded stack limit {}", self.limit))); return Err(Error(format!("exceeded stack limit {}", self.limit)));
} }
self.values.push(value); self.values.push(value);
Ok(()) Ok(())
} }
pub fn pop(&mut self) -> Result<T, Error> { pub fn pop(&mut self) -> Result<T, Error> {
self.values self.values
.pop() .pop()
.ok_or_else(|| Error("non-empty stack expected".into())) .ok_or_else(|| Error("non-empty stack expected".into()))
} }
pub fn resize(&mut self, new_size: usize, dummy: T) { pub fn resize(&mut self, new_size: usize, dummy: T) {
debug_assert!(new_size <= self.values.len()); debug_assert!(new_size <= self.values.len());
self.values.resize(new_size, dummy); self.values.resize(new_size, dummy);
} }
} }

View File

@ -2,14 +2,14 @@
use alloc::prelude::*; use alloc::prelude::*;
use alloc::rc::{Rc, Weak}; use alloc::rc::{Rc, Weak};
use core::fmt; use core::fmt;
use parity_wasm::elements::Local;
use {Trap, Signature};
use host::Externals; use host::Externals;
use runner::{check_function_args, Interpreter, InterpreterState};
use value::RuntimeValue;
use types::ValueType;
use module::ModuleInstance;
use isa; use isa;
use module::ModuleInstance;
use parity_wasm::elements::Local;
use runner::{check_function_args, Interpreter, InterpreterState};
use types::ValueType;
use value::RuntimeValue;
use {Signature, Trap};
/// Reference to a function (See [`FuncInstance`] for details). /// Reference to a function (See [`FuncInstance`] for details).
/// ///
@ -20,10 +20,10 @@ use isa;
pub struct FuncRef(Rc<FuncInstance>); pub struct FuncRef(Rc<FuncInstance>);
impl ::core::ops::Deref for FuncRef { impl ::core::ops::Deref for FuncRef {
type Target = FuncInstance; type Target = FuncInstance;
fn deref(&self) -> &FuncInstance { fn deref(&self) -> &FuncInstance {
&self.0 &self.0
} }
} }
/// Runtime representation of a function. /// Runtime representation of a function.
@ -44,271 +44,271 @@ pub struct FuncInstance(FuncInstanceInternal);
#[derive(Clone)] #[derive(Clone)]
pub(crate) enum FuncInstanceInternal { pub(crate) enum FuncInstanceInternal {
Internal { Internal {
signature: Rc<Signature>, signature: Rc<Signature>,
module: Weak<ModuleInstance>, module: Weak<ModuleInstance>,
body: Rc<FuncBody>, body: Rc<FuncBody>,
}, },
Host { Host {
signature: Signature, signature: Signature,
host_func_index: usize, host_func_index: usize,
}, },
} }
impl fmt::Debug for FuncInstance { impl fmt::Debug for FuncInstance {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.as_internal() { match self.as_internal() {
&FuncInstanceInternal::Internal { &FuncInstanceInternal::Internal { ref signature, .. } => {
ref signature, // We can't write description of self.module here, because it generate
.. // debug string for function instances and this will lead to infinite loop.
} => { write!(f, "Internal {{ signature={:?} }}", signature,)
// We can't write description of self.module here, because it generate }
// debug string for function instances and this will lead to infinite loop. &FuncInstanceInternal::Host { ref signature, .. } => {
write!( write!(f, "Host {{ signature={:?} }}", signature)
f, }
"Internal {{ signature={:?} }}", }
signature, }
)
}
&FuncInstanceInternal::Host { ref signature, .. } => {
write!(f, "Host {{ signature={:?} }}", signature)
}
}
}
} }
impl FuncInstance { impl FuncInstance {
/// Allocate a function instance for a host function. /// Allocate a function instance for a host function.
/// ///
/// When this function instance will be called by the wasm code, /// When this function instance will be called by the wasm code,
/// the instance of [`Externals`] will be invoked by calling `invoke_index` /// the instance of [`Externals`] will be invoked by calling `invoke_index`
/// with specified `host_func_index` here. /// with specified `host_func_index` here.
/// This call will be made with the `signature` provided here. /// This call will be made with the `signature` provided here.
/// ///
/// [`Externals`]: trait.Externals.html /// [`Externals`]: trait.Externals.html
pub fn alloc_host(signature: Signature, host_func_index: usize) -> FuncRef { pub fn alloc_host(signature: Signature, host_func_index: usize) -> FuncRef {
let func = FuncInstanceInternal::Host { let func = FuncInstanceInternal::Host {
signature, signature,
host_func_index, host_func_index,
}; };
FuncRef(Rc::new(FuncInstance(func))) FuncRef(Rc::new(FuncInstance(func)))
} }
/// Returns [signature] of this function instance. /// Returns [signature] of this function instance.
/// ///
/// This function instance can only be called with matching signatures. /// This function instance can only be called with matching signatures.
/// ///
/// [signature]: struct.Signature.html /// [signature]: struct.Signature.html
pub fn signature(&self) -> &Signature { pub fn signature(&self) -> &Signature {
match *self.as_internal() { match *self.as_internal() {
FuncInstanceInternal::Internal { ref signature, .. } => signature, FuncInstanceInternal::Internal { ref signature, .. } => signature,
FuncInstanceInternal::Host { ref signature, .. } => signature, FuncInstanceInternal::Host { ref signature, .. } => signature,
} }
} }
pub(crate) fn as_internal(&self) -> &FuncInstanceInternal { pub(crate) fn as_internal(&self) -> &FuncInstanceInternal {
&self.0 &self.0
} }
pub(crate) fn alloc_internal( pub(crate) fn alloc_internal(
module: Weak<ModuleInstance>, module: Weak<ModuleInstance>,
signature: Rc<Signature>, signature: Rc<Signature>,
body: FuncBody, body: FuncBody,
) -> FuncRef { ) -> FuncRef {
let func = FuncInstanceInternal::Internal { let func = FuncInstanceInternal::Internal {
signature, signature,
module: module, module: module,
body: Rc::new(body), body: Rc::new(body),
}; };
FuncRef(Rc::new(FuncInstance(func))) FuncRef(Rc::new(FuncInstance(func)))
} }
pub(crate) fn body(&self) -> Option<Rc<FuncBody>> { pub(crate) fn body(&self) -> Option<Rc<FuncBody>> {
match *self.as_internal() { match *self.as_internal() {
FuncInstanceInternal::Internal { ref body, .. } => Some(Rc::clone(body)), FuncInstanceInternal::Internal { ref body, .. } => Some(Rc::clone(body)),
FuncInstanceInternal::Host { .. } => None, FuncInstanceInternal::Host { .. } => None,
} }
} }
/// Invoke this function. /// Invoke this function.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if `args` types is not match function [`signature`] or /// Returns `Err` if `args` types is not match function [`signature`] or
/// if [`Trap`] at execution time occured. /// if [`Trap`] at execution time occured.
/// ///
/// [`signature`]: #method.signature /// [`signature`]: #method.signature
/// [`Trap`]: #enum.Trap.html /// [`Trap`]: #enum.Trap.html
pub fn invoke<E: Externals>( pub fn invoke<E: Externals>(
func: &FuncRef, func: &FuncRef,
args: &[RuntimeValue], args: &[RuntimeValue],
externals: &mut E, externals: &mut E,
) -> Result<Option<RuntimeValue>, Trap> { ) -> Result<Option<RuntimeValue>, Trap> {
check_function_args(func.signature(), &args)?; check_function_args(func.signature(), &args)?;
match *func.as_internal() { match *func.as_internal() {
FuncInstanceInternal::Internal { .. } => { FuncInstanceInternal::Internal { .. } => {
let mut interpreter = Interpreter::new(func, args)?; let mut interpreter = Interpreter::new(func, args)?;
interpreter.start_execution(externals) interpreter.start_execution(externals)
} }
FuncInstanceInternal::Host { FuncInstanceInternal::Host {
ref host_func_index, ref host_func_index,
.. ..
} => externals.invoke_index(*host_func_index, args.into()), } => externals.invoke_index(*host_func_index, args.into()),
} }
} }
/// Invoke the function, get a resumable handle. This handle can then be used to [`start_execution`]. If a /// Invoke the function, get a resumable handle. This handle can then be used to [`start_execution`]. If a
/// Host trap happens, caller can use [`resume_execution`] to feed the expected return value back in, and then /// Host trap happens, caller can use [`resume_execution`] to feed the expected return value back in, and then
/// continue the execution. /// continue the execution.
/// ///
/// This is an experimental API, and this functionality may not be available in other WebAssembly engines. /// This is an experimental API, and this functionality may not be available in other WebAssembly engines.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if `args` types is not match function [`signature`]. /// Returns `Err` if `args` types is not match function [`signature`].
/// ///
/// [`signature`]: #method.signature /// [`signature`]: #method.signature
/// [`Trap`]: #enum.Trap.html /// [`Trap`]: #enum.Trap.html
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution /// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution /// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
pub fn invoke_resumable<'args>( pub fn invoke_resumable<'args>(
func: &FuncRef, func: &FuncRef,
args: &'args [RuntimeValue], args: &'args [RuntimeValue],
) -> Result<FuncInvocation<'args>, Trap> { ) -> Result<FuncInvocation<'args>, Trap> {
check_function_args(func.signature(), &args)?; check_function_args(func.signature(), &args)?;
match *func.as_internal() { match *func.as_internal() {
FuncInstanceInternal::Internal { .. } => { FuncInstanceInternal::Internal { .. } => {
let interpreter = Interpreter::new(func, args)?; let interpreter = Interpreter::new(func, args)?;
Ok(FuncInvocation { Ok(FuncInvocation {
kind: FuncInvocationKind::Internal(interpreter), kind: FuncInvocationKind::Internal(interpreter),
}) })
} }
FuncInstanceInternal::Host { FuncInstanceInternal::Host {
ref host_func_index, ref host_func_index,
.. ..
} => { } => Ok(FuncInvocation {
Ok(FuncInvocation { kind: FuncInvocationKind::Host {
kind: FuncInvocationKind::Host { args,
args, host_func_index: *host_func_index,
host_func_index: *host_func_index, finished: false,
finished: false, },
}, }),
}) }
}, }
}
}
} }
/// A resumable invocation error. /// A resumable invocation error.
#[derive(Debug)] #[derive(Debug)]
pub enum ResumableError { pub enum ResumableError {
/// Trap happened. /// Trap happened.
Trap(Trap), Trap(Trap),
/// The invocation is not resumable. /// The invocation is not resumable.
/// ///
/// Invocations are only resumable if a host function is called, and the host function returns a trap of `Host` kind. For other cases, this error will be returned. This includes: /// Invocations are only resumable if a host function is called, and the host function returns a trap of `Host` kind. For other cases, this error will be returned. This includes:
/// - The invocation is directly a host function. /// - The invocation is directly a host function.
/// - The invocation has not been started. /// - The invocation has not been started.
/// - The invocation returns normally or returns any trap other than `Host` kind. /// - The invocation returns normally or returns any trap other than `Host` kind.
/// ///
/// This error is returned by [`resume_execution`]. /// This error is returned by [`resume_execution`].
/// ///
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution /// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
NotResumable, NotResumable,
/// The invocation has already been started. /// The invocation has already been started.
/// ///
/// This error is returned by [`start_execution`]. /// This error is returned by [`start_execution`].
/// ///
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution /// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
AlreadyStarted, AlreadyStarted,
} }
impl From<Trap> for ResumableError { impl From<Trap> for ResumableError {
fn from(trap: Trap) -> Self { fn from(trap: Trap) -> Self {
ResumableError::Trap(trap) ResumableError::Trap(trap)
} }
} }
/// A resumable invocation handle. This struct is returned by `FuncInstance::invoke_resumable`. /// A resumable invocation handle. This struct is returned by `FuncInstance::invoke_resumable`.
pub struct FuncInvocation<'args> { pub struct FuncInvocation<'args> {
kind: FuncInvocationKind<'args>, kind: FuncInvocationKind<'args>,
} }
enum FuncInvocationKind<'args> { enum FuncInvocationKind<'args> {
Internal(Interpreter), Internal(Interpreter),
Host { Host {
args: &'args [RuntimeValue], args: &'args [RuntimeValue],
host_func_index: usize, host_func_index: usize,
finished: bool finished: bool,
}, },
} }
impl<'args> FuncInvocation<'args> { impl<'args> FuncInvocation<'args> {
/// Whether this invocation is currently resumable. /// Whether this invocation is currently resumable.
pub fn is_resumable(&self) -> bool { pub fn is_resumable(&self) -> bool {
match &self.kind { match &self.kind {
&FuncInvocationKind::Internal(ref interpreter) => interpreter.state().is_resumable(), &FuncInvocationKind::Internal(ref interpreter) => interpreter.state().is_resumable(),
&FuncInvocationKind::Host { .. } => false, &FuncInvocationKind::Host { .. } => false,
} }
} }
/// If the invocation is resumable, the expected return value type to be feed back in. /// If the invocation is resumable, the expected return value type to be feed back in.
pub fn resumable_value_type(&self) -> Option<ValueType> { pub fn resumable_value_type(&self) -> Option<ValueType> {
match &self.kind { match &self.kind {
&FuncInvocationKind::Internal(ref interpreter) => { &FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() {
match interpreter.state() { &InterpreterState::Resumable(ref value_type) => value_type.clone(),
&InterpreterState::Resumable(ref value_type) => value_type.clone(), _ => None,
_ => None, },
} &FuncInvocationKind::Host { .. } => None,
}, }
&FuncInvocationKind::Host { .. } => None, }
}
}
/// Start the invocation execution. /// Start the invocation execution.
pub fn start_execution<'externals, E: Externals + 'externals>(&mut self, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> { pub fn start_execution<'externals, E: Externals + 'externals>(
match self.kind { &mut self,
FuncInvocationKind::Internal(ref mut interpreter) => { externals: &'externals mut E,
if interpreter.state() != &InterpreterState::Initialized { ) -> Result<Option<RuntimeValue>, ResumableError> {
return Err(ResumableError::AlreadyStarted); match self.kind {
} FuncInvocationKind::Internal(ref mut interpreter) => {
Ok(interpreter.start_execution(externals)?) if interpreter.state() != &InterpreterState::Initialized {
}, return Err(ResumableError::AlreadyStarted);
FuncInvocationKind::Host { ref args, ref mut finished, ref host_func_index } => { }
if *finished { Ok(interpreter.start_execution(externals)?)
return Err(ResumableError::AlreadyStarted); }
} FuncInvocationKind::Host {
*finished = true; ref args,
Ok(externals.invoke_index(*host_func_index, args.clone().into())?) ref mut finished,
}, ref host_func_index,
} } => {
} if *finished {
return Err(ResumableError::AlreadyStarted);
}
*finished = true;
Ok(externals.invoke_index(*host_func_index, args.clone().into())?)
}
}
}
/// Resume an execution if a previous trap of Host kind happened. /// Resume an execution if a previous trap of Host kind happened.
/// ///
/// `return_val` must be of the value type [`resumable_value_type`], defined by the host function import. Otherwise, /// `return_val` must be of the value type [`resumable_value_type`], defined by the host function import. Otherwise,
/// `UnexpectedSignature` trap will be returned. The current invocation must also be resumable /// `UnexpectedSignature` trap will be returned. The current invocation must also be resumable
/// [`is_resumable`]. Otherwise, a `NotResumable` error will be returned. /// [`is_resumable`]. Otherwise, a `NotResumable` error will be returned.
/// ///
/// [`resumable_value_type`]: #method.resumable_value_type /// [`resumable_value_type`]: #method.resumable_value_type
/// [`is_resumable`]: #method.is_resumable /// [`is_resumable`]: #method.is_resumable
pub fn resume_execution<'externals, E: Externals + 'externals>(&mut self, return_val: Option<RuntimeValue>, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> { pub fn resume_execution<'externals, E: Externals + 'externals>(
match self.kind { &mut self,
FuncInvocationKind::Internal(ref mut interpreter) => { return_val: Option<RuntimeValue>,
if !interpreter.state().is_resumable() { externals: &'externals mut E,
return Err(ResumableError::AlreadyStarted); ) -> Result<Option<RuntimeValue>, ResumableError> {
} match self.kind {
Ok(interpreter.resume_execution(return_val, externals)?) FuncInvocationKind::Internal(ref mut interpreter) => {
}, if !interpreter.state().is_resumable() {
FuncInvocationKind::Host { .. } => { return Err(ResumableError::AlreadyStarted);
return Err(ResumableError::NotResumable); }
}, Ok(interpreter.resume_execution(return_val, externals)?)
} }
} FuncInvocationKind::Host { .. } => {
return Err(ResumableError::NotResumable);
}
}
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct FuncBody { pub struct FuncBody {
pub locals: Vec<Local>, pub locals: Vec<Local>,
pub code: isa::Instructions, pub code: isa::Instructions,
} }

View File

@ -1,9 +1,9 @@
use alloc::rc::Rc; use alloc::rc::Rc;
use core::cell::Cell; use core::cell::Cell;
use parity_wasm::elements::ValueType as EValueType;
use types::ValueType;
use value::RuntimeValue; use value::RuntimeValue;
use Error; use Error;
use types::ValueType;
use parity_wasm::elements::{ValueType as EValueType};
/// Reference to a global variable (See [`GlobalInstance`] for details). /// Reference to a global variable (See [`GlobalInstance`] for details).
/// ///
@ -14,10 +14,10 @@ use parity_wasm::elements::{ValueType as EValueType};
pub struct GlobalRef(Rc<GlobalInstance>); pub struct GlobalRef(Rc<GlobalInstance>);
impl ::core::ops::Deref for GlobalRef { impl ::core::ops::Deref for GlobalRef {
type Target = GlobalInstance; type Target = GlobalInstance;
fn deref(&self) -> &GlobalInstance { fn deref(&self) -> &GlobalInstance {
&self.0 &self.0
} }
} }
/// Runtime representation of a global variable (or `global` for short). /// Runtime representation of a global variable (or `global` for short).
@ -33,57 +33,59 @@ impl ::core::ops::Deref for GlobalRef {
/// [`I64`]: enum.RuntimeValue.html#variant.I64 /// [`I64`]: enum.RuntimeValue.html#variant.I64
#[derive(Debug)] #[derive(Debug)]
pub struct GlobalInstance { pub struct GlobalInstance {
val: Cell<RuntimeValue>, val: Cell<RuntimeValue>,
mutable: bool, mutable: bool,
} }
impl GlobalInstance { impl GlobalInstance {
/// Allocate a global variable instance. /// Allocate a global variable instance.
/// ///
/// Since it is possible to export only immutable globals, /// Since it is possible to export only immutable globals,
/// users likely want to set `mutable` to `false`. /// users likely want to set `mutable` to `false`.
pub fn alloc(val: RuntimeValue, mutable: bool) -> GlobalRef { pub fn alloc(val: RuntimeValue, mutable: bool) -> GlobalRef {
GlobalRef(Rc::new(GlobalInstance { GlobalRef(Rc::new(GlobalInstance {
val: Cell::new(val), val: Cell::new(val),
mutable, mutable,
})) }))
} }
/// Change the value of this global variable. /// Change the value of this global variable.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if this global isn't mutable or if /// Returns `Err` if this global isn't mutable or if
/// type of `val` doesn't match global's type. /// type of `val` doesn't match global's type.
pub fn set(&self, val: RuntimeValue) -> Result<(), Error> { pub fn set(&self, val: RuntimeValue) -> Result<(), Error> {
if !self.mutable { if !self.mutable {
return Err(Error::Global("Attempt to change an immutable variable".into())); return Err(Error::Global(
} "Attempt to change an immutable variable".into(),
if self.value_type() != val.value_type() { ));
return Err(Error::Global("Attempt to change variable type".into())); }
} if self.value_type() != val.value_type() {
self.val.set(val); return Err(Error::Global("Attempt to change variable type".into()));
Ok(()) }
} self.val.set(val);
Ok(())
}
/// Get the value of this global variable. /// Get the value of this global variable.
pub fn get(&self) -> RuntimeValue { pub fn get(&self) -> RuntimeValue {
self.val.get() self.val.get()
} }
/// Returns if this global variable is mutable. /// Returns if this global variable is mutable.
/// ///
/// Note: Imported and/or exported globals are always immutable. /// Note: Imported and/or exported globals are always immutable.
pub fn is_mutable(&self) -> bool { pub fn is_mutable(&self) -> bool {
self.mutable self.mutable
} }
/// Returns value type of this global variable. /// Returns value type of this global variable.
pub fn value_type(&self) -> ValueType { pub fn value_type(&self) -> ValueType {
self.val.get().value_type() self.val.get().value_type()
} }
pub(crate) fn elements_value_type(&self) -> EValueType { pub(crate) fn elements_value_type(&self) -> EValueType {
self.value_type().into_elements() self.value_type().into_elements()
} }
} }

View File

@ -1,6 +1,6 @@
use core::any::TypeId; use core::any::TypeId;
use value::{RuntimeValue, FromRuntimeValue}; use value::{FromRuntimeValue, RuntimeValue};
use {TrapKind, Trap}; use {Trap, TrapKind};
/// Wrapper around slice of [`RuntimeValue`] for using it /// Wrapper around slice of [`RuntimeValue`] for using it
/// as an argument list conveniently. /// as an argument list conveniently.
@ -10,55 +10,64 @@ use {TrapKind, Trap};
pub struct RuntimeArgs<'a>(&'a [RuntimeValue]); pub struct RuntimeArgs<'a>(&'a [RuntimeValue]);
impl<'a> From<&'a [RuntimeValue]> for RuntimeArgs<'a> { impl<'a> From<&'a [RuntimeValue]> for RuntimeArgs<'a> {
fn from(inner: &'a [RuntimeValue]) -> Self { fn from(inner: &'a [RuntimeValue]) -> Self {
RuntimeArgs(inner) RuntimeArgs(inner)
} }
} }
impl<'a> AsRef<[RuntimeValue]> for RuntimeArgs<'a> { impl<'a> AsRef<[RuntimeValue]> for RuntimeArgs<'a> {
fn as_ref(&self) -> &[RuntimeValue] { fn as_ref(&self) -> &[RuntimeValue] {
self.0 self.0
} }
} }
impl<'a> RuntimeArgs<'a> { impl<'a> RuntimeArgs<'a> {
/// Extract argument by index `idx`. /// Extract argument by index `idx`.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if cast is invalid or not enough arguments. /// Returns `Err` if cast is invalid or not enough arguments.
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap> where T: FromRuntimeValue { pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap>
Ok(self.nth_value_checked(idx)?.try_into().ok_or_else(|| TrapKind::UnexpectedSignature)?) where
} T: FromRuntimeValue,
{
Ok(self
.nth_value_checked(idx)?
.try_into()
.ok_or_else(|| TrapKind::UnexpectedSignature)?)
}
/// Extract argument as a [`RuntimeValue`] by index `idx`. /// Extract argument as a [`RuntimeValue`] by index `idx`.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if this list has not enough arguments. /// Returns `Err` if this list has not enough arguments.
/// ///
/// [`RuntimeValue`]: enum.RuntimeValue.html /// [`RuntimeValue`]: enum.RuntimeValue.html
pub fn nth_value_checked(&self, idx: usize) -> Result<RuntimeValue, Trap> { pub fn nth_value_checked(&self, idx: usize) -> Result<RuntimeValue, Trap> {
if self.0.len() <= idx { if self.0.len() <= idx {
return Err(TrapKind::UnexpectedSignature.into()); return Err(TrapKind::UnexpectedSignature.into());
} }
Ok(self.0[idx]) Ok(self.0[idx])
} }
/// Extract argument by index `idx`. /// Extract argument by index `idx`.
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if cast is invalid or not enough arguments. /// Panics if cast is invalid or not enough arguments.
pub fn nth<T>(&self, idx: usize) -> T where T: FromRuntimeValue { pub fn nth<T>(&self, idx: usize) -> T
let value = self.nth_value_checked(idx).expect("Invalid argument index"); where
value.try_into().expect("Unexpected argument type") T: FromRuntimeValue,
} {
let value = self.nth_value_checked(idx).expect("Invalid argument index");
value.try_into().expect("Unexpected argument type")
}
/// Total number of arguments /// Total number of arguments
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.0.len()
} }
} }
/// Trait that allows the host to return custom error. /// Trait that allows the host to return custom error.
@ -99,31 +108,31 @@ impl<'a> RuntimeArgs<'a> {
/// } /// }
/// ``` /// ```
pub trait HostError: 'static + ::core::fmt::Display + ::core::fmt::Debug + Send + Sync { pub trait HostError: 'static + ::core::fmt::Display + ::core::fmt::Debug + Send + Sync {
#[doc(hidden)] #[doc(hidden)]
fn __private_get_type_id__(&self) -> TypeId { fn __private_get_type_id__(&self) -> TypeId {
TypeId::of::<Self>() TypeId::of::<Self>()
} }
} }
impl HostError { impl HostError {
/// Attempt to downcast this `HostError` to a concrete type by reference. /// Attempt to downcast this `HostError` to a concrete type by reference.
pub fn downcast_ref<T: HostError>(&self) -> Option<&T> { pub fn downcast_ref<T: HostError>(&self) -> Option<&T> {
if self.__private_get_type_id__() == TypeId::of::<T>() { if self.__private_get_type_id__() == TypeId::of::<T>() {
unsafe { Some(&*(self as *const HostError as *const T)) } unsafe { Some(&*(self as *const HostError as *const T)) }
} else { } else {
None None
} }
} }
/// Attempt to downcast this `HostError` to a concrete type by mutable /// Attempt to downcast this `HostError` to a concrete type by mutable
/// reference. /// reference.
pub fn downcast_mut<T: HostError>(&mut self) -> Option<&mut T> { pub fn downcast_mut<T: HostError>(&mut self) -> Option<&mut T> {
if self.__private_get_type_id__() == TypeId::of::<T>() { if self.__private_get_type_id__() == TypeId::of::<T>() {
unsafe { Some(&mut *(self as *mut HostError as *mut T)) } unsafe { Some(&mut *(self as *mut HostError as *mut T)) }
} else { } else {
None None
} }
} }
} }
/// Trait that allows to implement host functions. /// Trait that allows to implement host functions.
@ -189,13 +198,13 @@ impl HostError {
/// )) /// ))
/// } /// }
/// }; /// };
/// ///
/// if !self.check_signature(index, signature) { /// if !self.check_signature(index, signature) {
/// return Err(Error::Instantiation( /// return Err(Error::Instantiation(
/// format!("Export {} has a bad signature", field_name) /// format!("Export {} has a bad signature", field_name)
/// )); /// ));
/// } /// }
/// ///
/// Ok(FuncInstance::alloc_host( /// Ok(FuncInstance::alloc_host(
/// Signature::new(&[ValueType::I32, ValueType::I32][..], Some(ValueType::I32)), /// Signature::new(&[ValueType::I32, ValueType::I32][..], Some(ValueType::I32)),
/// index, /// index,
@ -204,12 +213,12 @@ impl HostError {
/// } /// }
/// ``` /// ```
pub trait Externals { pub trait Externals {
/// Perform invoke of a host function by specified `index`. /// Perform invoke of a host function by specified `index`.
fn invoke_index( fn invoke_index(
&mut self, &mut self,
index: usize, index: usize,
args: RuntimeArgs, args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Trap>; ) -> Result<Option<RuntimeValue>, Trap>;
} }
/// Implementation of [`Externals`] that just traps on [`invoke_index`]. /// Implementation of [`Externals`] that just traps on [`invoke_index`].
@ -219,35 +228,34 @@ pub trait Externals {
pub struct NopExternals; pub struct NopExternals;
impl Externals for NopExternals { impl Externals for NopExternals {
fn invoke_index( fn invoke_index(
&mut self, &mut self,
_index: usize, _index: usize,
_args: RuntimeArgs, _args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Trap> { ) -> Result<Option<RuntimeValue>, Trap> {
Err(TrapKind::Unreachable.into()) Err(TrapKind::Unreachable.into())
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use value::RuntimeValue; use super::{HostError, RuntimeArgs};
use super::{RuntimeArgs, HostError}; use value::RuntimeValue;
#[test] #[test]
fn i32_runtime_args() { fn i32_runtime_args() {
let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into(); let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into();
let val: i32 = args.nth_checked(0).unwrap(); let val: i32 = args.nth_checked(0).unwrap();
assert_eq!(val, 0); assert_eq!(val, 0);
} }
#[test] #[test]
fn i64_invalid_arg_cast() { fn i64_invalid_arg_cast() {
let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into(); let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into();
assert!(args.nth_checked::<i32>(0).is_err()); assert!(args.nth_checked::<i32>(0).is_err());
} }
// Tests that `HostError` trait is object safe. // Tests that `HostError` trait is object safe.
fn _host_error_is_object_safe(_: &HostError) { fn _host_error_is_object_safe(_: &HostError) {}
}
} }

View File

@ -1,20 +1,19 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
#[cfg(feature = "std")]
use std::collections::HashMap;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use hashmap_core::HashMap; use hashmap_core::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
use func::FuncRef;
use global::GlobalRef; use global::GlobalRef;
use memory::MemoryRef; use memory::MemoryRef;
use func::FuncRef;
use table::TableRef;
use module::ModuleRef; use module::ModuleRef;
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor}; use table::TableRef;
use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor};
use {Error, Signature}; use {Error, Signature};
/// Resolver of a module's dependencies. /// Resolver of a module's dependencies.
/// ///
/// A module have dependencies in a form of a list of imports (i.e. /// A module have dependencies in a form of a list of imports (i.e.
@ -27,56 +26,55 @@ use {Error, Signature};
/// ///
/// [`ImportsBuilder`]: struct.ImportsBuilder.html /// [`ImportsBuilder`]: struct.ImportsBuilder.html
pub trait ImportResolver { pub trait ImportResolver {
/// Resolve a function.
///
/// Returned function should match given `signature`, i.e. all parameter types and return value should have exact match.
/// Otherwise, link-time error will occur.
fn resolve_func(
&self,
_module_name: &str,
field_name: &str,
_signature: &Signature,
) -> Result<FuncRef, Error>;
/// Resolve a function. /// Resolve a global variable.
/// ///
/// Returned function should match given `signature`, i.e. all parameter types and return value should have exact match. /// Returned global should match given `descriptor`, i.e. type and mutability
/// Otherwise, link-time error will occur. /// should match. Otherwise, link-time error will occur.
fn resolve_func( fn resolve_global(
&self, &self,
_module_name: &str, module_name: &str,
field_name: &str, field_name: &str,
_signature: &Signature, descriptor: &GlobalDescriptor,
) -> Result<FuncRef, Error>; ) -> Result<GlobalRef, Error>;
/// Resolve a global variable. /// Resolve a memory.
/// ///
/// Returned global should match given `descriptor`, i.e. type and mutability /// Returned memory should match requested memory (described by the `descriptor`),
/// should match. Otherwise, link-time error will occur. /// i.e. initial size of a returned memory should be equal or larger than requested memory.
fn resolve_global( /// Furthermore, if requested memory have maximum size, returned memory either should have
&self, /// equal or larger maximum size or have no maximum size at all.
module_name: &str, /// If returned memory doesn't match the requested then link-time error will occur.
field_name: &str, fn resolve_memory(
descriptor: &GlobalDescriptor, &self,
) -> Result<GlobalRef, Error>; module_name: &str,
field_name: &str,
descriptor: &MemoryDescriptor,
) -> Result<MemoryRef, Error>;
/// Resolve a memory. /// Resolve a table.
/// ///
/// Returned memory should match requested memory (described by the `descriptor`), /// Returned table should match requested table (described by the `descriptor`),
/// i.e. initial size of a returned memory should be equal or larger than requested memory. /// i.e. initial size of a returned table should be equal or larger than requested table.
/// Furthermore, if requested memory have maximum size, returned memory either should have /// Furthermore, if requested memory have maximum size, returned memory either should have
/// equal or larger maximum size or have no maximum size at all. /// equal or larger maximum size or have no maximum size at all.
/// If returned memory doesn't match the requested then link-time error will occur. /// If returned table doesn't match the requested then link-time error will occur.
fn resolve_memory( fn resolve_table(
&self, &self,
module_name: &str, module_name: &str,
field_name: &str, field_name: &str,
descriptor: &MemoryDescriptor, descriptor: &TableDescriptor,
) -> Result<MemoryRef, Error>; ) -> Result<TableRef, Error>;
/// Resolve a table.
///
/// Returned table should match requested table (described by the `descriptor`),
/// i.e. initial size of a returned table should be equal or larger than requested table.
/// Furthermore, if requested memory have maximum size, returned memory either should have
/// equal or larger maximum size or have no maximum size at all.
/// If returned table doesn't match the requested then link-time error will occur.
fn resolve_table(
&self,
module_name: &str,
field_name: &str,
descriptor: &TableDescriptor,
) -> Result<TableRef, Error>;
} }
/// Convenience builder of [`ImportResolver`]. /// Convenience builder of [`ImportResolver`].
@ -108,216 +106,208 @@ pub trait ImportResolver {
/// [`ImportResolver`]: trait.ImportResolver.html /// [`ImportResolver`]: trait.ImportResolver.html
/// [`ModuleImportResolver`]: trait.ModuleImportResolver.html /// [`ModuleImportResolver`]: trait.ModuleImportResolver.html
pub struct ImportsBuilder<'a> { pub struct ImportsBuilder<'a> {
modules: HashMap<String, &'a ModuleImportResolver>, modules: HashMap<String, &'a ModuleImportResolver>,
} }
impl<'a> Default for ImportsBuilder<'a> { impl<'a> Default for ImportsBuilder<'a> {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl<'a> ImportsBuilder<'a> { impl<'a> ImportsBuilder<'a> {
/// Create an empty `ImportsBuilder`. /// Create an empty `ImportsBuilder`.
pub fn new() -> ImportsBuilder<'a> { pub fn new() -> ImportsBuilder<'a> {
ImportsBuilder { modules: HashMap::new() } ImportsBuilder {
} modules: HashMap::new(),
}
}
/// Register an resolver by a name. /// Register an resolver by a name.
pub fn with_resolver<N: Into<String>>( pub fn with_resolver<N: Into<String>>(
mut self, mut self,
name: N, name: N,
resolver: &'a ModuleImportResolver, resolver: &'a ModuleImportResolver,
) -> Self { ) -> Self {
self.modules.insert(name.into(), resolver); self.modules.insert(name.into(), resolver);
self self
} }
/// Register an resolver by a name. /// Register an resolver by a name.
/// ///
/// Mutable borrowed version. /// Mutable borrowed version.
pub fn push_resolver<N: Into<String>>(&mut self, name: N, resolver: &'a ModuleImportResolver) { pub fn push_resolver<N: Into<String>>(&mut self, name: N, resolver: &'a ModuleImportResolver) {
self.modules.insert(name.into(), resolver); self.modules.insert(name.into(), resolver);
} }
fn resolver(&self, name: &str) -> Option<&ModuleImportResolver> { fn resolver(&self, name: &str) -> Option<&ModuleImportResolver> {
self.modules.get(name).cloned() self.modules.get(name).cloned()
} }
} }
impl<'a> ImportResolver for ImportsBuilder<'a> { impl<'a> ImportResolver for ImportsBuilder<'a> {
fn resolve_func( fn resolve_func(
&self, &self,
module_name: &str, module_name: &str,
field_name: &str, field_name: &str,
signature: &Signature, signature: &Signature,
) -> Result<FuncRef, Error> { ) -> Result<FuncRef, Error> {
self.resolver(module_name).ok_or_else(|| self.resolver(module_name)
Error::Instantiation(format!("Module {} not found", module_name)) .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
)?.resolve_func(field_name, signature) .resolve_func(field_name, signature)
} }
fn resolve_global( fn resolve_global(
&self, &self,
module_name: &str, module_name: &str,
field_name: &str, field_name: &str,
global_type: &GlobalDescriptor, global_type: &GlobalDescriptor,
) -> Result<GlobalRef, Error> { ) -> Result<GlobalRef, Error> {
self.resolver(module_name).ok_or_else(|| self.resolver(module_name)
Error::Instantiation(format!("Module {} not found", module_name)) .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
)?.resolve_global(field_name, global_type) .resolve_global(field_name, global_type)
} }
fn resolve_memory( fn resolve_memory(
&self, &self,
module_name: &str, module_name: &str,
field_name: &str, field_name: &str,
memory_type: &MemoryDescriptor, memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, Error> { ) -> Result<MemoryRef, Error> {
self.resolver(module_name).ok_or_else(|| self.resolver(module_name)
Error::Instantiation(format!("Module {} not found", module_name)) .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
)?.resolve_memory(field_name, memory_type) .resolve_memory(field_name, memory_type)
} }
fn resolve_table( fn resolve_table(
&self, &self,
module_name: &str, module_name: &str,
field_name: &str, field_name: &str,
table_type: &TableDescriptor, table_type: &TableDescriptor,
) -> Result<TableRef, Error> { ) -> Result<TableRef, Error> {
self.resolver(module_name).ok_or_else(|| self.resolver(module_name)
Error::Instantiation(format!("Module {} not found", module_name)) .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
)?.resolve_table(field_name, table_type) .resolve_table(field_name, table_type)
} }
} }
/// Version of [`ImportResolver`] specialized for a single module. /// Version of [`ImportResolver`] specialized for a single module.
/// ///
/// [`ImportResolver`]: trait.ImportResolver.html /// [`ImportResolver`]: trait.ImportResolver.html
pub trait ModuleImportResolver { pub trait ModuleImportResolver {
/// Resolve a function. /// Resolve a function.
/// ///
/// See [`ImportResolver::resolve_func`] for details. /// See [`ImportResolver::resolve_func`] for details.
/// ///
/// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func /// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func
fn resolve_func( fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
&self, Err(Error::Instantiation(format!(
field_name: &str, "Export {} not found",
_signature: &Signature, field_name
) -> Result<FuncRef, Error> { )))
Err(Error::Instantiation( }
format!("Export {} not found", field_name),
))
}
/// Resolve a global variable. /// Resolve a global variable.
/// ///
/// See [`ImportResolver::resolve_global`] for details. /// See [`ImportResolver::resolve_global`] for details.
/// ///
/// [`ImportResolver::resolve_global`]: trait.ImportResolver.html#tymethod.resolve_global /// [`ImportResolver::resolve_global`]: trait.ImportResolver.html#tymethod.resolve_global
fn resolve_global( fn resolve_global(
&self, &self,
field_name: &str, field_name: &str,
_global_type: &GlobalDescriptor, _global_type: &GlobalDescriptor,
) -> Result<GlobalRef, Error> { ) -> Result<GlobalRef, Error> {
Err(Error::Instantiation( Err(Error::Instantiation(format!(
format!("Export {} not found", field_name), "Export {} not found",
)) field_name
} )))
}
/// Resolve a memory. /// Resolve a memory.
/// ///
/// See [`ImportResolver::resolve_memory`] for details. /// See [`ImportResolver::resolve_memory`] for details.
/// ///
/// [`ImportResolver::resolve_memory`]: trait.ImportResolver.html#tymethod.resolve_memory /// [`ImportResolver::resolve_memory`]: trait.ImportResolver.html#tymethod.resolve_memory
fn resolve_memory( fn resolve_memory(
&self, &self,
field_name: &str, field_name: &str,
_memory_type: &MemoryDescriptor, _memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, Error> { ) -> Result<MemoryRef, Error> {
Err(Error::Instantiation( Err(Error::Instantiation(format!(
format!("Export {} not found", field_name), "Export {} not found",
)) field_name
} )))
}
/// Resolve a table. /// Resolve a table.
/// ///
/// See [`ImportResolver::resolve_table`] for details. /// See [`ImportResolver::resolve_table`] for details.
/// ///
/// [`ImportResolver::resolve_table`]: trait.ImportResolver.html#tymethod.resolve_table /// [`ImportResolver::resolve_table`]: trait.ImportResolver.html#tymethod.resolve_table
fn resolve_table( fn resolve_table(
&self, &self,
field_name: &str, field_name: &str,
_table_type: &TableDescriptor, _table_type: &TableDescriptor,
) -> Result<TableRef, Error> { ) -> Result<TableRef, Error> {
Err(Error::Instantiation( Err(Error::Instantiation(format!(
format!("Export {} not found", field_name), "Export {} not found",
)) field_name
} )))
}
} }
impl ModuleImportResolver for ModuleRef { impl ModuleImportResolver for ModuleRef {
fn resolve_func( fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
&self, Ok(self
field_name: &str, .export_by_name(field_name)
_signature: &Signature, .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
) -> Result<FuncRef, Error> { .as_func()
Ok(self.export_by_name(field_name) .cloned()
.ok_or_else(|| { .ok_or_else(|| {
Error::Instantiation(format!("Export {} not found", field_name)) Error::Instantiation(format!("Export {} is not a function", field_name))
})? })?)
.as_func() }
.cloned()
.ok_or_else(|| {
Error::Instantiation(format!("Export {} is not a function", field_name))
})?)
}
fn resolve_global( fn resolve_global(
&self, &self,
field_name: &str, field_name: &str,
_global_type: &GlobalDescriptor, _global_type: &GlobalDescriptor,
) -> Result<GlobalRef, Error> { ) -> Result<GlobalRef, Error> {
Ok(self.export_by_name(field_name) Ok(self
.ok_or_else(|| { .export_by_name(field_name)
Error::Instantiation(format!("Export {} not found", field_name)) .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
})? .as_global()
.as_global() .cloned()
.cloned() .ok_or_else(|| {
.ok_or_else(|| { Error::Instantiation(format!("Export {} is not a global", field_name))
Error::Instantiation(format!("Export {} is not a global", field_name)) })?)
})?) }
}
fn resolve_memory( fn resolve_memory(
&self, &self,
field_name: &str, field_name: &str,
_memory_type: &MemoryDescriptor, _memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, Error> { ) -> Result<MemoryRef, Error> {
Ok(self.export_by_name(field_name) Ok(self
.ok_or_else(|| { .export_by_name(field_name)
Error::Instantiation(format!("Export {} not found", field_name)) .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
})? .as_memory()
.as_memory() .cloned()
.cloned() .ok_or_else(|| {
.ok_or_else(|| { Error::Instantiation(format!("Export {} is not a memory", field_name))
Error::Instantiation(format!("Export {} is not a memory", field_name)) })?)
})?) }
}
fn resolve_table( fn resolve_table(
&self, &self,
field_name: &str, field_name: &str,
_table_type: &TableDescriptor, _table_type: &TableDescriptor,
) -> Result<TableRef, Error> { ) -> Result<TableRef, Error> {
Ok(self.export_by_name(field_name) Ok(self
.ok_or_else(|| { .export_by_name(field_name)
Error::Instantiation(format!("Export {} not found", field_name)) .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
})? .as_table()
.as_table() .cloned()
.cloned() .ok_or_else(|| Error::Instantiation(format!("Export {} is not a table", field_name)))?)
.ok_or_else(|| { }
Error::Instantiation(format!("Export {} is not a table", field_name))
})?)
}
} }

1198
src/isa.rs

File diff suppressed because it is too large Load Diff

View File

@ -95,9 +95,7 @@
//! ``` //! ```
#![warn(missing_docs)] #![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
//// alloc is required in no_std //// alloc is required in no_std
#![cfg_attr(not(feature = "std"), feature(alloc))] #![cfg_attr(not(feature = "std"), feature(alloc))]
@ -117,11 +115,11 @@ extern crate wabt;
#[macro_use] #[macro_use]
extern crate assert_matches; extern crate assert_matches;
extern crate parity_wasm;
extern crate byteorder; extern crate byteorder;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
extern crate hashmap_core; extern crate hashmap_core;
extern crate memory_units as memory_units_crate; extern crate memory_units as memory_units_crate;
extern crate parity_wasm;
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
@ -138,32 +136,32 @@ extern crate libm;
/// Traps can't be handled by WebAssembly code, but are reported to the embedder. /// Traps can't be handled by WebAssembly code, but are reported to the embedder.
#[derive(Debug)] #[derive(Debug)]
pub struct Trap { pub struct Trap {
kind: TrapKind, kind: TrapKind,
} }
impl Trap { impl Trap {
/// Create new trap. /// Create new trap.
pub fn new(kind: TrapKind) -> Trap { pub fn new(kind: TrapKind) -> Trap {
Trap { kind } Trap { kind }
} }
/// Returns kind of this trap. /// Returns kind of this trap.
pub fn kind(&self) -> &TrapKind { pub fn kind(&self) -> &TrapKind {
&self.kind &self.kind
} }
} }
impl fmt::Display for Trap { impl fmt::Display for Trap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Trap: {:?}", self.kind) write!(f, "Trap: {:?}", self.kind)
} }
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl error::Error for Trap { impl error::Error for Trap {
fn description(&self) -> &str { fn description(&self) -> &str {
"runtime trap" "runtime trap"
} }
} }
/// Error type which can be thrown by wasm code or by host environment. /// Error type which can be thrown by wasm code or by host environment.
@ -173,393 +171,393 @@ impl error::Error for Trap {
/// [`Trap`]: struct.Trap.html /// [`Trap`]: struct.Trap.html
#[derive(Debug)] #[derive(Debug)]
pub enum TrapKind { pub enum TrapKind {
/// Wasm code executed `unreachable` opcode. /// Wasm code executed `unreachable` opcode.
/// ///
/// `unreachable` is a special opcode which always traps upon execution. /// `unreachable` is a special opcode which always traps upon execution.
/// This opcode have a similar purpose as `ud2` in x86. /// This opcode have a similar purpose as `ud2` in x86.
Unreachable, Unreachable,
/// Attempt to load or store at the address which /// Attempt to load or store at the address which
/// lies outside of bounds of the memory. /// lies outside of bounds of the memory.
/// ///
/// Since addresses are interpreted as unsigned integers, out of bounds access /// Since addresses are interpreted as unsigned integers, out of bounds access
/// can't happen with negative addresses (i.e. they will always wrap). /// can't happen with negative addresses (i.e. they will always wrap).
MemoryAccessOutOfBounds, MemoryAccessOutOfBounds,
/// Attempt to access table element at index which /// Attempt to access table element at index which
/// lies outside of bounds. /// lies outside of bounds.
/// ///
/// This typically can happen when `call_indirect` is executed /// This typically can happen when `call_indirect` is executed
/// with index that lies out of bounds. /// with index that lies out of bounds.
/// ///
/// Since indexes are interpreted as unsinged integers, out of bounds access /// Since indexes are interpreted as unsinged integers, out of bounds access
/// can't happen with negative indexes (i.e. they will always wrap). /// can't happen with negative indexes (i.e. they will always wrap).
TableAccessOutOfBounds, TableAccessOutOfBounds,
/// Attempt to access table element which is uninitialized (i.e. `None`). /// Attempt to access table element which is uninitialized (i.e. `None`).
/// ///
/// This typically can happen when `call_indirect` is executed. /// This typically can happen when `call_indirect` is executed.
ElemUninitialized, ElemUninitialized,
/// Attempt to divide by zero. /// Attempt to divide by zero.
/// ///
/// This trap typically can happen if `div` or `rem` is executed with /// This trap typically can happen if `div` or `rem` is executed with
/// zero as divider. /// zero as divider.
DivisionByZero, DivisionByZero,
/// Attempt to make a conversion to an int failed. /// Attempt to make a conversion to an int failed.
/// ///
/// This can happen when: /// This can happen when:
/// ///
/// - trying to do signed division (or get the remainder) -2<sup>N-1</sup> over -1. This is /// - trying to do signed division (or get the remainder) -2<sup>N-1</sup> over -1. This is
/// because the result +2<sup>N-1</sup> isn't representable as a N-bit signed integer. /// because the result +2<sup>N-1</sup> isn't representable as a N-bit signed integer.
/// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer. /// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer.
InvalidConversionToInt, InvalidConversionToInt,
/// Stack overflow. /// Stack overflow.
/// ///
/// This is likely caused by some infinite or very deep recursion. /// This is likely caused by some infinite or very deep recursion.
/// Extensive inlining might also be the cause of stack overflow. /// Extensive inlining might also be the cause of stack overflow.
StackOverflow, StackOverflow,
/// Attempt to invoke a function with mismatching signature. /// Attempt to invoke a function with mismatching signature.
/// ///
/// This can happen if [`FuncInstance`] was invoked /// This can happen if [`FuncInstance`] was invoked
/// with mismatching [signature][`Signature`]. /// with mismatching [signature][`Signature`].
/// ///
/// This can always happen with indirect calls. `call_indirect` instruction always /// This can always happen with indirect calls. `call_indirect` instruction always
/// specifies the expected signature of function. If `call_indirect` is executed /// specifies the expected signature of function. If `call_indirect` is executed
/// with index that points on function with signature different that is /// with index that points on function with signature different that is
/// expected by this `call_indirect`, this trap is raised. /// expected by this `call_indirect`, this trap is raised.
/// ///
/// [`Signature`]: struct.Signature.html /// [`Signature`]: struct.Signature.html
UnexpectedSignature, UnexpectedSignature,
/// Error specified by the host. /// Error specified by the host.
/// ///
/// Typically returned from an implementation of [`Externals`]. /// Typically returned from an implementation of [`Externals`].
/// ///
/// [`Externals`]: trait.Externals.html /// [`Externals`]: trait.Externals.html
Host(Box<host::HostError>), Host(Box<host::HostError>),
} }
impl TrapKind { impl TrapKind {
/// Whether this trap is specified by the host. /// Whether this trap is specified by the host.
pub fn is_host(&self) -> bool { pub fn is_host(&self) -> bool {
match self { match self {
&TrapKind::Host(_) => true, &TrapKind::Host(_) => true,
_ => false, _ => false,
} }
} }
} }
/// Internal interpreter error. /// Internal interpreter error.
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Module validation error. Might occur only at load time. /// Module validation error. Might occur only at load time.
Validation(String), Validation(String),
/// Error while instantiating a module. Might occur when provided /// Error while instantiating a module. Might occur when provided
/// with incorrect exports (i.e. linkage failure). /// with incorrect exports (i.e. linkage failure).
Instantiation(String), Instantiation(String),
/// Function-level error. /// Function-level error.
Function(String), Function(String),
/// Table-level error. /// Table-level error.
Table(String), Table(String),
/// Memory-level error. /// Memory-level error.
Memory(String), Memory(String),
/// Global-level error. /// Global-level error.
Global(String), Global(String),
/// Value-level error. /// Value-level error.
Value(String), Value(String),
/// Trap. /// Trap.
Trap(Trap), Trap(Trap),
/// Custom embedder error. /// Custom embedder error.
Host(Box<host::HostError>), Host(Box<host::HostError>),
} }
impl Error { impl Error {
/// Returns [`HostError`] if this `Error` represents some host error. /// Returns [`HostError`] if this `Error` represents some host error.
/// ///
/// I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error. /// I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error.
/// ///
/// [`HostError`]: trait.HostError.html /// [`HostError`]: trait.HostError.html
/// [`Host`]: enum.Error.html#variant.Host /// [`Host`]: enum.Error.html#variant.Host
/// [`Trap`]: enum.Error.html#variant.Trap /// [`Trap`]: enum.Error.html#variant.Trap
/// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host /// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
pub fn as_host_error(&self) -> Option<&host::HostError> { pub fn as_host_error(&self) -> Option<&host::HostError> {
match *self { match *self {
Error::Host(ref host_err) => Some(&**host_err), Error::Host(ref host_err) => Some(&**host_err),
Error::Trap(ref trap) => match *trap.kind() { Error::Trap(ref trap) => match *trap.kind() {
TrapKind::Host(ref host_err) => Some(&**host_err), TrapKind::Host(ref host_err) => Some(&**host_err),
_ => None, _ => None,
} },
_ => None, _ => None,
} }
} }
} }
impl Into<String> for Error { impl Into<String> for Error {
fn into(self) -> String { fn into(self) -> String {
match self { match self {
Error::Validation(s) => s, Error::Validation(s) => s,
Error::Instantiation(s) => s, Error::Instantiation(s) => s,
Error::Function(s) => s, Error::Function(s) => s,
Error::Table(s) => s, Error::Table(s) => s,
Error::Memory(s) => s, Error::Memory(s) => s,
Error::Global(s) => s, Error::Global(s) => s,
Error::Value(s) => s, Error::Value(s) => s,
Error::Trap(s) => format!("trap: {:?}", s), Error::Trap(s) => format!("trap: {:?}", s),
Error::Host(e) => format!("user: {}", e), Error::Host(e) => format!("user: {}", e),
} }
} }
} }
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
Error::Validation(ref s) => write!(f, "Validation: {}", s), Error::Validation(ref s) => write!(f, "Validation: {}", s),
Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s), Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s),
Error::Function(ref s) => write!(f, "Function: {}", s), Error::Function(ref s) => write!(f, "Function: {}", s),
Error::Table(ref s) => write!(f, "Table: {}", s), Error::Table(ref s) => write!(f, "Table: {}", s),
Error::Memory(ref s) => write!(f, "Memory: {}", s), Error::Memory(ref s) => write!(f, "Memory: {}", s),
Error::Global(ref s) => write!(f, "Global: {}", s), Error::Global(ref s) => write!(f, "Global: {}", s),
Error::Value(ref s) => write!(f, "Value: {}", s), Error::Value(ref s) => write!(f, "Value: {}", s),
Error::Trap(ref s) => write!(f, "Trap: {:?}", s), Error::Trap(ref s) => write!(f, "Trap: {:?}", s),
Error::Host(ref e) => write!(f, "User: {}", e), Error::Host(ref e) => write!(f, "User: {}", e),
} }
} }
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl error::Error for Error { impl error::Error for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
Error::Validation(ref s) => s, Error::Validation(ref s) => s,
Error::Instantiation(ref s) => s, Error::Instantiation(ref s) => s,
Error::Function(ref s) => s, Error::Function(ref s) => s,
Error::Table(ref s) => s, Error::Table(ref s) => s,
Error::Memory(ref s) => s, Error::Memory(ref s) => s,
Error::Global(ref s) => s, Error::Global(ref s) => s,
Error::Value(ref s) => s, Error::Value(ref s) => s,
Error::Trap(_) => "Trap", Error::Trap(_) => "Trap",
Error::Host(_) => "Host error", Error::Host(_) => "Host error",
} }
} }
} }
impl<U> From<U> for Error where U: host::HostError + Sized { impl<U> From<U> for Error
fn from(e: U) -> Self { where
Error::Host(Box::new(e)) U: host::HostError + Sized,
} {
fn from(e: U) -> Self {
Error::Host(Box::new(e))
}
} }
impl<U> From<U> for Trap where U: host::HostError + Sized { impl<U> From<U> for Trap
fn from(e: U) -> Self { where
Trap::new(TrapKind::Host(Box::new(e))) U: host::HostError + Sized,
} {
fn from(e: U) -> Self {
Trap::new(TrapKind::Host(Box::new(e)))
}
} }
impl From<Trap> for Error { impl From<Trap> for Error {
fn from(e: Trap) -> Error { fn from(e: Trap) -> Error {
Error::Trap(e) Error::Trap(e)
} }
} }
impl From<TrapKind> for Trap { impl From<TrapKind> for Trap {
fn from(e: TrapKind) -> Trap { fn from(e: TrapKind) -> Trap {
Trap::new(e) Trap::new(e)
} }
} }
impl From<validation::Error> for Error { impl From<validation::Error> for Error {
fn from(e: validation::Error) -> Error { fn from(e: validation::Error) -> Error {
Error::Validation(e.to_string()) Error::Validation(e.to_string())
} }
} }
mod validation;
mod common; mod common;
mod memory; mod func;
mod module; mod global;
mod runner;
mod table;
mod value;
mod host; mod host;
mod imports; mod imports;
mod global;
mod func;
mod types;
mod isa; mod isa;
mod memory;
mod module;
pub mod nan_preserving_float; pub mod nan_preserving_float;
mod runner;
mod table;
mod types;
mod validation;
mod value;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE}; pub use self::func::{FuncInstance, FuncInvocation, FuncRef, ResumableError};
pub use self::table::{TableInstance, TableRef};
pub use self::value::{FromRuntimeValue, RuntimeValue, LittleEndianConvert, Error as ValueError};
pub use self::host::{Externals, NopExternals, HostError, RuntimeArgs};
pub use self::imports::{ModuleImportResolver, ImportResolver, ImportsBuilder};
pub use self::module::{ModuleInstance, ModuleRef, ExternVal, NotStartedModuleRef};
pub use self::global::{GlobalInstance, GlobalRef}; pub use self::global::{GlobalInstance, GlobalRef};
pub use self::func::{FuncInstance, FuncRef, FuncInvocation, ResumableError}; pub use self::host::{Externals, HostError, NopExternals, RuntimeArgs};
pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor}; pub use self::imports::{ImportResolver, ImportsBuilder, ModuleImportResolver};
pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
pub use self::module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef};
pub use self::table::{TableInstance, TableRef};
pub use self::types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor, ValueType};
pub use self::value::{Error as ValueError, FromRuntimeValue, LittleEndianConvert, RuntimeValue};
/// WebAssembly-specific sizes and units. /// WebAssembly-specific sizes and units.
pub mod memory_units { pub mod memory_units {
pub use memory_units_crate::wasm32::*; pub use memory_units_crate::wasm32::*;
pub use memory_units_crate::{Bytes, ByteSize, RoundUpTo, size_of}; pub use memory_units_crate::{size_of, ByteSize, Bytes, RoundUpTo};
} }
/// Deserialized module prepared for instantiation. /// Deserialized module prepared for instantiation.
pub struct Module { pub struct Module {
code_map: Vec<isa::Instructions>, code_map: Vec<isa::Instructions>,
module: parity_wasm::elements::Module, module: parity_wasm::elements::Module,
} }
impl Module { impl Module {
/// Create `Module` from `parity_wasm::elements::Module`. /// Create `Module` from `parity_wasm::elements::Module`.
/// ///
/// This function will load, validate and prepare a `parity_wasm`'s `Module`. /// This function will load, validate and prepare a `parity_wasm`'s `Module`.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if provided `Module` is not valid. /// Returns `Err` if provided `Module` is not valid.
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// extern crate parity_wasm; /// extern crate parity_wasm;
/// extern crate wasmi; /// extern crate wasmi;
/// ///
/// use parity_wasm::builder; /// use parity_wasm::builder;
/// use parity_wasm::elements; /// use parity_wasm::elements;
/// ///
/// fn main() { /// fn main() {
/// let parity_module = /// let parity_module =
/// builder::module() /// builder::module()
/// .function() /// .function()
/// .signature().with_param(elements::ValueType::I32).build() /// .signature().with_param(elements::ValueType::I32).build()
/// .body().build() /// .body().build()
/// .build() /// .build()
/// .build(); /// .build();
/// ///
/// let module = wasmi::Module::from_parity_wasm_module(parity_module) /// let module = wasmi::Module::from_parity_wasm_module(parity_module)
/// .expect("parity-wasm builder generated invalid module!"); /// .expect("parity-wasm builder generated invalid module!");
/// ///
/// // Instantiate `module`, etc... /// // Instantiate `module`, etc...
/// } /// }
/// ``` /// ```
pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> { pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> {
use validation::{validate_module, ValidatedModule}; use validation::{validate_module, ValidatedModule};
let ValidatedModule { let ValidatedModule { code_map, module } = validate_module(module)?;
code_map,
module,
} = validate_module(module)?;
Ok(Module { Ok(Module { code_map, module })
code_map, }
module,
})
}
/// Fail if the module contains any floating-point operations /// Fail if the module contains any floating-point operations
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if provided `Module` is not valid. /// Returns `Err` if provided `Module` is not valid.
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # extern crate wasmi; /// # extern crate wasmi;
/// # extern crate wabt; /// # extern crate wabt;
/// ///
/// let wasm_binary: Vec<u8> = /// let wasm_binary: Vec<u8> =
/// wabt::wat2wasm( /// wabt::wat2wasm(
/// r#" /// r#"
/// (module /// (module
/// (func $add (param $lhs i32) (param $rhs i32) (result i32) /// (func $add (param $lhs i32) (param $rhs i32) (result i32)
/// get_local $lhs /// get_local $lhs
/// get_local $rhs /// get_local $rhs
/// i32.add)) /// i32.add))
/// "#, /// "#,
/// ) /// )
/// .expect("failed to parse wat"); /// .expect("failed to parse wat");
/// ///
/// // Load wasm binary and prepare it for instantiation. /// // Load wasm binary and prepare it for instantiation.
/// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed"); /// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
/// assert!(module.deny_floating_point().is_ok()); /// assert!(module.deny_floating_point().is_ok());
/// ///
/// let wasm_binary: Vec<u8> = /// let wasm_binary: Vec<u8> =
/// wabt::wat2wasm( /// wabt::wat2wasm(
/// r#" /// r#"
/// (module /// (module
/// (func $add (param $lhs f32) (param $rhs f32) (result f32) /// (func $add (param $lhs f32) (param $rhs f32) (result f32)
/// get_local $lhs /// get_local $lhs
/// get_local $rhs /// get_local $rhs
/// f32.add)) /// f32.add))
/// "#, /// "#,
/// ) /// )
/// .expect("failed to parse wat"); /// .expect("failed to parse wat");
/// ///
/// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed"); /// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
/// assert!(module.deny_floating_point().is_err()); /// assert!(module.deny_floating_point().is_err());
/// ///
/// let wasm_binary: Vec<u8> = /// let wasm_binary: Vec<u8> =
/// wabt::wat2wasm( /// wabt::wat2wasm(
/// r#" /// r#"
/// (module /// (module
/// (func $add (param $lhs f32) (param $rhs f32) (result f32) /// (func $add (param $lhs f32) (param $rhs f32) (result f32)
/// get_local $lhs)) /// get_local $lhs))
/// "#, /// "#,
/// ) /// )
/// .expect("failed to parse wat"); /// .expect("failed to parse wat");
/// ///
/// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed"); /// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
/// assert!(module.deny_floating_point().is_err()); /// assert!(module.deny_floating_point().is_err());
/// ``` /// ```
pub fn deny_floating_point(&self) -> Result<(), Error> { pub fn deny_floating_point(&self) -> Result<(), Error> {
validation::deny_floating_point(&self.module).map_err(Into::into) validation::deny_floating_point(&self.module).map_err(Into::into)
} }
/// Create `Module` from a given buffer. /// Create `Module` from a given buffer.
/// ///
/// This function will deserialize wasm module from a given module, /// This function will deserialize wasm module from a given module,
/// validate and prepare it for instantiation. /// validate and prepare it for instantiation.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if wasm binary in provided `buffer` is not valid wasm binary. /// Returns `Err` if wasm binary in provided `buffer` is not valid wasm binary.
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// extern crate wasmi; /// extern crate wasmi;
/// ///
/// fn main() { /// fn main() {
/// let module = /// let module =
/// wasmi::Module::from_buffer( /// wasmi::Module::from_buffer(
/// // Minimal module: /// // Minimal module:
/// // \0asm - magic /// // \0asm - magic
/// // 0x01 - version (in little-endian) /// // 0x01 - version (in little-endian)
/// &[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00] /// &[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]
/// ).expect("Failed to load minimal module"); /// ).expect("Failed to load minimal module");
/// ///
/// // Instantiate `module`, etc... /// // Instantiate `module`, etc...
/// } /// }
/// ``` /// ```
pub fn from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<Module, Error> { pub fn from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<Module, Error> {
let module = parity_wasm::elements::deserialize_buffer(buffer.as_ref()) let module = parity_wasm::elements::deserialize_buffer(buffer.as_ref())
.map_err(|e: parity_wasm::elements::Error| Error::Validation(e.to_string()))?; .map_err(|e: parity_wasm::elements::Error| Error::Validation(e.to_string()))?;
Module::from_parity_wasm_module(module) Module::from_parity_wasm_module(module)
} }
pub(crate) fn module(&self) -> &parity_wasm::elements::Module { pub(crate) fn module(&self) -> &parity_wasm::elements::Module {
&self.module &self.module
} }
pub(crate) fn code(&self) -> &Vec<isa::Instructions> { pub(crate) fn code(&self) -> &Vec<isa::Instructions> {
&self.code_map &self.code_map
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,8 @@
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use libm::{F32Ext, F64Ext}; use libm::{F32Ext, F64Ext};
use core::ops::{Add, Div, Mul, Neg, Sub, Rem};
use core::cmp::{Ordering, PartialEq, PartialOrd}; use core::cmp::{Ordering, PartialEq, PartialOrd};
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
macro_rules! impl_binop { macro_rules! impl_binop {
($for:ident, $is:ident, $op:ident, $func_name:ident) => { ($for:ident, $is:ident, $op:ident, $func_name:ident) => {
@ -13,19 +13,22 @@ macro_rules! impl_binop {
fn $func_name(self, other: T) -> Self { fn $func_name(self, other: T) -> Self {
$for( $for(
$op::$func_name( $op::$func_name($is::from_bits(self.0), $is::from_bits(other.into().0))
$is::from_bits(self.0), .to_bits(),
$is::from_bits(other.into().0)
).to_bits()
) )
} }
} }
} };
} }
macro_rules! float { macro_rules! float {
($for:ident, $rep:ident, $is:ident) => { ($for:ident, $rep:ident, $is:ident) => {
float!($for, $rep, $is, 1 << (::core::mem::size_of::<$is>() * 8 - 1)); float!(
$for,
$rep,
$is,
1 << (::core::mem::size_of::<$is>() * 8 - 1)
);
}; };
($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => { ($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -112,7 +115,7 @@ macro_rules! float {
$is::from(*self).fmt(f) $is::from(*self).fmt(f)
} }
} }
} };
} }
float!(F32, u32, f32); float!(F32, u32, f32);
@ -150,9 +153,9 @@ mod tests {
use super::{F32, F64}; use super::{F32, F64};
use core::ops::{Add, Div, Mul, Neg, Sub};
use core::fmt::Debug; use core::fmt::Debug;
use core::iter; use core::iter;
use core::ops::{Add, Div, Mul, Neg, Sub};
fn test_ops<T, F, I>(iter: I) fn test_ops<T, F, I>(iter: I)
where where

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use alloc::rc::Rc; use alloc::rc::Rc;
use core::u32;
use core::fmt;
use core::cell::RefCell; use core::cell::RefCell;
use parity_wasm::elements::ResizableLimits; use core::fmt;
use Error; use core::u32;
use func::FuncRef; use func::FuncRef;
use module::check_limits; use module::check_limits;
use parity_wasm::elements::ResizableLimits;
use Error;
/// Reference to a table (See [`TableInstance`] for details). /// Reference to a table (See [`TableInstance`] for details).
/// ///
@ -19,10 +19,10 @@ use module::check_limits;
pub struct TableRef(Rc<TableInstance>); pub struct TableRef(Rc<TableInstance>);
impl ::core::ops::Deref for TableRef { impl ::core::ops::Deref for TableRef {
type Target = TableInstance; type Target = TableInstance;
fn deref(&self) -> &TableInstance { fn deref(&self) -> &TableInstance {
&self.0 &self.0
} }
} }
/// Runtime representation of a table. /// Runtime representation of a table.
@ -39,118 +39,118 @@ impl ::core::ops::Deref for TableRef {
/// [`grow`]: #method.grow /// [`grow`]: #method.grow
/// ///
pub struct TableInstance { pub struct TableInstance {
/// Table limits. /// Table limits.
limits: ResizableLimits, limits: ResizableLimits,
/// Table memory buffer. /// Table memory buffer.
buffer: RefCell<Vec<Option<FuncRef>>>, buffer: RefCell<Vec<Option<FuncRef>>>,
} }
impl fmt::Debug for TableInstance { impl fmt::Debug for TableInstance {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("TableInstance") f.debug_struct("TableInstance")
.field("limits", &self.limits) .field("limits", &self.limits)
.field("buffer.len", &self.buffer.borrow().len()) .field("buffer.len", &self.buffer.borrow().len())
.finish() .finish()
} }
} }
impl TableInstance { impl TableInstance {
/// Allocate a table instance. /// Allocate a table instance.
/// ///
/// The table allocated with initial size, specified by `initial_size`. /// The table allocated with initial size, specified by `initial_size`.
/// Maximum size can be specified by `maximum_size`. /// Maximum size can be specified by `maximum_size`.
/// ///
/// All table elements are allocated uninitialized. /// All table elements are allocated uninitialized.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if `initial_size` is greater than `maximum_size`. /// Returns `Err` if `initial_size` is greater than `maximum_size`.
pub fn alloc(initial_size: u32, maximum_size: Option<u32>) -> Result<TableRef, Error> { pub fn alloc(initial_size: u32, maximum_size: Option<u32>) -> Result<TableRef, Error> {
let table = TableInstance::new(ResizableLimits::new(initial_size, maximum_size))?; let table = TableInstance::new(ResizableLimits::new(initial_size, maximum_size))?;
Ok(TableRef(Rc::new(table))) Ok(TableRef(Rc::new(table)))
} }
fn new(limits: ResizableLimits) -> Result<TableInstance, Error> { fn new(limits: ResizableLimits) -> Result<TableInstance, Error> {
check_limits(&limits)?; check_limits(&limits)?;
Ok(TableInstance { Ok(TableInstance {
buffer: RefCell::new(vec![None; limits.initial() as usize]), buffer: RefCell::new(vec![None; limits.initial() as usize]),
limits: limits, limits: limits,
}) })
} }
/// Return table limits. /// Return table limits.
pub(crate) fn limits(&self) -> &ResizableLimits { pub(crate) fn limits(&self) -> &ResizableLimits {
&self.limits &self.limits
} }
/// Returns size this table was created with. /// Returns size this table was created with.
pub fn initial_size(&self) -> u32 { pub fn initial_size(&self) -> u32 {
self.limits.initial() self.limits.initial()
} }
/// Returns maximum size `TableInstance` can grow to. /// Returns maximum size `TableInstance` can grow to.
pub fn maximum_size(&self) -> Option<u32> { pub fn maximum_size(&self) -> Option<u32> {
self.limits.maximum() self.limits.maximum()
} }
/// Returns current size of the table. /// Returns current size of the table.
pub fn current_size(&self) -> u32 { pub fn current_size(&self) -> u32 {
self.buffer.borrow().len() as u32 self.buffer.borrow().len() as u32
} }
/// Increases the size of the table by given number of elements. /// Increases the size of the table by given number of elements.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if tried to allocate more elements than permited by limit. /// Returns `Err` if tried to allocate more elements than permited by limit.
pub fn grow(&self, by: u32) -> Result<(), Error> { pub fn grow(&self, by: u32) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut(); let mut buffer = self.buffer.borrow_mut();
let maximum_size = self.maximum_size().unwrap_or(u32::MAX); let maximum_size = self.maximum_size().unwrap_or(u32::MAX);
let new_size = self.current_size().checked_add(by) let new_size = self
.and_then(|new_size| { .current_size()
if maximum_size < new_size { .checked_add(by)
None .and_then(|new_size| {
} else { if maximum_size < new_size {
Some(new_size) None
} } else {
}) Some(new_size)
.ok_or_else(|| }
Error::Table(format!( })
"Trying to grow table by {} items when there are already {} items", .ok_or_else(|| {
by, Error::Table(format!(
self.current_size(), "Trying to grow table by {} items when there are already {} items",
)) by,
)?; self.current_size(),
buffer.resize(new_size as usize, None); ))
Ok(()) })?;
} buffer.resize(new_size as usize, None);
Ok(())
}
/// Get the specific value in the table /// Get the specific value in the table
pub fn get(&self, offset: u32) -> Result<Option<FuncRef>, Error> { pub fn get(&self, offset: u32) -> Result<Option<FuncRef>, Error> {
let buffer = self.buffer.borrow(); let buffer = self.buffer.borrow();
let buffer_len = buffer.len(); let buffer_len = buffer.len();
let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| {
Error::Table(format!( Error::Table(format!(
"trying to read table item with index {} when there are only {} items", "trying to read table item with index {} when there are only {} items",
offset, offset, buffer_len
buffer_len ))
)), })?;
)?; Ok(table_elem)
Ok(table_elem) }
}
/// Set the table element to the specified function. /// Set the table element to the specified function.
pub fn set(&self, offset: u32, value: Option<FuncRef>) -> Result<(), Error> { pub fn set(&self, offset: u32, value: Option<FuncRef>) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut(); let mut buffer = self.buffer.borrow_mut();
let buffer_len = buffer.len(); let buffer_len = buffer.len();
let table_elem = buffer.get_mut(offset as usize).ok_or_else(|| let table_elem = buffer.get_mut(offset as usize).ok_or_else(|| {
Error::Table(format!( Error::Table(format!(
"trying to update table item with index {} when there are only {} items", "trying to update table item with index {} when there are only {} items",
offset, offset, buffer_len
buffer_len ))
)) })?;
)?; *table_elem = value;
*table_elem = value; Ok(())
Ok(()) }
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
use wabt; use wabt;
use {Module}; use Module;
mod host; mod host;
mod wasm; mod wasm;
@ -12,25 +12,31 @@ fn assert_std_err_impl<T: ::std::error::Error>() {}
#[test] #[test]
fn assert_error_properties() { fn assert_error_properties() {
assert_send::<Error>(); assert_send::<Error>();
assert_sync::<Error>(); assert_sync::<Error>();
assert_std_err_impl::<Error>(); assert_std_err_impl::<Error>();
} }
/// Test that converting an u32 (u64) that does not fit in an i32 (i64) /// Test that converting an u32 (u64) that does not fit in an i32 (i64)
/// to a RuntimeValue and back works as expected and the number remains unchanged. /// to a RuntimeValue and back works as expected and the number remains unchanged.
#[test] #[test]
fn unsigned_to_runtime_value() { fn unsigned_to_runtime_value() {
use super::RuntimeValue; use super::RuntimeValue;
let overflow_i32: u32 = ::core::i32::MAX as u32 + 1; let overflow_i32: u32 = ::core::i32::MAX as u32 + 1;
assert_eq!(RuntimeValue::from(overflow_i32).try_into::<u32>().unwrap(), overflow_i32); assert_eq!(
RuntimeValue::from(overflow_i32).try_into::<u32>().unwrap(),
overflow_i32
);
let overflow_i64: u64 = ::core::i64::MAX as u64 + 1; let overflow_i64: u64 = ::core::i64::MAX as u64 + 1;
assert_eq!(RuntimeValue::from(overflow_i64).try_into::<u64>().unwrap(), overflow_i64); assert_eq!(
RuntimeValue::from(overflow_i64).try_into::<u64>().unwrap(),
overflow_i64
);
} }
pub fn parse_wat(source: &str) -> Module { pub fn parse_wat(source: &str) -> Module {
let wasm_binary = wabt::wat2wasm(source).expect("Failed to parse wat source"); let wasm_binary = wabt::wat2wasm(source).expect("Failed to parse wat source");
Module::from_buffer(wasm_binary).expect("Failed to load parsed module") Module::from_buffer(wasm_binary).expect("Failed to load parsed module")
} }

View File

@ -1,110 +1,113 @@
use {
Error, Signature, FuncRef, GlobalInstance, GlobalRef, ImportsBuilder, MemoryInstance,
MemoryRef, ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue,
TableInstance, TableRef, Module, GlobalDescriptor, TableDescriptor, MemoryDescriptor,
};
use memory_units::Pages; use memory_units::Pages;
use std::fs::File; use std::fs::File;
use {
Error, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, MemoryDescriptor,
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, NopExternals,
RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
};
struct Env { struct Env {
table_base: GlobalRef, table_base: GlobalRef,
memory_base: GlobalRef, memory_base: GlobalRef,
memory: MemoryRef, memory: MemoryRef,
table: TableRef, table: TableRef,
} }
impl Env { impl Env {
fn new() -> Env { fn new() -> Env {
Env { Env {
table_base: GlobalInstance::alloc(RuntimeValue::I32(0), false), table_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
memory_base: GlobalInstance::alloc(RuntimeValue::I32(0), false), memory_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
memory: MemoryInstance::alloc(Pages(256), None).unwrap(), memory: MemoryInstance::alloc(Pages(256), None).unwrap(),
table: TableInstance::alloc(64, None).unwrap(), table: TableInstance::alloc(64, None).unwrap(),
} }
} }
} }
impl ModuleImportResolver for Env { impl ModuleImportResolver for Env {
fn resolve_func(&self, _field_name: &str, _func_type: &Signature) -> Result<FuncRef, Error> { fn resolve_func(&self, _field_name: &str, _func_type: &Signature) -> Result<FuncRef, Error> {
Err(Error::Instantiation( Err(Error::Instantiation(
"env module doesn't provide any functions".into(), "env module doesn't provide any functions".into(),
)) ))
} }
fn resolve_global( fn resolve_global(
&self, &self,
field_name: &str, field_name: &str,
_global_type: &GlobalDescriptor, _global_type: &GlobalDescriptor,
) -> Result<GlobalRef, Error> { ) -> Result<GlobalRef, Error> {
match field_name { match field_name {
"tableBase" => Ok(self.table_base.clone()), "tableBase" => Ok(self.table_base.clone()),
"memoryBase" => Ok(self.memory_base.clone()), "memoryBase" => Ok(self.memory_base.clone()),
_ => Err(Error::Instantiation(format!( _ => Err(Error::Instantiation(format!(
"env module doesn't provide global '{}'", "env module doesn't provide global '{}'",
field_name field_name
))), ))),
} }
} }
fn resolve_memory( fn resolve_memory(
&self, &self,
field_name: &str, field_name: &str,
_memory_type: &MemoryDescriptor, _memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, Error> { ) -> Result<MemoryRef, Error> {
match field_name { match field_name {
"memory" => Ok(self.memory.clone()), "memory" => Ok(self.memory.clone()),
_ => Err(Error::Instantiation(format!( _ => Err(Error::Instantiation(format!(
"env module doesn't provide memory '{}'", "env module doesn't provide memory '{}'",
field_name field_name
))), ))),
} }
} }
fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, Error> { fn resolve_table(
match field_name { &self,
"table" => Ok(self.table.clone()), field_name: &str,
_ => Err(Error::Instantiation( _table_type: &TableDescriptor,
format!("env module doesn't provide table '{}'", field_name), ) -> Result<TableRef, Error> {
)), match field_name {
} "table" => Ok(self.table.clone()),
} _ => Err(Error::Instantiation(format!(
"env module doesn't provide table '{}'",
field_name
))),
}
}
} }
fn load_from_file(filename: &str) -> Module { fn load_from_file(filename: &str) -> Module {
use std::io::prelude::*; use std::io::prelude::*;
let mut file = File::open(filename).unwrap(); let mut file = File::open(filename).unwrap();
let mut buf = Vec::new(); let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap(); file.read_to_end(&mut buf).unwrap();
let wasm_buf = ::wabt::wat2wasm(&buf).unwrap(); let wasm_buf = ::wabt::wat2wasm(&buf).unwrap();
Module::from_buffer(wasm_buf).unwrap() Module::from_buffer(wasm_buf).unwrap()
} }
#[test] #[test]
fn interpreter_inc_i32() { fn interpreter_inc_i32() {
// Name of function contained in WASM file (note the leading underline) // Name of function contained in WASM file (note the leading underline)
const FUNCTION_NAME: &'static str = "_inc_i32"; const FUNCTION_NAME: &'static str = "_inc_i32";
// The WASM file containing the module and function // The WASM file containing the module and function
const WASM_FILE: &str = &"res/fixtures/inc_i32.wast"; const WASM_FILE: &str = &"res/fixtures/inc_i32.wast";
let module = load_from_file(WASM_FILE); let module = load_from_file(WASM_FILE);
let env = Env::new(); let env = Env::new();
let instance = ModuleInstance::new( let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
&module, .expect("Failed to instantiate module")
&ImportsBuilder::new().with_resolver("env", &env), .assert_no_start();
).expect("Failed to instantiate module")
.assert_no_start();
let i32_val = 42; let i32_val = 42;
// the functions expects a single i32 parameter // the functions expects a single i32 parameter
let args = &[RuntimeValue::I32(i32_val)]; let args = &[RuntimeValue::I32(i32_val)];
let exp_retval = Some(RuntimeValue::I32(i32_val + 1)); let exp_retval = Some(RuntimeValue::I32(i32_val + 1));
let retval = instance let retval = instance
.invoke_export(FUNCTION_NAME, args, &mut NopExternals) .invoke_export(FUNCTION_NAME, args, &mut NopExternals)
.expect(""); .expect("");
assert_eq!(exp_retval, retval); assert_eq!(exp_retval, retval);
} }
#[test] #[test]
@ -114,18 +117,15 @@ fn interpreter_accumulate_u8() {
// The WASM file containing the module and function // The WASM file containing the module and function
const WASM_FILE: &str = &"res/fixtures/accumulate_u8.wast"; const WASM_FILE: &str = &"res/fixtures/accumulate_u8.wast";
// The octet sequence being accumulated // The octet sequence being accumulated
const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1]; const BUF: &[u8] = &[9, 8, 7, 6, 5, 4, 3, 2, 1];
// Load the module-structure from wasm-file and add to program // Load the module-structure from wasm-file and add to program
let module = load_from_file(WASM_FILE); let module = load_from_file(WASM_FILE);
let env = Env::new(); let env = Env::new();
let instance = ModuleInstance::new( let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
&module, .expect("Failed to instantiate module")
&ImportsBuilder::new().with_resolver("env", &env), .assert_no_start();
).expect("Failed to instantiate module")
.assert_no_start();
let env_memory = env.memory.clone(); let env_memory = env.memory.clone();
@ -134,7 +134,10 @@ fn interpreter_accumulate_u8() {
let _ = env_memory.set(offset, BUF); let _ = env_memory.set(offset, BUF);
// Set up the function argument list and invoke the function // Set up the function argument list and invoke the function
let args = &[RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)]; let args = &[
RuntimeValue::I32(BUF.len() as i32),
RuntimeValue::I32(offset as i32),
];
let retval = instance let retval = instance
.invoke_export(FUNCTION_NAME, args, &mut NopExternals) .invoke_export(FUNCTION_NAME, args, &mut NopExternals)
.expect("Failed to execute function"); .expect("Failed to execute function");

View File

@ -1,7 +1,8 @@
use alloc::borrow::Cow; use alloc::borrow::Cow;
use parity_wasm::elements::{ use parity_wasm::elements::{
FunctionType, ValueType as EValueType, GlobalType, TableType, MemoryType}; FunctionType, GlobalType, MemoryType, TableType, ValueType as EValueType,
};
/// Signature of a [function]. /// Signature of a [function].
/// ///
@ -13,55 +14,60 @@ use parity_wasm::elements::{
/// [function]: struct.FuncInstance.html /// [function]: struct.FuncInstance.html
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Signature { pub struct Signature {
params: Cow<'static, [ValueType]>, params: Cow<'static, [ValueType]>,
return_type: Option<ValueType>, return_type: Option<ValueType>,
} }
impl Signature { impl Signature {
/// Creates new signature with givens /// Creates new signature with givens
/// parameter types and optional return type. /// parameter types and optional return type.
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use wasmi::{Signature, ValueType}; /// use wasmi::{Signature, ValueType};
/// ///
/// // s1: (i32) -> () /// // s1: (i32) -> ()
/// let s1 = Signature::new(&[ValueType::I32][..], None); /// let s1 = Signature::new(&[ValueType::I32][..], None);
/// ///
/// // s2: () -> i32 /// // s2: () -> i32
/// let s2 = Signature::new(&[][..], Some(ValueType::I32)); /// let s2 = Signature::new(&[][..], Some(ValueType::I32));
/// ///
/// // s3: (I64) -> () /// // s3: (I64) -> ()
/// let dynamic_params = vec![ValueType::I64]; /// let dynamic_params = vec![ValueType::I64];
/// let s3 = Signature::new(dynamic_params, None); /// let s3 = Signature::new(dynamic_params, None);
/// ``` /// ```
pub fn new<C: Into<Cow<'static, [ValueType]>>>( pub fn new<C: Into<Cow<'static, [ValueType]>>>(
params: C, params: C,
return_type: Option<ValueType> return_type: Option<ValueType>,
) -> Signature { ) -> Signature {
Signature { Signature {
params: params.into(), params: params.into(),
return_type: return_type, return_type: return_type,
} }
} }
/// Returns parameter types of this signature. /// Returns parameter types of this signature.
pub fn params(&self) -> &[ValueType] { pub fn params(&self) -> &[ValueType] {
&self.params.as_ref() &self.params.as_ref()
} }
/// Returns return type of this signature. /// Returns return type of this signature.
pub fn return_type(&self) -> Option<ValueType> { pub fn return_type(&self) -> Option<ValueType> {
self.return_type self.return_type
} }
pub(crate) fn from_elements(func_type: &FunctionType) -> Signature { pub(crate) fn from_elements(func_type: &FunctionType) -> Signature {
Signature { Signature {
params: func_type.params().iter().cloned().map(ValueType::from_elements).collect(), params: func_type
return_type: func_type.return_type().map(ValueType::from_elements), .params()
} .iter()
} .cloned()
.map(ValueType::from_elements)
.collect(),
return_type: func_type.return_type().map(ValueType::from_elements),
}
}
} }
/// Type of a value. /// Type of a value.
@ -71,34 +77,34 @@ impl Signature {
/// [`RuntimeValue`]: enum.RuntimeValue.html /// [`RuntimeValue`]: enum.RuntimeValue.html
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ValueType { pub enum ValueType {
/// 32-bit signed or unsigned integer. /// 32-bit signed or unsigned integer.
I32, I32,
/// 64-bit signed or unsigned integer. /// 64-bit signed or unsigned integer.
I64, I64,
/// 32-bit IEEE 754-2008 floating point number. /// 32-bit IEEE 754-2008 floating point number.
F32, F32,
/// 64-bit IEEE 754-2008 floating point number. /// 64-bit IEEE 754-2008 floating point number.
F64, F64,
} }
impl ValueType { impl ValueType {
pub(crate) fn from_elements(value_type: EValueType) -> ValueType { pub(crate) fn from_elements(value_type: EValueType) -> ValueType {
match value_type { match value_type {
EValueType::I32 => ValueType::I32, EValueType::I32 => ValueType::I32,
EValueType::I64 => ValueType::I64, EValueType::I64 => ValueType::I64,
EValueType::F32 => ValueType::F32, EValueType::F32 => ValueType::F32,
EValueType::F64 => ValueType::F64, EValueType::F64 => ValueType::F64,
} }
} }
pub(crate) fn into_elements(self) -> EValueType { pub(crate) fn into_elements(self) -> EValueType {
match self { match self {
ValueType::I32 => EValueType::I32, ValueType::I32 => EValueType::I32,
ValueType::I64 => EValueType::I64, ValueType::I64 => EValueType::I64,
ValueType::F32 => EValueType::F32, ValueType::F32 => EValueType::F32,
ValueType::F64 => EValueType::F64, ValueType::F64 => EValueType::F64,
} }
} }
} }
/// Description of a global variable. /// Description of a global variable.
@ -108,29 +114,29 @@ impl ValueType {
/// ///
/// [`ImportResolver`]: trait.ImportResolver.html /// [`ImportResolver`]: trait.ImportResolver.html
pub struct GlobalDescriptor { pub struct GlobalDescriptor {
value_type: ValueType, value_type: ValueType,
mutable: bool, mutable: bool,
} }
impl GlobalDescriptor { impl GlobalDescriptor {
pub(crate) fn from_elements(global_type: &GlobalType) -> GlobalDescriptor { pub(crate) fn from_elements(global_type: &GlobalType) -> GlobalDescriptor {
GlobalDescriptor { GlobalDescriptor {
value_type: ValueType::from_elements(global_type.content_type()), value_type: ValueType::from_elements(global_type.content_type()),
mutable: global_type.is_mutable(), mutable: global_type.is_mutable(),
} }
} }
/// Returns [`ValueType`] of the requested global. /// Returns [`ValueType`] of the requested global.
/// ///
/// [`ValueType`]: enum.ValueType.html /// [`ValueType`]: enum.ValueType.html
pub fn value_type(&self) -> ValueType { pub fn value_type(&self) -> ValueType {
self.value_type self.value_type
} }
/// Returns whether the requested global mutable. /// Returns whether the requested global mutable.
pub fn is_mutable(&self) -> bool { pub fn is_mutable(&self) -> bool {
self.mutable self.mutable
} }
} }
/// Description of a table. /// Description of a table.
@ -140,27 +146,27 @@ impl GlobalDescriptor {
/// ///
/// [`ImportResolver`]: trait.ImportResolver.html /// [`ImportResolver`]: trait.ImportResolver.html
pub struct TableDescriptor { pub struct TableDescriptor {
initial: u32, initial: u32,
maximum: Option<u32>, maximum: Option<u32>,
} }
impl TableDescriptor { impl TableDescriptor {
pub(crate) fn from_elements(table_type: &TableType) -> TableDescriptor { pub(crate) fn from_elements(table_type: &TableType) -> TableDescriptor {
TableDescriptor { TableDescriptor {
initial: table_type.limits().initial(), initial: table_type.limits().initial(),
maximum: table_type.limits().maximum(), maximum: table_type.limits().maximum(),
} }
} }
/// Returns initial size of the requested table. /// Returns initial size of the requested table.
pub fn initial(&self) -> u32 { pub fn initial(&self) -> u32 {
self.initial self.initial
} }
/// Returns maximum size of the requested table. /// Returns maximum size of the requested table.
pub fn maximum(&self) -> Option<u32> { pub fn maximum(&self) -> Option<u32> {
self.maximum self.maximum
} }
} }
/// Description of a linear memory. /// Description of a linear memory.
@ -170,25 +176,25 @@ impl TableDescriptor {
/// ///
/// [`ImportResolver`]: trait.ImportResolver.html /// [`ImportResolver`]: trait.ImportResolver.html
pub struct MemoryDescriptor { pub struct MemoryDescriptor {
initial: u32, initial: u32,
maximum: Option<u32>, maximum: Option<u32>,
} }
impl MemoryDescriptor { impl MemoryDescriptor {
pub(crate) fn from_elements(memory_type: &MemoryType) -> MemoryDescriptor { pub(crate) fn from_elements(memory_type: &MemoryType) -> MemoryDescriptor {
MemoryDescriptor { MemoryDescriptor {
initial: memory_type.limits().initial(), initial: memory_type.limits().initial(),
maximum: memory_type.limits().maximum(), maximum: memory_type.limits().maximum(),
} }
} }
/// Returns initial size (in pages) of the requested memory. /// Returns initial size (in pages) of the requested memory.
pub fn initial(&self) -> u32 { pub fn initial(&self) -> u32 {
self.initial self.initial
} }
/// Returns maximum size (in pages) of the requested memory. /// Returns maximum size (in pages) of the requested memory.
pub fn maximum(&self) -> Option<u32> { pub fn maximum(&self) -> Option<u32> {
self.maximum self.maximum
} }
} }

View File

@ -1,136 +1,142 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use parity_wasm::elements::{MemoryType, TableType, GlobalType, BlockType, ValueType, FunctionType}; use parity_wasm::elements::{
BlockType, FunctionType, GlobalType, MemoryType, TableType, ValueType,
};
use validation::Error; use validation::Error;
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct ModuleContext { pub struct ModuleContext {
pub memories: Vec<MemoryType>, pub memories: Vec<MemoryType>,
pub tables: Vec<TableType>, pub tables: Vec<TableType>,
pub globals: Vec<GlobalType>, pub globals: Vec<GlobalType>,
pub types: Vec<FunctionType>, pub types: Vec<FunctionType>,
pub func_type_indexes: Vec<u32>, pub func_type_indexes: Vec<u32>,
} }
impl ModuleContext { impl ModuleContext {
pub fn memories(&self) -> &[MemoryType] { pub fn memories(&self) -> &[MemoryType] {
&self.memories &self.memories
} }
pub fn tables(&self) -> &[TableType] { pub fn tables(&self) -> &[TableType] {
&self.tables &self.tables
} }
pub fn globals(&self) -> &[GlobalType] { pub fn globals(&self) -> &[GlobalType] {
&self.globals &self.globals
} }
pub fn types(&self) -> &[FunctionType] { pub fn types(&self) -> &[FunctionType] {
&self.types &self.types
} }
pub fn func_type_indexes(&self) -> &[u32] { pub fn func_type_indexes(&self) -> &[u32] {
&self.func_type_indexes &self.func_type_indexes
} }
pub fn require_memory(&self, idx: u32) -> Result<(), Error> { pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
if self.memories().get(idx as usize).is_none() { if self.memories().get(idx as usize).is_none() {
return Err(Error(format!("Memory at index {} doesn't exists", idx))); return Err(Error(format!("Memory at index {} doesn't exists", idx)));
} }
Ok(()) Ok(())
} }
pub fn require_table(&self, idx: u32) -> Result<&TableType, Error> { pub fn require_table(&self, idx: u32) -> Result<&TableType, Error> {
self.tables() self.tables()
.get(idx as usize) .get(idx as usize)
.ok_or_else(|| Error(format!("Table at index {} doesn't exists", idx))) .ok_or_else(|| Error(format!("Table at index {} doesn't exists", idx)))
} }
pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
let ty_idx = self.func_type_indexes() let ty_idx = self
.get(idx as usize) .func_type_indexes()
.ok_or_else(|| Error(format!("Function at index {} doesn't exists", idx)))?; .get(idx as usize)
self.require_function_type(*ty_idx) .ok_or_else(|| Error(format!("Function at index {} doesn't exists", idx)))?;
} self.require_function_type(*ty_idx)
}
pub fn require_function_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { pub fn require_function_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
let ty = self.types() let ty = self
.get(idx as usize) .types()
.ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?; .get(idx as usize)
.ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?;
let params = ty.params(); let params = ty.params();
let return_ty = ty.return_type() let return_ty = ty
.map(BlockType::Value) .return_type()
.unwrap_or(BlockType::NoResult); .map(BlockType::Value)
Ok((params, return_ty)) .unwrap_or(BlockType::NoResult);
} Ok((params, return_ty))
}
pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<&GlobalType, Error> { pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<&GlobalType, Error> {
let global = self.globals() let global = self
.get(idx as usize) .globals()
.ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?; .get(idx as usize)
.ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?;
if let Some(expected_mutable) = mutability { if let Some(expected_mutable) = mutability {
if expected_mutable && !global.is_mutable() { if expected_mutable && !global.is_mutable() {
return Err(Error(format!("Expected global {} to be mutable", idx))); return Err(Error(format!("Expected global {} to be mutable", idx)));
} }
if !expected_mutable && global.is_mutable() { if !expected_mutable && global.is_mutable() {
return Err(Error(format!("Expected global {} to be immutable", idx))); return Err(Error(format!("Expected global {} to be immutable", idx)));
} }
} }
Ok(global) Ok(global)
} }
} }
#[derive(Default)] #[derive(Default)]
pub struct ModuleContextBuilder { pub struct ModuleContextBuilder {
memories: Vec<MemoryType>, memories: Vec<MemoryType>,
tables: Vec<TableType>, tables: Vec<TableType>,
globals: Vec<GlobalType>, globals: Vec<GlobalType>,
types: Vec<FunctionType>, types: Vec<FunctionType>,
func_type_indexes: Vec<u32>, func_type_indexes: Vec<u32>,
} }
impl ModuleContextBuilder { impl ModuleContextBuilder {
pub fn new() -> ModuleContextBuilder { pub fn new() -> ModuleContextBuilder {
ModuleContextBuilder::default() ModuleContextBuilder::default()
} }
pub fn push_memory(&mut self, memory: MemoryType) { pub fn push_memory(&mut self, memory: MemoryType) {
self.memories.push(memory); self.memories.push(memory);
} }
pub fn push_table(&mut self, table: TableType) { pub fn push_table(&mut self, table: TableType) {
self.tables.push(table); self.tables.push(table);
} }
pub fn push_global(&mut self, global: GlobalType) { pub fn push_global(&mut self, global: GlobalType) {
self.globals.push(global); self.globals.push(global);
} }
pub fn set_types(&mut self, types: Vec<FunctionType>) { pub fn set_types(&mut self, types: Vec<FunctionType>) {
self.types = types; self.types = types;
} }
pub fn push_func_type_index(&mut self, func_type_index: u32) { pub fn push_func_type_index(&mut self, func_type_index: u32) {
self.func_type_indexes.push(func_type_index); self.func_type_indexes.push(func_type_index);
} }
pub fn build(self) -> ModuleContext { pub fn build(self) -> ModuleContext {
let ModuleContextBuilder { let ModuleContextBuilder {
memories, memories,
tables, tables,
globals, globals,
types, types,
func_type_indexes, func_type_indexes,
} = self; } = self;
ModuleContext { ModuleContext {
memories, memories,
tables, tables,
globals, globals,
types, types,
func_type_indexes, func_type_indexes,
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,23 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use core::fmt;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::error; use std::error;
use core::fmt;
#[cfg(feature = "std")]
use std::collections::HashSet;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use hashmap_core::HashSet; use hashmap_core::HashSet;
#[cfg(feature = "std")]
use std::collections::HashSet;
use parity_wasm::elements::{
BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, Module, Instruction,
ResizableLimits, TableType, ValueType, InitExpr, Type,
};
use common::stack;
use self::context::ModuleContextBuilder; use self::context::ModuleContextBuilder;
use self::func::FunctionReader; use self::func::FunctionReader;
use memory_units::Pages; use common::stack;
use isa; use isa;
use memory_units::Pages;
use parity_wasm::elements::{
BlockType, External, GlobalEntry, GlobalType, InitExpr, Instruction, Internal, MemoryType,
Module, ResizableLimits, TableType, Type, ValueType,
};
mod context; mod context;
mod func; mod func;
@ -30,432 +30,427 @@ mod tests;
pub struct Error(String); pub struct Error(String);
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0) write!(f, "{}", self.0)
} }
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl error::Error for Error { impl error::Error for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
&self.0 &self.0
} }
} }
impl From<stack::Error> for Error { impl From<stack::Error> for Error {
fn from(e: stack::Error) -> Error { fn from(e: stack::Error) -> Error {
Error(format!("Stack: {}", e)) Error(format!("Stack: {}", e))
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct ValidatedModule { pub struct ValidatedModule {
pub code_map: Vec<isa::Instructions>, pub code_map: Vec<isa::Instructions>,
pub module: Module, pub module: Module,
} }
impl ::core::ops::Deref for ValidatedModule { impl ::core::ops::Deref for ValidatedModule {
type Target = Module; type Target = Module;
fn deref(&self) -> &Module { fn deref(&self) -> &Module {
&self.module &self.module
} }
} }
pub fn deny_floating_point(module: &Module) -> Result<(), Error> { pub fn deny_floating_point(module: &Module) -> Result<(), Error> {
if let Some(code) = module.code_section() { if let Some(code) = module.code_section() {
for op in code.bodies().iter().flat_map(|body| body.code().elements()) { for op in code.bodies().iter().flat_map(|body| body.code().elements()) {
use parity_wasm::elements::Instruction::*; use parity_wasm::elements::Instruction::*;
macro_rules! match_eq { macro_rules! match_eq {
($pattern:pat) => { ($pattern:pat) => {
|val| if let $pattern = *val { true } else { false } |val| if let $pattern = *val { true } else { false }
}; };
} }
const DENIED: &[fn(&Instruction) -> bool] = &[ const DENIED: &[fn(&Instruction) -> bool] = &[
match_eq!(F32Load(_, _)), match_eq!(F32Load(_, _)),
match_eq!(F64Load(_, _)), match_eq!(F64Load(_, _)),
match_eq!(F32Store(_, _)), match_eq!(F32Store(_, _)),
match_eq!(F64Store(_, _)), match_eq!(F64Store(_, _)),
match_eq!(F32Const(_)), match_eq!(F32Const(_)),
match_eq!(F64Const(_)), match_eq!(F64Const(_)),
match_eq!(F32Eq), match_eq!(F32Eq),
match_eq!(F32Ne), match_eq!(F32Ne),
match_eq!(F32Lt), match_eq!(F32Lt),
match_eq!(F32Gt), match_eq!(F32Gt),
match_eq!(F32Le), match_eq!(F32Le),
match_eq!(F32Ge), match_eq!(F32Ge),
match_eq!(F64Eq), match_eq!(F64Eq),
match_eq!(F64Ne), match_eq!(F64Ne),
match_eq!(F64Lt), match_eq!(F64Lt),
match_eq!(F64Gt), match_eq!(F64Gt),
match_eq!(F64Le), match_eq!(F64Le),
match_eq!(F64Ge), match_eq!(F64Ge),
match_eq!(F32Abs), match_eq!(F32Abs),
match_eq!(F32Neg), match_eq!(F32Neg),
match_eq!(F32Ceil), match_eq!(F32Ceil),
match_eq!(F32Floor), match_eq!(F32Floor),
match_eq!(F32Trunc), match_eq!(F32Trunc),
match_eq!(F32Nearest), match_eq!(F32Nearest),
match_eq!(F32Sqrt), match_eq!(F32Sqrt),
match_eq!(F32Add), match_eq!(F32Add),
match_eq!(F32Sub), match_eq!(F32Sub),
match_eq!(F32Mul), match_eq!(F32Mul),
match_eq!(F32Div), match_eq!(F32Div),
match_eq!(F32Min), match_eq!(F32Min),
match_eq!(F32Max), match_eq!(F32Max),
match_eq!(F32Copysign), match_eq!(F32Copysign),
match_eq!(F64Abs), match_eq!(F64Abs),
match_eq!(F64Neg), match_eq!(F64Neg),
match_eq!(F64Ceil), match_eq!(F64Ceil),
match_eq!(F64Floor), match_eq!(F64Floor),
match_eq!(F64Trunc), match_eq!(F64Trunc),
match_eq!(F64Nearest), match_eq!(F64Nearest),
match_eq!(F64Sqrt), match_eq!(F64Sqrt),
match_eq!(F64Add), match_eq!(F64Add),
match_eq!(F64Sub), match_eq!(F64Sub),
match_eq!(F64Mul), match_eq!(F64Mul),
match_eq!(F64Div), match_eq!(F64Div),
match_eq!(F64Min), match_eq!(F64Min),
match_eq!(F64Max), match_eq!(F64Max),
match_eq!(F64Copysign), match_eq!(F64Copysign),
match_eq!(F32ConvertSI32), match_eq!(F32ConvertSI32),
match_eq!(F32ConvertUI32), match_eq!(F32ConvertUI32),
match_eq!(F32ConvertSI64), match_eq!(F32ConvertSI64),
match_eq!(F32ConvertUI64), match_eq!(F32ConvertUI64),
match_eq!(F32DemoteF64), match_eq!(F32DemoteF64),
match_eq!(F64ConvertSI32), match_eq!(F64ConvertSI32),
match_eq!(F64ConvertUI32), match_eq!(F64ConvertUI32),
match_eq!(F64ConvertSI64), match_eq!(F64ConvertSI64),
match_eq!(F64ConvertUI64), match_eq!(F64ConvertUI64),
match_eq!(F64PromoteF32), match_eq!(F64PromoteF32),
match_eq!(F32ReinterpretI32), match_eq!(F32ReinterpretI32),
match_eq!(F64ReinterpretI64), match_eq!(F64ReinterpretI64),
match_eq!(I32TruncSF32), match_eq!(I32TruncSF32),
match_eq!(I32TruncUF32), match_eq!(I32TruncUF32),
match_eq!(I32TruncSF64), match_eq!(I32TruncSF64),
match_eq!(I32TruncUF64), match_eq!(I32TruncUF64),
match_eq!(I64TruncSF32), match_eq!(I64TruncSF32),
match_eq!(I64TruncUF32), match_eq!(I64TruncUF32),
match_eq!(I64TruncSF64), match_eq!(I64TruncSF64),
match_eq!(I64TruncUF64), match_eq!(I64TruncUF64),
match_eq!(I32ReinterpretF32), match_eq!(I32ReinterpretF32),
match_eq!(I64ReinterpretF64), match_eq!(I64ReinterpretF64),
]; ];
if DENIED.iter().any(|is_denied| is_denied(op)) { if DENIED.iter().any(|is_denied| is_denied(op)) {
return Err(Error(format!("Floating point operation denied: {:?}", op))); return Err(Error(format!("Floating point operation denied: {:?}", op)));
} }
} }
} }
if let (Some(sec), Some(types)) = (module.function_section(), module.type_section()) { if let (Some(sec), Some(types)) = (module.function_section(), module.type_section()) {
use parity_wasm::elements::{Type, ValueType}; use parity_wasm::elements::{Type, ValueType};
let types = types.types(); let types = types.types();
for sig in sec.entries() { for sig in sec.entries() {
if let Some(typ) = types.get(sig.type_ref() as usize) { if let Some(typ) = types.get(sig.type_ref() as usize) {
match *typ { match *typ {
Type::Function(ref func) => { Type::Function(ref func) => {
if func.params() if func
.iter() .params()
.chain(func.return_type().as_ref()) .iter()
.any(|&typ| typ == ValueType::F32 || typ == ValueType::F64) .chain(func.return_type().as_ref())
{ .any(|&typ| typ == ValueType::F32 || typ == ValueType::F64)
return Err(Error(format!("Use of floating point types denied"))); {
} return Err(Error(format!("Use of floating point types denied")));
} }
} }
} }
} }
} }
}
Ok(()) Ok(())
} }
pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> { pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
let mut context_builder = ModuleContextBuilder::new(); let mut context_builder = ModuleContextBuilder::new();
let mut imported_globals = Vec::new(); let mut imported_globals = Vec::new();
let mut code_map = Vec::new(); let mut code_map = Vec::new();
// Copy types from module as is. // Copy types from module as is.
context_builder.set_types( context_builder.set_types(
module module
.type_section() .type_section()
.map(|ts| { .map(|ts| {
ts.types() ts.types()
.into_iter() .into_iter()
.map(|&Type::Function(ref ty)| ty) .map(|&Type::Function(ref ty)| ty)
.cloned() .cloned()
.collect() .collect()
}) })
.unwrap_or_default(), .unwrap_or_default(),
); );
// Fill elements with imported values. // Fill elements with imported values.
for import_entry in module for import_entry in module
.import_section() .import_section()
.map(|i| i.entries()) .map(|i| i.entries())
.unwrap_or_default() .unwrap_or_default()
{ {
match *import_entry.external() { match *import_entry.external() {
External::Function(idx) => context_builder.push_func_type_index(idx), External::Function(idx) => context_builder.push_func_type_index(idx),
External::Table(ref table) => context_builder.push_table(table.clone()), External::Table(ref table) => context_builder.push_table(table.clone()),
External::Memory(ref memory) => context_builder.push_memory(memory.clone()), External::Memory(ref memory) => context_builder.push_memory(memory.clone()),
External::Global(ref global) => { External::Global(ref global) => {
context_builder.push_global(global.clone()); context_builder.push_global(global.clone());
imported_globals.push(global.clone()); imported_globals.push(global.clone());
} }
} }
} }
// Concatenate elements with defined in the module. // Concatenate elements with defined in the module.
if let Some(function_section) = module.function_section() { if let Some(function_section) = module.function_section() {
for func_entry in function_section.entries() { for func_entry in function_section.entries() {
context_builder.push_func_type_index(func_entry.type_ref()) context_builder.push_func_type_index(func_entry.type_ref())
} }
} }
if let Some(table_section) = module.table_section() { if let Some(table_section) = module.table_section() {
for table_entry in table_section.entries() { for table_entry in table_section.entries() {
validate_table_type(table_entry)?; validate_table_type(table_entry)?;
context_builder.push_table(table_entry.clone()); context_builder.push_table(table_entry.clone());
} }
} }
if let Some(mem_section) = module.memory_section() { if let Some(mem_section) = module.memory_section() {
for mem_entry in mem_section.entries() { for mem_entry in mem_section.entries() {
validate_memory_type(mem_entry)?; validate_memory_type(mem_entry)?;
context_builder.push_memory(mem_entry.clone()); context_builder.push_memory(mem_entry.clone());
} }
} }
if let Some(global_section) = module.global_section() { if let Some(global_section) = module.global_section() {
for global_entry in global_section.entries() { for global_entry in global_section.entries() {
validate_global_entry(global_entry, &imported_globals)?; validate_global_entry(global_entry, &imported_globals)?;
context_builder.push_global(global_entry.global_type().clone()); context_builder.push_global(global_entry.global_type().clone());
} }
} }
let context = context_builder.build(); let context = context_builder.build();
let function_section_len = module let function_section_len = module
.function_section() .function_section()
.map(|s| s.entries().len()) .map(|s| s.entries().len())
.unwrap_or(0); .unwrap_or(0);
let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0); let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0);
if function_section_len != code_section_len { if function_section_len != code_section_len {
return Err(Error(format!( return Err(Error(format!(
"length of function section is {}, while len of code section is {}", "length of function section is {}, while len of code section is {}",
function_section_len, function_section_len, code_section_len
code_section_len )));
))); }
}
// validate every function body in user modules // validate every function body in user modules
if function_section_len != 0 { if function_section_len != 0 {
// tests use invalid code // tests use invalid code
let function_section = module.function_section().expect( let function_section = module
"function_section_len != 0; qed", .function_section()
); .expect("function_section_len != 0; qed");
let code_section = module.code_section().expect( let code_section = module
"function_section_len != 0; function_section_len == code_section_len; qed", .code_section()
); .expect("function_section_len != 0; function_section_len == code_section_len; qed");
// check every function body // check every function body
for (index, function) in function_section.entries().iter().enumerate() { for (index, function) in function_section.entries().iter().enumerate() {
let function_body = code_section.bodies().get(index as usize).ok_or( let function_body = code_section
Error(format!( .bodies()
"Missing body for function {}", .get(index as usize)
index .ok_or(Error(format!("Missing body for function {}", index)))?;
)), let code =
)?; FunctionReader::read_function(&context, function, function_body).map_err(|e| {
let code = FunctionReader::read_function(&context, function, function_body) let Error(ref msg) = e;
.map_err(|e| { Error(format!(
let Error(ref msg) = e; "Function #{} reading/validation error: {}",
Error(format!("Function #{} reading/validation error: {}", index, msg)) index, msg
})?; ))
code_map.push(code); })?;
} code_map.push(code);
} }
}
// validate start section // validate start section
if let Some(start_fn_idx) = module.start_section() { if let Some(start_fn_idx) = module.start_section() {
let (params, return_ty) = context.require_function(start_fn_idx)?; let (params, return_ty) = context.require_function(start_fn_idx)?;
if return_ty != BlockType::NoResult || params.len() != 0 { if return_ty != BlockType::NoResult || params.len() != 0 {
return Err(Error( return Err(Error(
"start function expected to have type [] -> []".into(), "start function expected to have type [] -> []".into(),
)); ));
} }
} }
// validate export section // validate export section
if let Some(export_section) = module.export_section() { if let Some(export_section) = module.export_section() {
let mut export_names = HashSet::with_capacity(export_section.entries().len()); let mut export_names = HashSet::with_capacity(export_section.entries().len());
for export in export_section.entries() { for export in export_section.entries() {
// HashSet::insert returns false if item already in set. // HashSet::insert returns false if item already in set.
let duplicate = export_names.insert(export.field()) == false; let duplicate = export_names.insert(export.field()) == false;
if duplicate { if duplicate {
return Err(Error( return Err(Error(format!("duplicate export {}", export.field())));
format!("duplicate export {}", export.field()), }
)); match *export.internal() {
} Internal::Function(function_index) => {
match *export.internal() { context.require_function(function_index)?;
Internal::Function(function_index) => { }
context.require_function(function_index)?; Internal::Global(global_index) => {
} context.require_global(global_index, Some(false))?;
Internal::Global(global_index) => { }
context.require_global(global_index, Some(false))?; Internal::Memory(memory_index) => {
} context.require_memory(memory_index)?;
Internal::Memory(memory_index) => { }
context.require_memory(memory_index)?; Internal::Table(table_index) => {
} context.require_table(table_index)?;
Internal::Table(table_index) => { }
context.require_table(table_index)?; }
} }
} }
}
}
// validate import section // validate import section
if let Some(import_section) = module.import_section() { if let Some(import_section) = module.import_section() {
for import in import_section.entries() { for import in import_section.entries() {
match *import.external() { match *import.external() {
External::Function(function_type_index) => { External::Function(function_type_index) => {
context.require_function_type(function_type_index)?; context.require_function_type(function_type_index)?;
} }
External::Global(ref global_type) => { External::Global(ref global_type) => {
if global_type.is_mutable() { if global_type.is_mutable() {
return Err(Error(format!( return Err(Error(format!(
"trying to import mutable global {}", "trying to import mutable global {}",
import.field() import.field()
))); )));
} }
} }
External::Memory(ref memory_type) => { External::Memory(ref memory_type) => {
validate_memory_type(memory_type)?; validate_memory_type(memory_type)?;
} }
External::Table(ref table_type) => { External::Table(ref table_type) => {
validate_table_type(table_type)?; validate_table_type(table_type)?;
} }
} }
} }
} }
// there must be no greater than 1 table in tables index space // there must be no greater than 1 table in tables index space
if context.tables().len() > 1 { if context.tables().len() > 1 {
return Err(Error(format!( return Err(Error(format!(
"too many tables in index space: {}", "too many tables in index space: {}",
context.tables().len() context.tables().len()
))); )));
} }
// there must be no greater than 1 linear memory in memory index space // there must be no greater than 1 linear memory in memory index space
if context.memories().len() > 1 { if context.memories().len() > 1 {
return Err(Error(format!( return Err(Error(format!(
"too many memory regions in index space: {}", "too many memory regions in index space: {}",
context.memories().len() context.memories().len()
))); )));
} }
// use data section to initialize linear memory regions // use data section to initialize linear memory regions
if let Some(data_section) = module.data_section() { if let Some(data_section) = module.data_section() {
for data_segment in data_section.entries() { for data_segment in data_section.entries() {
context.require_memory(data_segment.index())?; context.require_memory(data_segment.index())?;
let init_ty = expr_const_type(data_segment.offset(), context.globals())?; let init_ty = expr_const_type(data_segment.offset(), context.globals())?;
if init_ty != ValueType::I32 { if init_ty != ValueType::I32 {
return Err(Error("segment offset should return I32".into())); return Err(Error("segment offset should return I32".into()));
} }
} }
} }
// use element section to fill tables // use element section to fill tables
if let Some(element_section) = module.elements_section() { if let Some(element_section) = module.elements_section() {
for element_segment in element_section.entries() { for element_segment in element_section.entries() {
context.require_table(element_segment.index())?; context.require_table(element_segment.index())?;
let init_ty = expr_const_type(element_segment.offset(), context.globals())?; let init_ty = expr_const_type(element_segment.offset(), context.globals())?;
if init_ty != ValueType::I32 { if init_ty != ValueType::I32 {
return Err(Error("segment offset should return I32".into())); return Err(Error("segment offset should return I32".into()));
} }
for function_index in element_segment.members() { for function_index in element_segment.members() {
context.require_function(*function_index)?; context.require_function(*function_index)?;
} }
} }
} }
Ok(ValidatedModule { Ok(ValidatedModule { module, code_map })
module,
code_map,
})
} }
fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> { fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> {
if let Some(maximum) = limits.maximum() { if let Some(maximum) = limits.maximum() {
if limits.initial() > maximum { if limits.initial() > maximum {
return Err(Error(format!( return Err(Error(format!(
"maximum limit {} is less than minimum {}", "maximum limit {} is less than minimum {}",
maximum, maximum,
limits.initial() limits.initial()
))); )));
} }
} }
Ok(()) Ok(())
} }
fn validate_memory_type(memory_type: &MemoryType) -> Result<(), Error> { fn validate_memory_type(memory_type: &MemoryType) -> Result<(), Error> {
let initial: Pages = Pages(memory_type.limits().initial() as usize); let initial: Pages = Pages(memory_type.limits().initial() as usize);
let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize)); let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
::memory::validate_memory(initial, maximum).map_err(Error) ::memory::validate_memory(initial, maximum).map_err(Error)
} }
fn validate_table_type(table_type: &TableType) -> Result<(), Error> { fn validate_table_type(table_type: &TableType) -> Result<(), Error> {
validate_limits(table_type.limits()) validate_limits(table_type.limits())
} }
fn validate_global_entry(global_entry: &GlobalEntry, globals: &[GlobalType]) -> Result<(), Error> { fn validate_global_entry(global_entry: &GlobalEntry, globals: &[GlobalType]) -> Result<(), Error> {
let init = global_entry.init_expr(); let init = global_entry.init_expr();
let init_expr_ty = expr_const_type(init, globals)?; let init_expr_ty = expr_const_type(init, globals)?;
if init_expr_ty != global_entry.global_type().content_type() { if init_expr_ty != global_entry.global_type().content_type() {
return Err(Error(format!( return Err(Error(format!(
"Trying to initialize variable of type {:?} with value of type {:?}", "Trying to initialize variable of type {:?} with value of type {:?}",
global_entry.global_type().content_type(), global_entry.global_type().content_type(),
init_expr_ty init_expr_ty
))); )));
} }
Ok(()) Ok(())
} }
/// Returns type of this constant expression. /// Returns type of this constant expression.
fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<ValueType, Error> { fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<ValueType, Error> {
let code = init_expr.code(); let code = init_expr.code();
if code.len() != 2 { if code.len() != 2 {
return Err(Error( return Err(Error(
"Init expression should always be with length 2".into(), "Init expression should always be with length 2".into(),
)); ));
} }
let expr_ty: ValueType = match code[0] { let expr_ty: ValueType = match code[0] {
Instruction::I32Const(_) => ValueType::I32, Instruction::I32Const(_) => ValueType::I32,
Instruction::I64Const(_) => ValueType::I64, Instruction::I64Const(_) => ValueType::I64,
Instruction::F32Const(_) => ValueType::F32, Instruction::F32Const(_) => ValueType::F32,
Instruction::F64Const(_) => ValueType::F64, Instruction::F64Const(_) => ValueType::F64,
Instruction::GetGlobal(idx) => { Instruction::GetGlobal(idx) => match globals.get(idx as usize) {
match globals.get(idx as usize) { Some(target_global) => {
Some(target_global) => { if target_global.is_mutable() {
if target_global.is_mutable() { return Err(Error(format!("Global {} is mutable", idx)));
return Err(Error(format!("Global {} is mutable", idx))); }
} target_global.content_type()
target_global.content_type() }
} None => {
None => { return Err(Error(format!(
return Err(Error( "Global {} doesn't exists or not yet defined",
format!("Global {} doesn't exists or not yet defined", idx), idx
)) )));
} }
} },
} _ => return Err(Error("Non constant opcode in init expr".into())),
_ => return Err(Error("Non constant opcode in init expr".into())), };
}; if code[1] != Instruction::End {
if code[1] != Instruction::End { return Err(Error("Expression doesn't ends with `end` opcode".into()));
return Err(Error("Expression doesn't ends with `end` opcode".into())); }
} Ok(expr_ty)
Ok(expr_ty)
} }

File diff suppressed because it is too large Load Diff

View File

@ -10,124 +10,119 @@ use validation::Error;
/// of a value_type and a count. /// of a value_type and a count.
#[derive(Debug)] #[derive(Debug)]
pub struct Locals<'a> { pub struct Locals<'a> {
params: &'a [ValueType], params: &'a [ValueType],
local_groups: &'a [Local], local_groups: &'a [Local],
count: u32, count: u32,
} }
impl<'a> Locals<'a> { impl<'a> Locals<'a> {
/// Create a new wrapper around declared variables and parameters. /// Create a new wrapper around declared variables and parameters.
pub fn new(params: &'a [ValueType], local_groups: &'a [Local]) -> Result<Locals<'a>, Error> { pub fn new(params: &'a [ValueType], local_groups: &'a [Local]) -> Result<Locals<'a>, Error> {
let mut acc = params.len() as u32; let mut acc = params.len() as u32;
for locals_group in local_groups { for locals_group in local_groups {
acc = acc acc = acc
.checked_add(locals_group.count()) .checked_add(locals_group.count())
.ok_or_else(|| .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
Error(String::from("Locals range not in 32-bit range")) }
)?;
}
Ok(Locals { Ok(Locals {
params, params,
local_groups, local_groups,
count: acc, count: acc,
}) })
} }
/// Returns parameter count. /// Returns parameter count.
pub fn param_count(&self) -> u32 { pub fn param_count(&self) -> u32 {
self.params.len() as u32 self.params.len() as u32
} }
/// Returns total count of all declared locals and paramaterers. /// Returns total count of all declared locals and paramaterers.
pub fn count(&self) -> u32 { pub fn count(&self) -> u32 {
self.count self.count
} }
/// Returns the type of a local variable (either a declared local or a param). /// Returns the type of a local variable (either a declared local or a param).
/// ///
/// Returns `Err` in the case of overflow or when idx falls out of range. /// Returns `Err` in the case of overflow or when idx falls out of range.
pub fn type_of_local(&self, idx: u32) -> Result<ValueType, Error> { pub fn type_of_local(&self, idx: u32) -> Result<ValueType, Error> {
if let Some(param) = self.params.get(idx as usize) { if let Some(param) = self.params.get(idx as usize) {
return Ok(*param); return Ok(*param);
} }
// If an index doesn't point to a param, then we have to look into local declarations. // If an index doesn't point to a param, then we have to look into local declarations.
let mut start_idx = self.param_count(); let mut start_idx = self.param_count();
for locals_group in self.local_groups { for locals_group in self.local_groups {
let end_idx = start_idx let end_idx = start_idx
.checked_add(locals_group.count()) .checked_add(locals_group.count())
.ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?; .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
if idx >= start_idx && idx < end_idx { if idx >= start_idx && idx < end_idx {
return Ok(locals_group.value_type()); return Ok(locals_group.value_type());
} }
start_idx = end_idx; start_idx = end_idx;
} }
// We didn't find anything, that's an error. // We didn't find anything, that's an error.
// At this moment `start_idx` should hold the count of all locals // At this moment `start_idx` should hold the count of all locals
// (since it's either set to the `end_idx` or equal to `params.len()`) // (since it's either set to the `end_idx` or equal to `params.len()`)
let total_count = start_idx; let total_count = start_idx;
Err(Error(format!( Err(Error(format!(
"Trying to access local with index {} when there are only {} locals", "Trying to access local with index {} when there are only {} locals",
idx, total_count idx, total_count
))) )))
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn locals_it_works() { fn locals_it_works() {
let params = vec![ValueType::I32, ValueType::I64]; let params = vec![ValueType::I32, ValueType::I64];
let local_groups = vec![Local::new(2, ValueType::F32), Local::new(2, ValueType::F64)]; let local_groups = vec![Local::new(2, ValueType::F32), Local::new(2, ValueType::F64)];
let locals = Locals::new(&params, &local_groups).unwrap(); let locals = Locals::new(&params, &local_groups).unwrap();
assert_matches!(locals.type_of_local(0), Ok(ValueType::I32)); assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
assert_matches!(locals.type_of_local(1), Ok(ValueType::I64)); assert_matches!(locals.type_of_local(1), Ok(ValueType::I64));
assert_matches!(locals.type_of_local(2), Ok(ValueType::F32)); assert_matches!(locals.type_of_local(2), Ok(ValueType::F32));
assert_matches!(locals.type_of_local(3), Ok(ValueType::F32)); assert_matches!(locals.type_of_local(3), Ok(ValueType::F32));
assert_matches!(locals.type_of_local(4), Ok(ValueType::F64)); assert_matches!(locals.type_of_local(4), Ok(ValueType::F64));
assert_matches!(locals.type_of_local(5), Ok(ValueType::F64)); assert_matches!(locals.type_of_local(5), Ok(ValueType::F64));
assert_matches!(locals.type_of_local(6), Err(_)); assert_matches!(locals.type_of_local(6), Err(_));
} }
#[test] #[test]
fn locals_no_declared_locals() { fn locals_no_declared_locals() {
let params = vec![ValueType::I32]; let params = vec![ValueType::I32];
let locals = Locals::new(&params, &[]).unwrap(); let locals = Locals::new(&params, &[]).unwrap();
assert_matches!(locals.type_of_local(0), Ok(ValueType::I32)); assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
assert_matches!(locals.type_of_local(1), Err(_)); assert_matches!(locals.type_of_local(1), Err(_));
} }
#[test] #[test]
fn locals_no_params() { fn locals_no_params() {
let local_groups = vec![Local::new(2, ValueType::I32), Local::new(3, ValueType::I64)]; let local_groups = vec![Local::new(2, ValueType::I32), Local::new(3, ValueType::I64)];
let locals = Locals::new(&[], &local_groups).unwrap(); let locals = Locals::new(&[], &local_groups).unwrap();
assert_matches!(locals.type_of_local(0), Ok(ValueType::I32)); assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
assert_matches!(locals.type_of_local(1), Ok(ValueType::I32)); assert_matches!(locals.type_of_local(1), Ok(ValueType::I32));
assert_matches!(locals.type_of_local(2), Ok(ValueType::I64)); assert_matches!(locals.type_of_local(2), Ok(ValueType::I64));
assert_matches!(locals.type_of_local(3), Ok(ValueType::I64)); assert_matches!(locals.type_of_local(3), Ok(ValueType::I64));
assert_matches!(locals.type_of_local(4), Ok(ValueType::I64)); assert_matches!(locals.type_of_local(4), Ok(ValueType::I64));
assert_matches!(locals.type_of_local(5), Err(_)); assert_matches!(locals.type_of_local(5), Err(_));
} }
#[test] #[test]
fn locals_u32_overflow() { fn locals_u32_overflow() {
let local_groups = vec![ let local_groups = vec![
Local::new(u32::max_value(), ValueType::I32), Local::new(u32::max_value(), ValueType::I32),
Local::new(1, ValueType::I64), Local::new(1, ValueType::I64),
]; ];
assert_matches!( assert_matches!(Locals::new(&[], &local_groups), Err(_));
Locals::new(&[], &local_groups), }
Err(_)
);
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
mod run; mod run;
macro_rules! run_test { macro_rules! run_test {
($label: expr, $test_name: ident) => ( ($label: expr, $test_name: ident) => {
#[test] #[test]
fn $test_name() { fn $test_name() {
self::run::spec($label) self::run::spec($label)
} }
); };
} }
run_test!("address", wasm_address); run_test!("address", wasm_address);

View File

@ -1,22 +1,24 @@
#![cfg(test)] #![cfg(test)]
use std::fs::File;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File;
use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value}; use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value};
use wasmi::memory_units::Pages; use wasmi::memory_units::Pages;
use wasmi::{Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, use wasmi::{
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance,
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap}; ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature,
TableDescriptor, TableInstance, TableRef, Trap,
};
fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue { fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
match val { match val {
Value::I32(v) => RuntimeValue::I32(v), Value::I32(v) => RuntimeValue::I32(v),
Value::I64(v) => RuntimeValue::I64(v), Value::I64(v) => RuntimeValue::I64(v),
Value::F32(v) => RuntimeValue::F32(v.into()), Value::F32(v) => RuntimeValue::F32(v.into()),
Value::F64(v) => RuntimeValue::F64(v.into()), Value::F64(v) => RuntimeValue::F64(v.into()),
} }
} }
#[derive(Debug)] #[derive(Debug)]
@ -48,15 +50,15 @@ struct SpecModule {
} }
impl SpecModule { impl SpecModule {
fn new() -> Self { fn new() -> Self {
SpecModule { SpecModule {
table: TableInstance::alloc(10, Some(20)).unwrap(), table: TableInstance::alloc(10, Some(20)).unwrap(),
memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(), memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(),
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false), global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0.into()), false), global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0.into()), false),
global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0.into()), false), global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0.into()), false),
} }
} }
} }
const PRINT_FUNC_INDEX: usize = 0; const PRINT_FUNC_INDEX: usize = 0;
@ -83,29 +85,29 @@ impl ModuleImportResolver for SpecModule {
field_name: &str, field_name: &str,
func_type: &Signature, func_type: &Signature,
) -> Result<FuncRef, InterpreterError> { ) -> Result<FuncRef, InterpreterError> {
let index = match field_name { let index = match field_name {
"print" => PRINT_FUNC_INDEX, "print" => PRINT_FUNC_INDEX,
"print_i32" => PRINT_FUNC_INDEX, "print_i32" => PRINT_FUNC_INDEX,
"print_i32_f32" => PRINT_FUNC_INDEX, "print_i32_f32" => PRINT_FUNC_INDEX,
"print_f64_f64" => PRINT_FUNC_INDEX, "print_f64_f64" => PRINT_FUNC_INDEX,
"print_f32" => PRINT_FUNC_INDEX, "print_f32" => PRINT_FUNC_INDEX,
"print_f64" => PRINT_FUNC_INDEX, "print_f64" => PRINT_FUNC_INDEX,
_ => { _ => {
return Err(InterpreterError::Instantiation(format!( return Err(InterpreterError::Instantiation(format!(
"Unknown host func import {}", "Unknown host func import {}",
field_name field_name
))); )));
} }
}; };
if func_type.return_type().is_some() { if func_type.return_type().is_some() {
return Err(InterpreterError::Instantiation( return Err(InterpreterError::Instantiation(
"Function `print_` have unit return type".into(), "Function `print_` have unit return type".into(),
)); ));
} }
let func = FuncInstance::alloc_host(func_type.clone(), index); let func = FuncInstance::alloc_host(func_type.clone(), index);
return Ok(func); return Ok(func);
} }
fn resolve_global( fn resolve_global(
@ -113,380 +115,389 @@ impl ModuleImportResolver for SpecModule {
field_name: &str, field_name: &str,
_global_type: &GlobalDescriptor, _global_type: &GlobalDescriptor,
) -> Result<GlobalRef, InterpreterError> { ) -> Result<GlobalRef, InterpreterError> {
match field_name { match field_name {
"global_i32" => Ok(self.global_i32.clone()), "global_i32" => Ok(self.global_i32.clone()),
"global_f32" => Ok(self.global_f32.clone()), "global_f32" => Ok(self.global_f32.clone()),
"global_f64" => Ok(self.global_f64.clone()), "global_f64" => Ok(self.global_f64.clone()),
_ => Err(InterpreterError::Instantiation(format!( _ => Err(InterpreterError::Instantiation(format!(
"Unknown host global import {}", "Unknown host global import {}",
field_name field_name
))), ))),
} }
} }
fn resolve_memory( fn resolve_memory(
&self, &self,
field_name: &str, field_name: &str,
_memory_type: &MemoryDescriptor, _memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, InterpreterError> { ) -> Result<MemoryRef, InterpreterError> {
if field_name == "memory" { if field_name == "memory" {
return Ok(self.memory.clone()); return Ok(self.memory.clone());
} }
Err(InterpreterError::Instantiation(format!( Err(InterpreterError::Instantiation(format!(
"Unknown host memory import {}", "Unknown host memory import {}",
field_name field_name
))) )))
} }
fn resolve_table( fn resolve_table(
&self, &self,
field_name: &str, field_name: &str,
_table_type: &TableDescriptor, _table_type: &TableDescriptor,
) -> Result<TableRef, InterpreterError> { ) -> Result<TableRef, InterpreterError> {
if field_name == "table" { if field_name == "table" {
return Ok(self.table.clone()); return Ok(self.table.clone());
} }
Err(InterpreterError::Instantiation(format!( Err(InterpreterError::Instantiation(format!(
"Unknown host table import {}", "Unknown host table import {}",
field_name field_name
))) )))
} }
} }
struct SpecDriver { struct SpecDriver {
spec_module: SpecModule, spec_module: SpecModule,
instances: HashMap<String, ModuleRef>, instances: HashMap<String, ModuleRef>,
last_module: Option<ModuleRef>, last_module: Option<ModuleRef>,
} }
impl SpecDriver { impl SpecDriver {
fn new() -> SpecDriver { fn new() -> SpecDriver {
SpecDriver { SpecDriver {
spec_module: SpecModule::new(), spec_module: SpecModule::new(),
instances: HashMap::new(), instances: HashMap::new(),
last_module: None, last_module: None,
} }
} }
fn spec_module(&mut self) -> &mut SpecModule { fn spec_module(&mut self) -> &mut SpecModule {
&mut self.spec_module &mut self.spec_module
} }
fn add_module(&mut self, name: Option<String>, module: ModuleRef) { fn add_module(&mut self, name: Option<String>, module: ModuleRef) {
self.last_module = Some(module.clone()); self.last_module = Some(module.clone());
if let Some(name) = name { if let Some(name) = name {
self.instances.insert(name, module); self.instances.insert(name, module);
} }
} }
fn module(&self, name: &str) -> Result<ModuleRef, InterpreterError> { fn module(&self, name: &str) -> Result<ModuleRef, InterpreterError> {
self.instances.get(name).cloned().ok_or_else(|| { self.instances.get(name).cloned().ok_or_else(|| {
InterpreterError::Instantiation(format!("Module not registered {}", name)) InterpreterError::Instantiation(format!("Module not registered {}", name))
}) })
} }
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> { fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
match name { match name {
Some(name) => self.module(name), Some(name) => self.module(name),
None => self.last_module None => self
.clone() .last_module
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())), .clone()
} .ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
} }
}
} }
impl ImportResolver for SpecDriver { impl ImportResolver for SpecDriver {
fn resolve_func( fn resolve_func(
&self, &self,
module_name: &str, module_name: &str,
field_name: &str, field_name: &str,
func_type: &Signature, func_type: &Signature,
) -> Result<FuncRef, InterpreterError> { ) -> Result<FuncRef, InterpreterError> {
if module_name == "spectest" { if module_name == "spectest" {
self.spec_module.resolve_func(field_name, func_type) self.spec_module.resolve_func(field_name, func_type)
} else { } else {
self.module(module_name)? self.module(module_name)?
.resolve_func(field_name, func_type) .resolve_func(field_name, func_type)
} }
} }
fn resolve_global( fn resolve_global(
&self, &self,
module_name: &str, module_name: &str,
field_name: &str, field_name: &str,
global_type: &GlobalDescriptor, global_type: &GlobalDescriptor,
) -> Result<GlobalRef, InterpreterError> { ) -> Result<GlobalRef, InterpreterError> {
if module_name == "spectest" { if module_name == "spectest" {
self.spec_module.resolve_global(field_name, global_type) self.spec_module.resolve_global(field_name, global_type)
} else { } else {
self.module(module_name)? self.module(module_name)?
.resolve_global(field_name, global_type) .resolve_global(field_name, global_type)
} }
} }
fn resolve_memory( fn resolve_memory(
&self, &self,
module_name: &str, module_name: &str,
field_name: &str, field_name: &str,
memory_type: &MemoryDescriptor, memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, InterpreterError> { ) -> Result<MemoryRef, InterpreterError> {
if module_name == "spectest" { if module_name == "spectest" {
self.spec_module.resolve_memory(field_name, memory_type) self.spec_module.resolve_memory(field_name, memory_type)
} else { } else {
self.module(module_name)? self.module(module_name)?
.resolve_memory(field_name, memory_type) .resolve_memory(field_name, memory_type)
} }
} }
fn resolve_table( fn resolve_table(
&self, &self,
module_name: &str, module_name: &str,
field_name: &str, field_name: &str,
table_type: &TableDescriptor, table_type: &TableDescriptor,
) -> Result<TableRef, InterpreterError> { ) -> Result<TableRef, InterpreterError> {
if module_name == "spectest" { if module_name == "spectest" {
self.spec_module.resolve_table(field_name, table_type) self.spec_module.resolve_table(field_name, table_type)
} else { } else {
self.module(module_name)? self.module(module_name)?
.resolve_table(field_name, table_type) .resolve_table(field_name, table_type)
} }
} }
} }
fn try_load_module(wasm: &[u8]) -> Result<Module, Error> { fn try_load_module(wasm: &[u8]) -> Result<Module, Error> {
Module::from_buffer(wasm).map_err(|e| Error::Load(e.to_string())) Module::from_buffer(wasm).map_err(|e| Error::Load(e.to_string()))
} }
fn try_load(wasm: &[u8], spec_driver: &mut SpecDriver) -> Result<(), Error> { fn try_load(wasm: &[u8], spec_driver: &mut SpecDriver) -> Result<(), Error> {
let module = try_load_module(wasm)?; let module = try_load_module(wasm)?;
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?; let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?;
instance instance
.run_start(spec_driver.spec_module()) .run_start(spec_driver.spec_module())
.map_err(|trap| Error::Start(trap))?; .map_err(|trap| Error::Start(trap))?;
Ok(()) Ok(())
} }
fn load_module( fn load_module(
wasm: &[u8], wasm: &[u8],
name: &Option<String>, name: &Option<String>,
spec_driver: &mut SpecDriver, spec_driver: &mut SpecDriver,
) -> Result<ModuleRef, Error> { ) -> Result<ModuleRef, Error> {
let module = try_load_module(wasm)?; let module = try_load_module(wasm)?;
let instance = ModuleInstance::new(&module, spec_driver) let instance = ModuleInstance::new(&module, spec_driver)
.map_err(|e| Error::Load(e.to_string()))? .map_err(|e| Error::Load(e.to_string()))?
.run_start(spec_driver.spec_module()) .run_start(spec_driver.spec_module())
.map_err(|trap| Error::Start(trap))?; .map_err(|trap| Error::Start(trap))?;
let module_name = name.clone(); let module_name = name.clone();
spec_driver.add_module(module_name, instance.clone()); spec_driver.add_module(module_name, instance.clone());
Ok(instance) Ok(instance)
} }
fn run_action( fn run_action(
program: &mut SpecDriver, program: &mut SpecDriver,
action: &Action<u32, u64>, action: &Action<u32, u64>,
) -> Result<Option<RuntimeValue>, InterpreterError> { ) -> Result<Option<RuntimeValue>, InterpreterError> {
match *action { match *action {
Action::Invoke { Action::Invoke {
ref module, ref module,
ref field, ref field,
ref args, ref args,
} => { } => {
let module = program let module = program
.module_or_last(module.as_ref().map(|x| x.as_ref())) .module_or_last(module.as_ref().map(|x| x.as_ref()))
.expect(&format!( .expect(&format!(
"Expected program to have loaded module {:?}", "Expected program to have loaded module {:?}",
module module
)); ));
let vec_args = args.iter() let vec_args = args
.cloned() .iter()
.map(spec_to_runtime_value) .cloned()
.collect::<Vec<_>>(); .map(spec_to_runtime_value)
module.invoke_export(field, &vec_args, program.spec_module()) .collect::<Vec<_>>();
} module.invoke_export(field, &vec_args, program.spec_module())
Action::Get { }
ref module, Action::Get {
ref field, ref module,
.. ref field,
} => { ..
let module = program } => {
.module_or_last(module.as_ref().map(|x| x.as_ref())) let module = program
.expect(&format!( .module_or_last(module.as_ref().map(|x| x.as_ref()))
"Expected program to have loaded module {:?}", .expect(&format!(
module "Expected program to have loaded module {:?}",
)); module
let global = module ));
.export_by_name(&field) let global = module
.ok_or_else(|| { .export_by_name(&field)
InterpreterError::Global(format!("Expected to have export with name {}", field)) .ok_or_else(|| {
})? InterpreterError::Global(format!("Expected to have export with name {}", field))
.as_global() })?
.cloned() .as_global()
.ok_or_else(|| { .cloned()
InterpreterError::Global(format!("Expected export {} to be a global", field)) .ok_or_else(|| {
})?; InterpreterError::Global(format!("Expected export {} to be a global", field))
Ok(Some(global.get())) })?;
} Ok(Some(global.get()))
} }
}
} }
pub fn spec(name: &str) { pub fn spec(name: &str) {
println!("running test: {}", name); println!("running test: {}", name);
try_spec(name).expect("Failed to run spec"); try_spec(name).expect("Failed to run spec");
} }
fn try_spec(name: &str) -> Result<(), Error> { fn try_spec(name: &str) -> Result<(), Error> {
let mut spec_driver = SpecDriver::new(); let mut spec_driver = SpecDriver::new();
let spec_script_path = format!("tests/spec/testsuite/{}.wast", name); let spec_script_path = format!("tests/spec/testsuite/{}.wast", name);
use std::io::Read; use std::io::Read;
let mut spec_source = Vec::new(); let mut spec_source = Vec::new();
let mut spec_file = File::open(&spec_script_path).expect("Can't open file"); let mut spec_file = File::open(&spec_script_path).expect("Can't open file");
spec_file.read_to_end(&mut spec_source).expect("Can't read file"); spec_file
.read_to_end(&mut spec_source)
.expect("Can't read file");
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name)).expect("Can't read spec script"); let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name))
let mut errors = vec![]; .expect("Can't read spec script");
let mut errors = vec![];
while let Some(Command { kind, line }) = parser.next()? { while let Some(Command { kind, line }) = parser.next()? {
macro_rules! assert_eq { macro_rules! assert_eq {
($a:expr, $b:expr) => {{ ($a:expr, $b:expr) => {{
let (a, b) = ($a, $b); let (a, b) = ($a, $b);
if a != b { if a != b {
errors.push(format!( errors.push(format!(
r#"ERROR (line {}): r#"ERROR (line {}):
expected: {:?} expected: {:?}
got: {:?} got: {:?}
"#, "#,
line, b, a, line, b, a,
)); ));
} }
}}; }};
} }
println!("Running spec cmd {}: {:?}", line, kind); println!("Running spec cmd {}: {:?}", line, kind);
match kind { match kind {
CommandKind::Module { name, module, .. } => { CommandKind::Module { name, module, .. } => {
load_module(&module.into_vec(), &name, &mut spec_driver) load_module(&module.into_vec(), &name, &mut spec_driver)
.expect("Failed to load module"); .expect("Failed to load module");
} }
CommandKind::AssertReturn { action, expected } => { CommandKind::AssertReturn { action, expected } => {
let result = run_action(&mut spec_driver, &action); let result = run_action(&mut spec_driver, &action);
match result { match result {
Ok(result) => { Ok(result) => {
let spec_expected = expected let spec_expected = expected
.iter() .iter()
.cloned() .cloned()
.map(spec_to_runtime_value) .map(spec_to_runtime_value)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let actual_result = result.into_iter().collect::<Vec<RuntimeValue>>(); let actual_result = result.into_iter().collect::<Vec<RuntimeValue>>();
for (actual_result, spec_expected) in for (actual_result, spec_expected) in
actual_result.iter().zip(spec_expected.iter()) actual_result.iter().zip(spec_expected.iter())
{ {
assert_eq!(actual_result.value_type(), spec_expected.value_type()); assert_eq!(actual_result.value_type(), spec_expected.value_type());
// f32::NAN != f32::NAN // f32::NAN != f32::NAN
match spec_expected { match spec_expected {
&RuntimeValue::F32(val) if val.is_nan() => match actual_result { &RuntimeValue::F32(val) if val.is_nan() => match actual_result {
&RuntimeValue::F32(val) => assert!(val.is_nan()), &RuntimeValue::F32(val) => assert!(val.is_nan()),
_ => unreachable!(), // checked above that types are same _ => unreachable!(), // checked above that types are same
}, },
&RuntimeValue::F64(val) if val.is_nan() => match actual_result { &RuntimeValue::F64(val) if val.is_nan() => match actual_result {
&RuntimeValue::F64(val) => assert!(val.is_nan()), &RuntimeValue::F64(val) => assert!(val.is_nan()),
_ => unreachable!(), // checked above that types are same _ => unreachable!(), // checked above that types are same
}, },
spec_expected @ _ => assert_eq!(actual_result, spec_expected), spec_expected @ _ => assert_eq!(actual_result, spec_expected),
} }
} }
} }
Err(e) => { Err(e) => {
panic!("Expected action to return value, got error: {:?}", e); panic!("Expected action to return value, got error: {:?}", e);
} }
} }
} }
CommandKind::AssertReturnCanonicalNan { action } CommandKind::AssertReturnCanonicalNan { action }
| CommandKind::AssertReturnArithmeticNan { action } => { | CommandKind::AssertReturnArithmeticNan { action } => {
let result = run_action(&mut spec_driver, &action); let result = run_action(&mut spec_driver, &action);
match result { match result {
Ok(result) => { Ok(result) => {
for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() { for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() {
match actual_result { match actual_result {
RuntimeValue::F32(val) => if !val.is_nan() { RuntimeValue::F32(val) => {
panic!("Expected nan value, got {:?}", val) if !val.is_nan() {
}, panic!("Expected nan value, got {:?}", val)
RuntimeValue::F64(val) => if !val.is_nan() { }
panic!("Expected nan value, got {:?}", val) }
}, RuntimeValue::F64(val) => {
val @ _ => { if !val.is_nan() {
panic!("Expected action to return float value, got {:?}", val) panic!("Expected nan value, got {:?}", val)
} }
} }
} val @ _ => {
} panic!("Expected action to return float value, got {:?}", val)
Err(e) => { }
panic!("Expected action to return value, got error: {:?}", e); }
} }
} }
} Err(e) => {
CommandKind::AssertExhaustion { action, .. } => { panic!("Expected action to return value, got error: {:?}", e);
let result = run_action(&mut spec_driver, &action); }
match result { }
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result), }
Err(_e) => {}, CommandKind::AssertExhaustion { action, .. } => {
} let result = run_action(&mut spec_driver, &action);
} match result {
CommandKind::AssertTrap { action, .. } => { Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
let result = run_action(&mut spec_driver, &action); Err(_e) => {}
match result { }
Ok(result) => { }
panic!( CommandKind::AssertTrap { action, .. } => {
"Expected action to result in a trap, got result: {:?}", let result = run_action(&mut spec_driver, &action);
result match result {
); Ok(result) => {
} panic!(
Err(_e) => {} "Expected action to result in a trap, got result: {:?}",
} result
} );
CommandKind::AssertInvalid { module, .. } }
| CommandKind::AssertMalformed { module, .. } Err(_e) => {}
| CommandKind::AssertUnlinkable { module, .. } => { }
let module_load = try_load(&module.into_vec(), &mut spec_driver); }
match module_load { CommandKind::AssertInvalid { module, .. }
Ok(_) => panic!("Expected invalid module definition, got some module!"), | CommandKind::AssertMalformed { module, .. }
Err(_e) => {}, | CommandKind::AssertUnlinkable { module, .. } => {
} let module_load = try_load(&module.into_vec(), &mut spec_driver);
} match module_load {
CommandKind::AssertUninstantiable { module, .. } => { Ok(_) => panic!("Expected invalid module definition, got some module!"),
match try_load(&module.into_vec(), &mut spec_driver) { Err(_e) => {}
Ok(_) => panic!("Expected error running start function at line {}", line), }
Err(_e) => {}, }
} CommandKind::AssertUninstantiable { module, .. } => {
} match try_load(&module.into_vec(), &mut spec_driver) {
CommandKind::Register { name, as_name, .. } => { Ok(_) => panic!("Expected error running start function at line {}", line),
let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) { Err(_e) => {}
Ok(module) => module, }
Err(e) => panic!("No such module, at line {} - ({:?})", e, line), }
}; CommandKind::Register { name, as_name, .. } => {
spec_driver.add_module(Some(as_name.clone()), module); let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) {
} Ok(module) => module,
CommandKind::PerformAction(action) => match run_action(&mut spec_driver, &action) { Err(e) => panic!("No such module, at line {} - ({:?})", e, line),
Ok(_) => {} };
Err(e) => panic!("Failed to invoke action at line {}: {:?}", line, e), spec_driver.add_module(Some(as_name.clone()), module);
}, }
} CommandKind::PerformAction(action) => match run_action(&mut spec_driver, &action) {
} Ok(_) => {}
Err(e) => panic!("Failed to invoke action at line {}: {:?}", line, e),
},
}
}
if !errors.is_empty() { if !errors.is_empty() {
use std::fmt::Write; use std::fmt::Write;
let mut out = "\n".to_owned(); let mut out = "\n".to_owned();
for err in errors { for err in errors {
write!(out, "{}", err).expect("Error formatting errors"); write!(out, "{}", err).expect("Error formatting errors");
} }
panic!(out); panic!(out);
} }
Ok(()) Ok(())
} }

View File

@ -1,6 +1,6 @@
//! Official spec testsuite. //! Official spec testsuite.
extern crate wasmi;
extern crate wabt; extern crate wabt;
extern crate wasmi;
mod spec; mod spec;